Redis原始碼筆記-初步
目錄
5.3. 多路複用呼叫aeCreateFileEvent 12
6.2. 接受連線請求acceptTcpHandler 14
6.3. 建立client物件createClient 16
6.4. 讀取命令readQueryFromClient 17
7.1. propagate函式(aof&replication) 22
7.2. 寫AOF檔案feedAppendOnlyFile 23
7.3. 資料複製replicationFeedSlaves 25
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中設定rfileProc和wfileProc // rfileProc和wfileProc的初始化均在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原始碼閱讀筆記—sdsRedis系統當中,針對字串進行的更加完善的封裝,建立了一個動態字串,並構建了大量的實用api。相關的實現程式碼為sds.h及sds.c,以下為我的原始碼閱讀筆記。內容較多,逐步更新typedefchar *sds; struct __attribute__ ((__pac redis原始碼閱讀筆記-dict.hdict.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學習筆記(九): replicationmas server 完整 開始 客戶端 生成 查看 兩種 partial replication的代碼還沒完全看完,先記錄看到的一些東西: 1、master/slave之間的同步有兩種方式,一種full sync,一種partial sync 2、full sync也有兩 |