Redis設(shè)計(jì)與實(shí)現(xiàn)2:單機(jī)數(shù)據(jù)庫的實(shí)現(xiàn)

數(shù)據(jù)庫

多數(shù)據(jù)庫結(jié)構(gòu)

一個(gè)Redis實(shí)例可以支持多個(gè)數(shù)據(jù)庫,當(dāng)客戶端與服務(wù)端連接并指定到某個(gè)數(shù)據(jù)庫時(shí)苍日,兩者的結(jié)構(gòu)如下圖所示:

客戶端指定數(shù)據(jù)庫

redisServerredisClient是兩個(gè)結(jié)構(gòu)體囱皿,前者擁有數(shù)據(jù)庫數(shù)組侮穿。當(dāng)客戶端使用SELECT命令選擇數(shù)據(jù)庫是沙绝,會(huì)把客戶端的db指針指向數(shù)據(jù)庫數(shù)組中的一個(gè)對(duì)象。

數(shù)據(jù)庫鍵空間

Redis的每個(gè)數(shù)據(jù)庫都用redisDb的結(jié)構(gòu)體表示:

redisDb

dict 屬性是一個(gè)字典肛响,存儲(chǔ)所有的鍵值對(duì),叫做鍵空間惜索。每個(gè)鍵都是一個(gè)字符串對(duì)象特笋,每個(gè)值可以是字符串對(duì)象、列表對(duì)象门扇、哈希表對(duì)象雹有、集合對(duì)象和有序集合對(duì)象當(dāng)中的一種偿渡。

一個(gè)鍵空間的例子

鍵空間操作

在對(duì)數(shù)據(jù)庫進(jìn)行讀寫操作時(shí)臼寄,Redis還會(huì)執(zhí)行一些額外的操作來記錄一些指標(biāo)霸奕,比如命中率,可以通過INFO命令查看吉拳。

鍵的過期時(shí)間

當(dāng)用EXPIRE/PEXPIRE/EXPIREAT/PEXPIREAT給鍵設(shè)置過期時(shí)間時(shí)质帅,過期時(shí)間會(huì)保存在redisDb對(duì)象的expires字典中:

保存過期時(shí)間

此處dictexpires中值相同的鍵其實(shí)是同一個(gè)對(duì)象,只是為了簡化說明所以劃成兩份留攒。

TTL命令和PTTL命令會(huì)計(jì)算當(dāng)前時(shí)間和expires中存儲(chǔ)的時(shí)間的差值然后輸出剩下的生存時(shí)間煤惩。

過期鍵的刪除策略

Redis刪除過期鍵的策略結(jié)合了惰性刪除和定期刪除兩種。

惰性刪除

當(dāng)對(duì)鍵進(jìn)行讀取命令時(shí)炼邀,Redis會(huì)判斷該鍵是否過期魄揉,如果過期那么就刪除,然后繼續(xù)執(zhí)行命令拭宁。缺點(diǎn)是洛退,如果一個(gè)鍵一直沒有被訪問,那么就永遠(yuǎn)停留在內(nèi)存里杰标,造成資源浪費(fèi)兵怯,因此Redis還采用了定期刪除的策略。

定期刪除

當(dāng)定期刪除被觸發(fā)時(shí)腔剂,Redis會(huì)遍歷各個(gè)數(shù)據(jù)庫媒区,然后從expires字典中隨機(jī)檢查一部分鍵的過期時(shí)間,如果過期了那么就刪除過期的鍵掸犬。

AOF袜漩、RDB和復(fù)制功能對(duì)過期鍵的處理

生成RDB文件以及AOF重寫

當(dāng)Redis生成RDB文件或者進(jìn)行AOF重寫時(shí),會(huì)對(duì)鍵進(jìn)行檢查湾碎,過期的鍵不會(huì)寫入到文件中噪服。

載入RDB文件

如果服務(wù)器以主服務(wù)器模式運(yùn)行,那么過期的鍵不會(huì)被載入內(nèi)存胜茧;如果服務(wù)器以從服務(wù)器模式運(yùn)行粘优,那么過期的鍵會(huì)載入內(nèi)存廊遍。

AOF文件寫入

當(dāng)服務(wù)器以AOF持久化模式運(yùn)行時(shí),如果某個(gè)鍵已經(jīng)過期但還沒有被刪除,那么不會(huì)有任何影響。當(dāng)過期鍵被刪除后,Redis會(huì)向AOF文件追加一條DEL命令俐东,顯示記錄鍵已被刪除乒裆。

復(fù)制

當(dāng)服務(wù)器運(yùn)行在主從復(fù)制模式下運(yùn)行時(shí)稿黄,當(dāng)主服務(wù)器刪除一個(gè)鍵后會(huì)向從服務(wù)器發(fā)送一個(gè)DEL命令陵珍;當(dāng)客戶端在從服務(wù)器讀取時(shí)即使碰到過期的鍵也不會(huì)刪除,只有收到主服務(wù)器發(fā)來的DEL命令后才會(huì)把鍵刪除殖卑。

RDB持久化

Redis提供了RDB持久化功能可以將數(shù)據(jù)存儲(chǔ)在硬盤里肛冶,我們可以使用SAVE或者BGSAVE命令創(chuàng)建RDB文件,兩者的區(qū)別是SAVE命令由服務(wù)器進(jìn)程執(zhí)行爱只,執(zhí)行時(shí)會(huì)阻塞進(jìn)程训柴,在此期間Redis會(huì)阻塞來自客戶端的請(qǐng)求,直到RDB文件創(chuàng)建完畢贡翘;BGSAVE命令由子進(jìn)程執(zhí)行,執(zhí)行完畢后會(huì)通知服務(wù)器進(jìn)程踊东,因此不會(huì)阻塞客戶端的請(qǐng)求北滥。

自動(dòng)創(chuàng)建RDB文件

處了上述兩個(gè)命令之外,我們還可以通過配置來讓Redis自動(dòng)創(chuàng)建RDB文件闸翅,其本質(zhì)是自動(dòng)執(zhí)行BGSAVE命令再芋。

#自動(dòng)執(zhí)行BGSAVE命令的默認(rèn)配置
save 900 1
save 300 10
save 60 10000

如果你進(jìn)行了上面的配置,那么只要滿足下面三個(gè)條件的任意一個(gè)坚冀,服務(wù)器就會(huì)執(zhí)行BGSAVE命令:

  1. 900秒內(nèi)數(shù)據(jù)庫進(jìn)行了至少1次修改
  2. 300秒內(nèi)數(shù)據(jù)庫進(jìn)行了至少10次修改
  3. 60秒內(nèi)數(shù)據(jù)庫進(jìn)行了至少10000次修改

自動(dòng)保存RDB文件的配置存儲(chǔ)在redisServer結(jié)構(gòu)體的saveparams數(shù)組中济赎,其數(shù)據(jù)結(jié)構(gòu)如圖所示:

saveparam數(shù)據(jù)結(jié)構(gòu)

redisServer還有一個(gè)dirty屬性記錄了數(shù)據(jù)庫被修改次數(shù),執(zhí)行SAVEBGSAVE命令成功后會(huì)自動(dòng)歸零记某。Redis通過一個(gè)周期性執(zhí)行的函數(shù)司训,默認(rèn)每100毫秒對(duì)自動(dòng)配置以及dirty屬性進(jìn)行檢查,如果達(dá)到觸發(fā)條件就執(zhí)行BGSAVE命令液南。

RDB文件結(jié)構(gòu)

RDB文件(版本6)結(jié)構(gòu)如下圖所示壳猜,圖中的層次只是為了展示方便,實(shí)際并不存在滑凉。


RDB文件結(jié)構(gòu)

value的編碼

RDB文件中的TYPE屬性決定了value的存儲(chǔ)格式统扳,下面依次介紹。

字符串對(duì)象 [TYPE=REDIS_RDB_TYPE_STRING]

如果字符串對(duì)象的編碼是REDIS_ENCODING_INT譬涡,那么會(huì)以下面的結(jié)構(gòu)存儲(chǔ):

存儲(chǔ)結(jié)構(gòu)

比如:
存儲(chǔ)結(jié)構(gòu)

如果對(duì)象的編碼是REDIS_ENCODING_RAW闪幽,那么有兩種情況:

  1. 字符串長度小于等于20字節(jié),其存儲(chǔ)結(jié)構(gòu)如下:


    存儲(chǔ)結(jié)構(gòu)
  2. 如果字符串長度大于20字節(jié)涡匀,那么字符串會(huì)經(jīng)過壓縮再存儲(chǔ)盯腌,其結(jié)構(gòu)如下:

    存儲(chǔ)結(jié)構(gòu)

    REDIS_RDB_ENC_LZF表示經(jīng)過LZF算法壓縮。

列表對(duì)象 [TYPE=REDIS_RDB_TYPE_LIST]

對(duì)應(yīng)的value的編碼是REDIS_ENCODING_LINKEDLIST陨瘩,其存儲(chǔ)結(jié)構(gòu)如圖:

存儲(chǔ)結(jié)構(gòu)

集合對(duì)象 [TYPE=REDIS_RDB_TYPE_SET]

對(duì)應(yīng)的value的編碼是REDIS_ENCODING_HT腕够,其存儲(chǔ)結(jié)構(gòu)類似列表對(duì)象:

存儲(chǔ)結(jié)構(gòu)

INTSET編碼的集合對(duì)象 [TYPE=REDIS_RDB_TYPE_SET_INTSET]

value保存的是一個(gè)整數(shù)集合,Redis會(huì)把整數(shù)集合轉(zhuǎn)換為一個(gè)字符串對(duì)象舌劳,然后把轉(zhuǎn)換后出的字符串對(duì)象寫入RDB文件帚湘。

哈希表對(duì)象 [TYPE=REDIS_RDB_TYPE_HASH]

對(duì)應(yīng)的value的編碼是REDIS_ENCODING_HT,其存儲(chǔ)結(jié)構(gòu)如圖:

存儲(chǔ)結(jié)構(gòu)

有序集合對(duì)象 [TYPE=REDIS_RDB_TYPE_ZSET]

對(duì)應(yīng)的value的編碼是REDIS_ENCODING_SKIPLIST甚淡,其存儲(chǔ)結(jié)構(gòu)如圖:

存儲(chǔ)結(jié)構(gòu)

ZIPLIST編碼的列表大诸、哈希表或有序集合 [TYPE=REDIS_RDB_TYPE_LIST_ZIPLIST,REDIS_RDB_TYPE_HASH_ZIPLIST,REDIS_RDB_TYPE_ZSET_ZIPLIST]

value保存的是一個(gè)壓縮列表對(duì)象,Redis會(huì)把它轉(zhuǎn)換為一個(gè)字符串對(duì)象,然后把轉(zhuǎn)換后出的字符串對(duì)象寫入RDB文件资柔。

AOF持久化

RDB持久化功能是把數(shù)據(jù)庫的鍵值對(duì)存儲(chǔ)到文件中焙贷,AOF持久化功能則是把服務(wù)器執(zhí)行的寫命令以Redis的命令請(qǐng)求協(xié)議格式存儲(chǔ)到文件中。

在了解AOF持久化過程前贿堰,我們需要先了解Redis的進(jìn)程模型辙芍,其本質(zhì)是一個(gè)事件循環(huán)模型,流程圖如下:


事件循環(huán)模型

當(dāng)執(zhí)行完一個(gè)寫命令后羹与,服務(wù)器會(huì)把命令以協(xié)議格式追加到AOF緩沖區(qū)的末尾故硅,在每一個(gè)事件循環(huán)結(jié)束前會(huì)將AOF緩沖區(qū)中的內(nèi)容寫到AOF文件。
需要注意的是纵搁,寫入文件不代表寫入磁盤吃衅,現(xiàn)代操作系統(tǒng)中為提高效率,通常在寫文件時(shí)會(huì)把數(shù)據(jù)寫入內(nèi)存緩沖區(qū)诡渴,等到緩沖區(qū)滿時(shí)或者超過一定時(shí)間后才會(huì)真正寫入磁盤捐晶,這一操作稱為同步菲语。
雖然每次事件循環(huán)都會(huì)寫入AOF文件妄辩,但是并不是每次都會(huì)執(zhí)行同步操作,Redis提供了appendfsync配置項(xiàng)山上,有以下3個(gè)選項(xiàng):

  1. always 每一次事件循環(huán)都進(jìn)行同步
  2. everysec 每隔一秒進(jìn)行同步
  3. no 由操作系統(tǒng)決定是否同步

還原數(shù)據(jù)時(shí)眼耀,Redis會(huì)創(chuàng)建一個(gè)沒有網(wǎng)絡(luò)連接的偽客戶端,依次執(zhí)行AOF文件中的命令佩憾。執(zhí)行完畢后數(shù)據(jù)庫就恢復(fù)了之前的狀態(tài)哮伟。

AOF重寫

如果AOF文件中存儲(chǔ)的命令不斷增長下去,那么AOF文件會(huì)越來越大妄帘,載入時(shí)間會(huì)越來越長楞黄。Redis提供了一種重寫AOF文件的方法,可以把多條命令合并成一條(或幾條)生成一個(gè)新的AOF文件抡驼,并替換原文件鬼廓。

RPUSH list "A" "B"
RPUSH list "C"
RPUSH list "D" "E"
LPOP list
LPOP list
RPUSH list "F" "G"

上面的5條語句經(jīng)過重寫后可以合并成下面一行命令:

RPUSH list "C" "D" "E" "F" "G"

Redis通過讀取數(shù)據(jù)庫當(dāng)前鍵值對(duì)的方式進(jìn)行AOF文件重寫,而不是分析原來的AOF文件致盟。
重寫過程中會(huì)產(chǎn)生大量的寫操作碎税,為了不阻塞進(jìn)程,Redis在子進(jìn)程中進(jìn)行重寫工作馏锡。這樣導(dǎo)致的一個(gè)問題是雷蹂,當(dāng)AOF重寫正在進(jìn)行時(shí),可能會(huì)有新的命令被服務(wù)器執(zhí)行杯道,導(dǎo)致新的AOF文件和數(shù)據(jù)庫狀態(tài)不一致匪煌。Redis的解決方法是:除了AOF緩沖區(qū)外再增加一個(gè)AOF重寫緩沖區(qū),這個(gè)緩沖區(qū)只在開啟子進(jìn)程后使用,服務(wù)器把執(zhí)行的寫命令同時(shí)追加到這兩個(gè)緩沖區(qū)的末尾萎庭,當(dāng)子進(jìn)程完成重寫后通知服務(wù)器進(jìn)程玛歌,服務(wù)器把重寫緩沖區(qū)中的內(nèi)容全部寫入新的AOF文件,然后原子地替換原來的AOF文件擎椰。

自動(dòng)AOF重寫

當(dāng)serverCron函數(shù)執(zhí)行時(shí)支子,它會(huì)檢查以下條件是否全部滿足,如果全部滿足的話达舒,就觸發(fā)自動(dòng)的AOF重寫操作:

  1. 沒有RDB持久化或AOF持久化在執(zhí)行值朋;
  2. 沒有AOF重寫在進(jìn)行;
  3. 當(dāng)前AOF文件大小要大于server.aof_rewrite_min_size(默認(rèn)為1MB)巩搏,或者在redis.conf配置了auto-aof-rewrite-min-size大凶虻恰;
  4. 當(dāng)前AOF文件大小和最后一次重寫后的大小之間的比率等于或者等于指定的增長百分比(在配置文件設(shè)置了auto-aof-rewrite-percentage參數(shù)贯底,不設(shè)置默認(rèn)為100%)

客戶端

在網(wǎng)絡(luò)連接部分丰辣,Redis使用了I/O多路復(fù)用技術(shù),用單線程接收和響應(yīng)客戶端的請(qǐng)求禽捆。每個(gè)客戶端對(duì)應(yīng)一個(gè)redisClient類型的結(jié)構(gòu)體笙什,多個(gè)客戶端以鏈表的形式存儲(chǔ)在redisServer對(duì)象中∨呦耄可以使用CLIENT list命令查看所有的客戶端信息琐凭,其中age屬性是客戶端連接到服務(wù)器的秒數(shù)。

id=2 addr=127.0.0.1:35400 fd=6 name= age=8 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client

可以使用CLIENT SETNAME name為當(dāng)前連接的客戶端設(shè)置一個(gè)名字浊服。

id=2 addr=127.0.0.1:35400 fd=6 name=hello age=603 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client

標(biāo)志

redisClient結(jié)構(gòu)體中有一個(gè)重要的屬性flags统屈,主要有以下幾種標(biāo)志常量:

  1. REDIS_MASTER 表示客戶端代表的是一個(gè)主服務(wù)器
  2. REDIS_SLAVE 表示客戶端代表是的一個(gè)從服務(wù)器
  3. REDIS_LUA_CLIENT 表示客戶端是專門處理Lua腳本的偽客戶端
  4. REDIS_BLOCKED 表示客戶端正在被BRPOPBLPOP等命令阻塞
  5. REDIS_MULTI 表示客戶端正在執(zhí)行事務(wù)
  6. REDIS_FORCE_AOF 強(qiáng)制服務(wù)器將當(dāng)前命令寫入AOF文件
  7. REDIS_FORCE_REPL 強(qiáng)制主服務(wù)器將當(dāng)前命令復(fù)制給所有從服務(wù)器

flags屬性可以是多個(gè)常量的組合牙躺,比如REDIS_LUA_CLIENT|REDIS_FORCE_AOF

服務(wù)器

當(dāng)服務(wù)器接收到客戶端發(fā)來的命令后愁憔,會(huì)在命令表中查找對(duì)應(yīng)的命令,其數(shù)據(jù)結(jié)構(gòu)如圖所示:

命令表

每一個(gè)命令對(duì)應(yīng)一個(gè)redisCommand類型的對(duì)象孽拷,其中:

  • name 表示命令的名稱
  • proc 指向具體的實(shí)現(xiàn)函數(shù)
  • arity 命令的參數(shù)個(gè)數(shù)吨掌,命令本身也是參數(shù)。如果該值是負(fù)數(shù)乓搬,如-N思犁,則表示參數(shù)的數(shù)量大于等于N
  • sflags 命令的屬性,比如是不是可寫命令进肯,是不是只讀命令每種屬性由一個(gè)字符表示激蹲,比如wm表示這是一個(gè)可寫命令(w),并且可能需要占用大量內(nèi)存江掩,服務(wù)器需要進(jìn)行內(nèi)存空間的檢查(m)学辱,SET命令的屬性就是wm

服務(wù)器查找命令時(shí)是忽略大小寫的乘瓤。

命令執(zhí)行器

在執(zhí)行命令前,服務(wù)器會(huì)執(zhí)行一些檢查工作策泣,比如命令參數(shù)個(gè)數(shù)是否正確衙傀、客戶端是否通過了身份驗(yàn)證、如果服務(wù)器開啟了maxmemory功能萨咕,那么還要檢查內(nèi)存占用情況并按需回收等步驟统抬。
在命令執(zhí)行后,服務(wù)器也會(huì)執(zhí)行一些后續(xù)工作危队,以下列出部分:

  1. 如果開啟了慢日志查詢功能聪建,服務(wù)器會(huì)檢查是否要為剛才執(zhí)行的命令添加一條慢查詢?nèi)罩?/li>
  2. 如果開啟了AOF功能,那么就寫入到AOF緩沖區(qū)
  3. 如果由其他從服務(wù)器正在復(fù)制當(dāng)前這個(gè)服務(wù)器茫陆,那么服務(wù)器會(huì)把剛才執(zhí)行的命令傳播給所有從服務(wù)器

serverCron函數(shù)

serverCron函數(shù)每100毫秒執(zhí)行一次金麸,負(fù)責(zé)管理服務(wù)器的資源,主要有以下幾個(gè)任務(wù):

  1. 更新時(shí)間緩存
    為了減少系統(tǒng)調(diào)用的次數(shù)簿盅,在redisServer對(duì)象中有兩個(gè)字段分別緩存了秒級(jí)和毫秒級(jí)精度的UNIX時(shí)間戳挥下,由于精度不高,所以時(shí)間緩存僅用于打印日志桨醋、更新服務(wù)器的LRU時(shí)鐘棚瘟、決定是否執(zhí)行持久化任務(wù)、計(jì)算服務(wù)器uptime這類對(duì)時(shí)間精度不高的功能上讨盒。對(duì)于設(shè)置過期時(shí)間等需要高精度時(shí)間的功能還是會(huì)通過系統(tǒng)調(diào)用解取。
  2. 更新LRU時(shí)鐘
    redisServerlruclock屬性緩存了服務(wù)器的LRU時(shí)鐘,serverCron函數(shù)每10秒更新該屬性的值返顺。Redis通過lruclock減去每個(gè)redisObject中的lru屬性的值就可以得到鍵的空轉(zhuǎn)時(shí)間。
  3. 更新服務(wù)器每次執(zhí)行命令次數(shù)
    Redis通過抽樣計(jì)算的方式把多次抽樣結(jié)果存儲(chǔ)在一個(gè)數(shù)組中蔓肯,通過遍歷數(shù)組計(jì)算平均數(shù)的方式遂鹊,得到過去1秒的平均執(zhí)行命令次數(shù),每100毫秒進(jìn)行一次抽樣蔗包。
  4. 更新內(nèi)存峰值記錄
    每次serverCron函數(shù)執(zhí)行時(shí)都會(huì)對(duì)比之前記錄的內(nèi)存峰值秉扑,如果比之前的大,就設(shè)置為新的內(nèi)存峰值调限。
  5. 處理SIGTERM信號(hào)
    當(dāng)Redis收到SIGTERM信號(hào)時(shí)會(huì)把redisServer對(duì)象的shutdown_asap屬性設(shè)置為1舟陆,表示關(guān)閉服務(wù)器,serverCron函數(shù)每次都會(huì)檢查這個(gè)屬性耻矮,如果要關(guān)閉服務(wù)器秦躯,則執(zhí)行RDB持久化,然后關(guān)閉服務(wù)器裆装。
  6. 管理客戶端資源
    serverCron函數(shù)每次執(zhí)行會(huì)對(duì)一定數(shù)量的客戶端進(jìn)行以下檢查:
  7. 如果客戶端在很長一段時(shí)間里都沒有跟服務(wù)端互動(dòng)踱承,那么程序就釋放連接
  8. 如果客戶端在上一次執(zhí)行命令請(qǐng)求后輸入緩沖區(qū)的大小超過了一定的長度倡缠,那么程序就釋放這塊內(nèi)存并重新創(chuàng)建一個(gè)默認(rèn)大小的輸入緩沖區(qū)
  9. 管理數(shù)據(jù)庫資源
    檢查數(shù)據(jù)庫,刪除過期的鍵茎活,在需要時(shí)也會(huì)對(duì)字典進(jìn)行收縮操作昙沦。
  10. 執(zhí)行被延遲的BGREWRITEAOF
    在服務(wù)器執(zhí)行BGSAVE命令期間,如果客戶端發(fā)來BGREWRITEAOF命令载荔,那么服務(wù)器會(huì)在BGSAVE命令執(zhí)行完畢后延遲執(zhí)行BGREWRITEAOF命令盾饮。當(dāng)redisServeraof_rewrite_scheduled值為1時(shí),表示有延遲的BGREWRITEAOF命令需要被執(zhí)行懒熙。
  11. 檢查持久化操作的運(yùn)行狀態(tài)
    serverCron函數(shù)會(huì)檢查當(dāng)前是否有持久化任務(wù)正在執(zhí)行丐谋,如果沒有,那么會(huì)走下面的流程:


    檢查持久化操作的運(yùn)行狀態(tài)
  12. 將AOF緩沖區(qū)中的內(nèi)容寫入AOF文件
  13. 關(guān)閉輸出緩沖區(qū)超過大小限制的客戶端

發(fā)布訂閱

發(fā)布訂閱功能分為兩種煌珊,一種是精確匹配SUBSCRIBE/UNSUBSCRIBE号俐,一種是模式匹配PSUBSCRIBE/PUNSUBSCRIBE,這兩種訂閱狀態(tài)分別存儲(chǔ)在redisServer結(jié)構(gòu)體的pubsub_channelspubsub_patterns字段中定庵。

精確匹配
模式匹配

當(dāng)客戶端向服務(wù)器發(fā)送PUBLISH消息時(shí)吏饿,服務(wù)器會(huì)遍歷pubsub_channels中某個(gè)頻道下的客戶端發(fā)送消息,然后再遍歷pubsub_patterns蔬浙,找出符合條件的客戶端發(fā)送消息猪落。

在集群模式中,當(dāng)客戶端向某個(gè)節(jié)點(diǎn)發(fā)送PUBLISH消息后畴博,服務(wù)器會(huì)向整個(gè)集群廣播這條消息笨忌,每一個(gè)節(jié)點(diǎn)都會(huì)執(zhí)行PUBLISH消息,這樣俱病,連接在其它節(jié)點(diǎn)上的客戶端也能收到這條消息官疲。

事務(wù)

Redis的事務(wù)主要有三個(gè)步驟:

  1. 事務(wù)開始
    MULTI命令標(biāo)志事務(wù)的開始,服務(wù)器會(huì)打開客戶端狀態(tài)的事務(wù)標(biāo)識(shí)亮隙,表示客戶端已進(jìn)入事務(wù)模式途凫。
  2. 命令入隊(duì)
    當(dāng)服務(wù)器接收到處于事務(wù)模式的客戶端發(fā)來的消息后,除非是EXEC溢吻、DISCARD维费、WATCHMULTI中的一個(gè)命令促王,否則就把命令放入隊(duì)列犀盟,并且向客戶端回復(fù)QUEUED消息。
  3. 事務(wù)執(zhí)行
    服務(wù)器依次執(zhí)行隊(duì)列中的消息蝇狼,清空客戶端的事務(wù)狀態(tài)阅畴,并把執(zhí)行命令的全部結(jié)果返回給客戶端。

Redis的事務(wù)具有原子性题翰。事務(wù)中的命令要么全部執(zhí)行恶阴,要么一個(gè)都不執(zhí)行(比如監(jiān)控的鍵被修改)诈胜,但是Redis的事務(wù)即使全部執(zhí)行不代表所有的命令都正確執(zhí)行,比如某條命令參數(shù)錯(cuò)誤導(dǎo)致執(zhí)行失敗冯事,Redis仍然會(huì)執(zhí)行完接下來的命令焦匈,對(duì)已經(jīng)執(zhí)行的命令不會(huì)有任何影響。

作者認(rèn)為事務(wù)中出現(xiàn)錯(cuò)誤通常是編程錯(cuò)誤昵仅,比如少寫了一個(gè)參數(shù)缓熟。

WATCH命令

服務(wù)器會(huì)把當(dāng)前所有正在被監(jiān)控的鍵以及客戶端的狀態(tài)存在redisDb結(jié)構(gòu)體的watched_keys字段中。

watched_keys

當(dāng)執(zhí)行對(duì)數(shù)據(jù)庫的修改后(如SET摔笤、LPUSH等命令)够滑,會(huì)遍歷watched_keys字段,如果被修改的鍵存在字典中吕世,那么就打開該鍵關(guān)聯(lián)的所有客戶端的REDIS_DIRTY_CAS標(biāo)識(shí)彰触,當(dāng)服務(wù)器執(zhí)行EXEC時(shí),它會(huì)先檢查客戶端的REDIS_DIRTY_CAS標(biāo)識(shí)是否打開命辖,如果已經(jīng)打開嗅骄,那么就拒絕執(zhí)行事務(wù)平酿。

參考/圖片出處:
1. 機(jī)械工業(yè)出版社 -《Redis設(shè)計(jì)與實(shí)現(xiàn)》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末唱凯,一起剝皮案震驚了整個(gè)濱河市喜庞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌终娃,老刑警劉巖味廊,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異棠耕,居然都是意外死亡余佛,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門昧辽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衙熔,“玉大人,你說我怎么就攤上這事搅荞。” “怎么了框咙?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵咕痛,是天一觀的道長。 經(jīng)常有香客問我喇嘱,道長茉贡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任者铜,我火速辦了婚禮腔丧,結(jié)果婚禮上放椰,老公的妹妹穿的比我還像新娘。我一直安慰自己愉粤,他們只是感情好砾医,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著衣厘,像睡著了一般如蚜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上影暴,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天错邦,我揣著相機(jī)與錄音,去河邊找鬼型宙。 笑死撬呢,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的妆兑。 我是一名探鬼主播魂拦,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼箭跳!你這毒婦竟也來了晨另?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤谱姓,失蹤者是張志新(化名)和其女友劉穎借尿,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屉来,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡路翻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了茄靠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茂契。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖慨绳,靈堂內(nèi)的尸體忽然破棺而出掉冶,到底是詐尸還是另有隱情,我是刑警寧澤脐雪,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布厌小,位于F島的核電站,受9級(jí)特大地震影響战秋,放射性物質(zhì)發(fā)生泄漏璧亚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一脂信、第九天 我趴在偏房一處隱蔽的房頂上張望癣蟋。 院中可真熱鬧透硝,春花似錦、人聲如沸疯搅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽秉撇。三九已至甜攀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間琐馆,已是汗流浹背规阀。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瘦麸,地道東北人谁撼。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像滋饲,于是被迫代替她去往敵國和親厉碟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容