1. 程式人生 > >Redis原始碼筆記-初步

Redis原始碼筆記-初步

目錄

目錄 1

1. 前言 2

2. 名詞 2

3. dict.c 2

3.1. siphash演算法 2

3.2. 核心函式 3

3.3. 核心巨集 3

3.4. 核心結構體 3

3.4.1. dictEntry 3

4. Redis命令 4

4.1. SELECT命令 4

4.1.1. redisCommand結構體 4

4.1.2. redisCommandTable變數 5

4.1.3. selectCommand函式 5

4.2. SET命令 6

4.2.1. setCommand函式 6

4.2.2. dictAdd函式 9

4.2.3. dictFind函式 9

4.3. HSETNX命令 10

4.3.1. hsetnxCommand函式 10

4.3.2. hashTypeSet函式 11

5. Redis多路複用機制 11

5.1. AE是什麼? 11

5.2. 多路複用選擇aeApiPoll 11

5.3. 多路複用呼叫aeCreateFileEvent 12

6. 網路收發過程 14

6.1. 過程簡要 14

6.2. 接受連線請求acceptTcpHandler 14

6.3. 建立client物件createClient 16

6.4. 讀取命令readQueryFromClient 17

6.5. 命令處理processCommand 17

7. 持久化和複製 22

7.1. propagate函式(aof&replication) 22

7.2. 寫AOF檔案feedAppendOnlyFile 23

7.3. 資料複製replicationFeedSlaves 25

8. 程序啟動時做了些什麼? 26

9. 核心物件 29

9.1. redisServer 29

9.2. client 30

9.3. redisCommand 31

9.4. redisDb 31

9.5. clusterNode 32

9.6. clusterState 32

10. 主備同步 33

11. 主備切換 33

 

1. 前言

Redis程式碼優美,註釋也很到位,閱讀起來會賞心悅目,大大降低了理解門檻。由於redis單執行緒幾乎完成所有工作,整體邏輯是相當複雜的,涉及了太多狀態,作者的技術深厚可見一斑。

Redis的單執行緒設計給出了一種優雅實現高效能服務思路,在實踐中值得借鑑。需要注意Redis並不是嚴格的單執行緒,實際上它是多程序+多執行緒。為解決IO和慢速操作帶的效能毛刺和卡頓,Redis實現引入的多程序和多執行緒。但是它的主體仍然是單執行緒的,單個執行緒完成網路IO操作和其它操作。

疑問:Redis選擇slot數為16384,是一個偶數,沒有采用質數,這個可能並不是一個最好的主意。

2. 名詞

RESP

REdis Serialization Protocol

Redis序列化協議

AE

A simple Event drived programming library

一個簡單的事務驅動程式設計庫

ASAP

 

 

AOF

Append Only File

僅僅追加寫檔案

BIO

Backgroup IO

後臺IO操作,三種涉及BIO:FSYNC、關閉檔案和記憶體free,均為阻塞或慢操作

3. dict.c

雜湊表的實現,雜湊函式使用了siphash雜湊演算法。

3.1. siphash演算法

一種非加密的64位雜湊演算法。

3.2. 核心函式

Redis中非常核心的演算法——雜湊表的實現在這個檔案中,很多命令都有用到,比如set/hset等,它是除記憶體分配管理外的最基礎實現。核心函式包含但不限於:dictFind、dictNext、dictAdd、dictDelete、dictFetchValue、dictGetHash、dictReplace、dictAddRaw、dictCreate、dictDelete、dictRelease、dictUnlink、dictRehash、dictEmpty、dictResize等。

3.3. 核心巨集

CLUSTER_SLOTS

定義Redis叢集Slots個數,值為16384

DICT_HT_INITIAL_SIZE

定義雜湊表預設大小巨集,值為4

dictSetVal

設定Value

dictSetKey

設定Key

dictCompareKeys

比較兩個Key

dictHashKey

對Key求雜湊

dictGetKey

取Key

dictGetVal

取Value

dictSlots

得到槽(slot)個數

dictSize

得到已用槽(slot)個數

dictGetSignedIntegerVal

取8位元組有符號整數值

dictGetUnsignedIntegerVal

取8位元組無符號整數值

dictGetDoubleVal

取double型別值

3.4. 核心結構體

3.4.1. dictEntry

定義了雜湊節點的資料結構:

typedef struct dictEntry {

    void *key// Key

    union { // 支援4種類型的值

        void *val; // 字串值,或二進位制值

        uint64_t u64; // 8位元組無符號整數值

        int64_t s64; // 8位元組有符號整數值

        double d; // 雙精度浮點值

    } v;

    struct dictEntry *next; // 下一節點,鏈地址衝突解決法

} dictEntry;

4. Redis命令

各具體的命令並不做持久化(寫AOF等)和傳播給Slaves,這兩項是公共操作,在上層統一執行,具體函式為server.c中的“void call(client *c, int flags)”,具體執行函式為server.c中的“void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc, int flags)”。

4.1. SELECT命令

群裡有人問select跟普通命令的效率,猜想select沒有效率問題,閱讀原始碼也證實了這一點。因為它只是改變指標指向,所以不存在效率問題,自然比普通命令效率要高。

Redis支援的所有命令,都儲存在型別為redisCommand函式表中,函式表名為redisCommandTable的陣列中。

4.1.1. redisCommand結構體

如果以C++來理解redisCommand,可以將redisCommand看抽象基類,它定義了兩個虛擬函式proc和getkeys_proc,各種command是它的具體實現。

// server.h

struct redisCommand {

    char *name// 命令名,比如:GET

    redisCommandProc *proc; // 命令處理過程(函式指標)

    // 命令引數的個數,用於檢查命令請求的格式是否正確

    // 如果這個值為負數-N,那麼表示引數的數量大於等於N

    // 注意:命令的名字本身也是一個引數。

    int arity;

    // 字串形式的標識值, 這個值記錄了命令的屬性

    // 比如是讀命令還是寫命令,是否允許在載入資料時使用,是否允許在 Lua 指令碼中使用等

    char *sflags; /* Flags as string representation, one char per flag. */

    // sflags對應的二進位制值,方便程式“與”、“或”等操作操作

    int flags;    /* The actual flags, obtained from the 'sflags' field. */

 

    /* Use a function to determine keys arguments in a command line.

     * Used for Redis Cluster redirect. */

    redisGetKeysProc *getkeys_proc// 函式指標

    /* What keys should be loaded in background when calling this command? */

    int firstkey; /* The first argument that's a key (0 = no keys) */

    int lastkey;  /* The last argument that's a key */

    int keystep;  /* The step between first and last key */

 

    // 伺服器執行這個命令所耗費的總時長

    long long microseconds;;

    // 伺服器總共執行了多少次這個命令

    long long calls;

};

4.1.2. redisCommandTable變數

redisCommandTable的部分定義:

// server.c

struct redisCommand redisCommandTable[] = {

    {"module",moduleCommand,-2,"as",0,NULL,0,0,0,0,0},

    {"get",getCommand,2,"rF",0,NULL,1,1,1,0,0},

    {"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0},

    。。。。。。

    {"select",selectCommand,2,"lF",0,NULL,0,0,0,0,0},

    。。。。。。

    {"post",securityWarningCommand,-1,"lt",0,NULL,0,0,0,0,0},

    {"host:",securityWarningCommand,-1,"lt",0,NULL,0,0,0,0,0},

    {"latency",latencyCommand,-2,"aslt",0,NULL,0,0,0,0,0}

};

4.1.3. selectCommand函式

// server.c

void selectCommand(client *c) {

    long id;

 

    // 第一個引數值為DB的ID

    if (getLongFromObjectOrReply(c, c->argv[1], &id,

        "invalid DB index") != C_OK)

        return;

 

    // 叢集不支援SELECT命令

    if (server.cluster_enabled && id != 0) {

        addReplyError(c,"SELECT is not allowed in cluster mode");

        return;

    }

    if (selectDb(c,id) == C_ERR) { // 切換DB

        addReplyError(c,"DB index is out of range");

    } else {

        addReply(c,shared.ok);

    }

}

 

// db.c

int selectDb(client *c, int id) {

    if (id < 0 || id >= server.dbnum// dbnum值由redis.conf中的databases決定,預設為16

        return C_ERR;

    c->db = &server.db[id];

    return C_OK;

}

4.2. SET命令

SET命令主要是字典操作(dict)。

4.2.1. setCommand函式

// t_string.c

/* SET key value [NX] [XX] [EX <seconds>] [PX <milliseconds>] */

void setCommand(client *c) {

    int j;

    robj *expire = NULL;

    int unit = UNIT_SECONDS;

    int flags = OBJ_SET_NO_FLAGS;

 

    // 以下一長段均為引數選項解析,

    // 第一3個開始,因為第一個為命令SET自身,第二個為KEY,第三個為VALUE

    for (j = 3; j < c->argc; j++) {

        char *a = c->argv[j]->ptr;

        robj *next = (j == c->argc-1) ? NULL : c->argv[j+1];

 

        if ((a[0] == 'n' || a[0] == 'N') &&

            (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&

            !(flags & OBJ_SET_XX))

        {

            flags |= OBJ_SET_NX;

        } else if ((a[0] == 'x' || a[0] == 'X') &&

                   (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&

                   !(flags & OBJ_SET_NX))

        {

            flags |= OBJ_SET_XX;

        } else if ((a[0] == 'e' || a[0] == 'E') &&

                   (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&

                   !(flags & OBJ_SET_PX) && next)

        {

            flags |= OBJ_SET_EX;

            unit = UNIT_SECONDS;

            expire = next;

            j++;

        } else if ((a[0] == 'p' || a[0] == 'P') &&

                   (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&

                   !(flags & OBJ_SET_EX) && next)

        {

            flags |= OBJ_SET_PX;

            unit = UNIT_MILLISECONDS;

            expire = next;

            j++;

        } else {

            addReply(c,shared.syntaxerr); // 語法錯誤

            return;

        }

    }

 

    c->argv[2] = tryObjectEncoding(c->argv[2]);

    setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL);

}

 

// t_string.c

void setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) {

    。。。。。。

    setKey(c->db,key,val);

    server.dirty++;

    if (expire) setExpire(c,c->db,key,mstime()+milliseconds);

    notifyKeyspaceEvent(NOTIFY_STRING,"set",key,c->db->id);

    if (expire) notifyKeyspaceEvent(NOTIFY_GENERIC,"expire",key,c->db->id);

    addReply(c, ok_reply ? ok_reply : shared.ok);

}

 

// db.c

void setKey(redisDb *db, robj *key, robj *val) {

    if (lookupKeyWrite(db,key) == NULL) { // 效能開銷主要在這,參見dictFind函式

        dbAdd(db,key,val);

    } else {

        dbOverwrite(db,key,val);

    }

    incrRefCount(val);

    removeExpire(db,key);

    signalModifiedKey(db,key);

}

 

// db.c

/* Add the key to the DB. It's up to the caller to increment the reference

 * counter of the value if needed.

 *

 * The program is aborted if the key already exists. */

void dbAdd(redisDb *db, robj *key, robj *val) {

    sds copy = sdsdup(key->ptr);

    int retval = dictAdd(db->dict, copy, val);

 

    serverAssertWithInfo(NULL,key,retval == DICT_OK);

    if (val->type == OBJ_LIST ||

        val->type == OBJ_ZSET)

        signalKeyAsReady(db, key);

    if (server.cluster_enabled) slotToKeyAdd(key);

}

 

// dict.h

#define dictSetVal(d, entry, _val_) do { \

    if ((d)->type->valDup) \

        (entry)->v.val = (d)->type->valDup((d)->privdata, _val_); \

    else \

        (entry)->v.val = (_val_); \

} while(0)

 

// db.c

// KEY存在時覆蓋寫

/* Overwrite an existing key with a new value. Incrementing the reference

 * count of the new value is up to the caller.

 * This function does not modify the expire time of the existing key.

 *

 * The program is aborted if the key was not already present. */

void dbOverwrite(redisDb *db, robj *key, robj *val) {

    dictEntry *de = dictFind(db->dict,key->ptr);

 

    serverAssertWithInfo(NULL,key,de != NULL);

    dictEntry auxentry = *de;

    robj *old = dictGetVal(de);

 

    // 記憶體策略由redis.conf中的maxmemory-policy決定

    // LFU策略是4.0開始引入的(Least Frequently Used,最不經常使用

    // #define MAXMEMORY_FLAG_LRU (1<<0)

    // #define MAXMEMORY_FLAG_LFU (1<<1)

    // #define MAXMEMORY_FLAG_ALLKEYS (1<<2)

    // #define MAXMEMORY_FLAG_NO_SHARED_INTEGERS (MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU)

    // #define MAXMEMORY_VOLATILE_LRU ((0<<8)|MAXMEMORY_FLAG_LRU)

    // #define MAXMEMORY_VOLATILE_LFU ((1<<8)|MAXMEMORY_FLAG_LFU)

    // #define MAXMEMORY_VOLATILE_TTL (2<<8)

    // #define MAXMEMORY_VOLATILE_RANDOM (3<<8)

    // #define MAXMEMORY_ALLKEYS_LRU ((4<<8)|MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_ALLKEYS)

    // #define MAXMEMORY_ALLKEYS_LFU ((5<<8)|MAXMEMORY_FLAG_LFU|MAXMEMORY_FLAG_ALLKEYS)

    // #define MAXMEMORY_ALLKEYS_RANDOM ((6<<8)|MAXMEMORY_FLAG_ALLKEYS)

    // #define MAXMEMORY_NO_EVICTION (7<<8)

    if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {

        val->lru = old->lru;

    }

    dictSetVal(db->dict, de, val);

 

    if (server.lazyfree_lazy_server_del) {

        freeObjAsync(old);

        dictSetVal(db->dict, &auxentry, NULL);

    }

 

    dictFreeVal(db->dict, &auxentry);

}

4.2.2. dictAdd函式

// dict.c

// KEY不存在時新增

/* Add an element to the target hash table */

int dictAdd(dict *d, void *key, void *val)

{

    dictEntry *entry = dictAddRaw(d,key,NULL);

 

    if (!entry) return DICT_ERR;

    dictSetVal(d, entry, val);

    return DICT_OK;

}

4.2.3. dictFind函式

字典查詢實現:

dictEntry *dictFind(dict *d, const void *key)

{

    dictEntry *he;

    uint64_t h, idx, table;

 

    if (d->ht[0].used + d->ht[1].used == 0) return NULL; /* dict is empty */

    if (dictIsRehashing(d)) _dictRehashStep(d);

 

    // #define dictHashKey(d, key) (d)->type->hashFunction(key)

    h = dictHashKey(d, key); // 對key求雜湊值

    for (table = 0; table <= 1; table++) {

        idx = h & d->ht[table].sizemask;

        he = d->ht[table].table[idx];

        while(he) { // 鏈式衝突解決法

            if (key==he->key || dictCompareKeys(d, key, he->key))

                return he;

            he = he->next;

        }

        if (!dictIsRehashing(d)) return NULL; // 重雜湊

    }

    return NULL;

}

 

typedef struct dictType {

    uint64_t (*hashFunction)(const void *key);

    void *(*keyDup)(void *privdata, const void *key);

    void *(*valDup)(void *privdata, const void *obj);

    int (*keyCompare)(void *privdata, const void *key1, const void *key2);

    void (*keyDestructor)(void *privdata, void *key);

    void (*valDestructor)(void *privdata, void *obj);

} dictType;

4.3. HSETNX命令

4.3.1. hsetnxCommand函式

// t_hash.c

void hsetnxCommand(client *c) {

    robj *o;

    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;

    hashTypeTryConversion(o,c->argv,2,3);

 

    if (hashTypeExists(o, c->argv[2]->ptr)) {

        addReply(c, shared.czero);

    } else {

        hashTypeSet(o,c->argv[2]->ptr,c->argv[3]->ptr,HASH_SET_COPY);

        addReply(c, shared.cone);

        signalModifiedKey(c->db,c->argv[1]);

        notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);

        server.dirty++;

    }

}

4.3.2. hashTypeSet函式

// t_hash.c

int hashTypeSet(robj *o, sds field, sds value, int flags) {

    if (o->encoding == OBJ_ENCODING_ZIPLIST) {

        // ziplistInsert

        // ziplistPush

    } else if (o->encoding == OBJ_ENCODING_HT) {

        。。。。。。

        dictAdd(o->ptr,f,v);

    } else {

        serverPanic("Unknown hash encoding");

    }

}

5. Redis多路複用機制

5.1. AE是什麼?

“AE”為“A simple event-driven programming library”的縮寫,翻譯成中文,即一個簡單的事件驅動程式設計庫。就Linux而言,可簡單理解為對epoll的封裝。

5.2. 多路複用選擇aeApiPoll

Redis支援select、epoll、kqueue和evport四種模式,編譯Redis時決定採用哪一種,因此執行時只會有一種有效。優先順序是:evport -> epoll -> kqueue -> select,其中select為兜底用,因為所有系統都會支援select。

對應的原始碼檔案,分別為:

evport

ae_evport.c

Solaris

epoll

ae_epoll.c

Linux系統

kqueue

ae_kqueue.c

BSD類系統

select

ae_select.c

其它不支援epoll、evport和kqueue系統

 

橋接它們的檔案則是ae.c,橋接程式碼:

/* Include the best multiplexing layer supported by this system.

 * The following should be ordered by performances, descending. */

#ifdef HAVE_EVPORT

#include "ae_evport.c"

#else

    #ifdef HAVE_EPOLL

    #include "ae_epoll.c"

    #else

        #ifdef HAVE_KQUEUE

        #include "ae_kqueue.c"

        #else

        #include "ae_select.c"

        #endif

    #endif

#endif

 

巨集HAVE_EVPORT、HAVE_EPOLL、HAVE_KQUEUE則在config.h中決議出:

#ifdef __sun

#include <sys/feature_tests.h>

#ifdef _DTRACE_VERSION

#define HAVE_EVPORT 1

#endif

#endif

 

#ifdef __linux__

#define HAVE_EPOLL 1

#endif

 

#if (defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__)

#define HAVE_KQUEUE 1

#endif

5.3. 多路複用呼叫aeCreateFileEvent

自main函式開始的呼叫過程(讀事件和寫事件的順序,作者在這裡用了個小技巧,支援優先響應是查詢,即立即返回查詢結果):

int main(int argc, char **argv) { // server.c

    。。。。。。

    // aeMain(server.el);

    void aeMain(aeEventLoop *eventLoop) // ae.c

    {

        while (!eventLoop->stop) {

            if (eventLoop->beforesleep != NULL)

                eventLoop->beforesleep(eventLoop); // 這很重要,寫AOF檔案在這裡進行

            // aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);

            int aeProcessEvents(aeEventLoop *eventLoop, int flags) { // ae.c

                // linux實際呼叫的是ae_epoll.c中的aeApiPoll函式

                numevents = aeApiPoll(eventLoop, tvp);

                if (eventLoop->aftersleep != NULL && flags & AE_CALL_AFTER_SLEEP)

                    eventLoop->aftersleep(eventLoop);

                    

                for (j = 0; j < numevents; j++) {

                    // 一般優先處理讀事件,然後再寫事件,但有時為立即響應是查詢則寫優先

                    /* Normally we execute the readable event first, 

                       and the writable event laster. */

                    int invert = fe->mask & AE_BARRIER;

                    /* Fire the readable event if the call sequence is not inverted. */

                    fe->rfileProc(eventLoop,fd,fe->clientData,mask);

                    /* Fire the writable event. */

                    fe->wfileProc(eventLoop,fd,fe->clientData,mask);

                    /* If we have to invert the call, 

                    fire the readable event now after the writable one. */

                    fe->rfileProc(eventLoop,fd,fe->clientData,mask);

                    // 函式aeCreateFileEvent中設定rfileProcwfileProc

                    // rfileProcwfileProc的初始化均在aeCreateFileEvent中完成

                }

            }

        }

    }

    。。。。。。

}

 

// 初始化rfileProc和wfileProc

int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,

                      aeFileProc *proc, void *clientData)

{

    if (fd >= eventLoop->setsize) {

        errno = ERANGE;

        return AE_ERR;

    }

    aeFileEvent *fe = &eventLoop->events[fd]; // events是一個以fd為下標的陣列,0查詢

 

    if (aeApiAddEvent(eventLoop, fd, mask) == -1) // fd放到epoll或select等中

        return AE_ERR;

    fe->mask |= mask; // 讀寫事件

    if (mask & AE_READABLE) fe->rfileProc = proc;

    if (mask & AE_WRITABLE) fe->wfileProc = proc;

    fe->clientData = clientData; // 附加資料

    if (fd > eventLoop->maxfd)

        eventLoop->maxfd = fd; // 這個主要給select時用,epoll等不需要

    return AE_OK;

}

6. 網路收發過程

6.1. 過程簡要

main

-> initServer

-> aeCreateFileEvent(acceptTcpHandler)

-> createClient

-> aeCreateFileEvent(readQueryFromClient)

-> read

相關推薦

Redis原始碼筆記-初步

目錄 目錄 1 1. 前言 2 2. 名詞 2 3. dict.c 2 3.1. siphash演算法 2 3.2. 核心函式 3 3.3. 核心巨集 3 3.4. 核心結構體 3 3.4.1.

Redis原始碼閱讀筆記--資料庫redisDb

一. 資料庫 Redis的資料庫使用字典作為底層實現,資料庫的增、刪、查、改都是構建在字典的操作之上的。 redis伺服器將所有資料庫都儲存在伺服器狀態結構redisServer(redis.h/redisServer)的db陣列(應該是一個連結串列)裡:

Redis原始碼閱讀筆記—sds

Redis系統當中,針對字串進行的更加完善的封裝,建立了一個動態字串,並構建了大量的實用api。相關的實現程式碼為sds.h及sds.c,以下為我的原始碼閱讀筆記。內容較多,逐步更新typedefchar *sds;  struct __attribute__ ((__pac

redis原始碼閱讀筆記-dict.h

dict.h 在redis中,dict.h主要是hash的底層實現方式。 在dict.h中主要是一些資料結構的定義,以及一些巨集函式的定義相關的內容。 雜湊節點定義 原始碼中的dictEntry就是雜湊節點的相關定義 typedef struct dictEnt

Redis學習筆記~Twenproxy所起到的作用

out arm mdb ntp ddd pin alq odi mib 回到目錄 Twenproxy除了可以作為redis的代理,它同樣支持memerycached。我這裏主要了解Twemproxy在redis集群上的解決方案。Twemproxy除了完美的解決了分片,路由

Redis學習筆記3-Redis5個可運行程序命令的使用

運行程序 檢查 mil 數據文件 img usr pre text mod 在redis安裝文章中,說到安裝好redis後,在/usr/local/bin下有5個關於redis的可運行程序。以下關於這5個可運行程序命令的具體說明。 redis-server Redi

Redis學習筆記(一)關於在windows64位環境下的安裝學習使用

客戶端 mas key-value 錯誤 services 再次 基準 alt 類型 前言 由於工作需要,目前我正在學習使用Redis。我當時學習Redis就從網上下載了點資料就開始學習了。入門看的是《REDIS入門指南》,這本書個人覺得很適合新手用來學習接觸。根據書上的引

Redis學習筆記1--入門篇

ase list ica cati ctu apple string replace first 一、Redis簡介: Redis(http://redis.io)是一款開源的、高性能的鍵-值存儲(key-value store),它是用ANSI C來編寫。Redis的項目

Redis學習筆記

原子 模型 edi jpg session web2.0 ttl soft 不存在 1.分布式與集群簡單理解:分布式:不同的多臺服務器上面部署不同的服務模塊,他們之間通過RPC/RMI之間通信和調用,對外提供服務和組內協作。集 群:不同的多臺服務器上面部署相同的服務模塊,

Redis學習筆記-----Redis數據過期策略詳解

登錄 及其 可能 ger 方式 處理方式 base 持久化數據 簡單的   本文對Redis的過期機制簡單的講解一下  講解之前我們先拋出一個問題,我們知道很多時候服務器經常會用到redis作為緩存,有很多數據都是臨時緩存一下,可能用過之後很久都不會再用到了(比如暫存ses

Redis學習筆記04Redis命令之(3)服務器操作

毫秒 上下 set 文件描述符 mil 輸出鏈表 事件 客戶 moni 1.1.1. client list 列出所有客戶端連接信息。 每個連接使用一個id=xxx的行表示。 redis.coe2coe.me:6379> client list id=8 ad

Redis 學習筆記

支持 模式 包括 sun 有序 網頁 hyper 原子 類型 Redis特點:   1、速度快   2、支持豐富的數據類型:字符串、哈希列表、集合   3、操作具有原子性,所有Redis操作都是原子操作   4、多實用工具,可應用如緩存,消息隊列,應用程序中任何短期數據,如

Redis學習筆記09Redis數據類型之(2) 哈希表類型

原來 1.2 sts lis holding 名稱 pty against 鍵值 1.1.1. hset 向hash中添加鍵值對。 語法: HSET key field value 參數: key :鍵名稱,鍵值為一個hash表對象。 field:hash表中的鍵名。

Redis學習筆記06Redis命令之(5)事務

mman 客戶 cau 連接 discard 順序 strong 存在 執行命令 1.1.1. multi 開始一個新事務。 redis.coe2coe.me:6379> multi OK 執行此命令後,後面執行的set等命令將被緩存,直到被discard

Redis學習筆記(三)常用命令整理

mes ember nbsp end 插入 學習筆記 頻道 hash value Redis 常用命令 1.DEL key 刪除key2.EXISTS key 檢查key是否存在3.KEYS * 查看所有的key4.EXPIRE key seconds 設置key的過期時

redis實戰筆記(3)-第3章 Redis命令

chan 4.4 ges 打包 常用 重要 讀取 表操作 nio 第3章 Redis命令 本章主要內容 字符串命令、 列表命令和集合命令 散列命令和有序集合命令 發布命令與訂閱命令 其他命令 在每個不同的數據類型的章節裏, 展示的都是該數據類型所獨有的、 最具代表性

redis實戰筆記(4)-第4章 數據安全與性能保障

4.6 特殊 pac 命名 可用 lsp sentinel 樂觀鎖 個人開發 本章主要內容 4.1 將數據持久化至硬盤 4.2 將數據復制至其他機器 4.3 處理系統故障 4.4 Redis事務 4.5 非事務型流水線( non-transactional pipeline

redis 學習筆記

緩解 實時 代理 水平擴展 命令連接 事件 都沒有 分數 能力 一、redis 復制 數據庫復制指的是發生在不同數據庫實例之間,單向的信息傳播的行為,通常由被復制方和復制方組成,被復制方和復制方之間建立網絡連接,復制方式通常為被復制方主動將數據發送到復制方,復制方接收到數據

redis學習筆記(14)---redis基本命令總結

del diff lan 命令 列表 對象 很多 順序 reg http://doc.redisfans.com/ 網頁,對所有redis命令的用法與示例進行了詳細的描述 概述 Redis的鍵值可以使用物種數據類型:字符串,散列表,列表,集合,有序集合。本文詳細介紹這

redis學習筆記(九): replication

mas server 完整 開始 客戶端 生成 查看 兩種 partial replication的代碼還沒完全看完,先記錄看到的一些東西: 1、master/slave之間的同步有兩種方式,一種full sync,一種partial sync 2、full sync也有兩