數(shù)據(jù)庫
多數(shù)據(jù)庫結(jié)構(gòu)
一個(gè)Redis實(shí)例可以支持多個(gè)數(shù)據(jù)庫,當(dāng)客戶端與服務(wù)端連接并指定到某個(gè)數(shù)據(jù)庫時(shí)苍日,兩者的結(jié)構(gòu)如下圖所示:
redisServer
和redisClient
是兩個(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)體表示:
dict
屬性是一個(gè)字典肛响,存儲(chǔ)所有的鍵值對(duì),叫做鍵空間惜索。每個(gè)鍵都是一個(gè)字符串對(duì)象特笋,每個(gè)值可以是字符串對(duì)象、列表對(duì)象门扇、哈希表對(duì)象雹有、集合對(duì)象和有序集合對(duì)象當(dāng)中的一種偿渡。
鍵空間操作
在對(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
字典中:
此處dict
和expires
中值相同的鍵其實(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
命令:
- 900秒內(nèi)數(shù)據(jù)庫進(jìn)行了至少1次修改
- 300秒內(nèi)數(shù)據(jù)庫進(jìn)行了至少10次修改
- 60秒內(nèi)數(shù)據(jù)庫進(jìn)行了至少10000次修改
自動(dòng)保存RDB文件的配置存儲(chǔ)在redisServer
結(jié)構(gòu)體的saveparams
數(shù)組中济赎,其數(shù)據(jù)結(jié)構(gòu)如圖所示:
redisServer
還有一個(gè)dirty
屬性記錄了數(shù)據(jù)庫被修改次數(shù),執(zhí)行SAVE
或BGSAVE
命令成功后會(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í)際并不存在滑凉。
value的編碼
RDB文件中的TYPE
屬性決定了value的存儲(chǔ)格式统扳,下面依次介紹。
字符串對(duì)象 [TYPE=REDIS_RDB_TYPE_STRING]
如果字符串對(duì)象的編碼是REDIS_ENCODING_INT
譬涡,那么會(huì)以下面的結(jié)構(gòu)存儲(chǔ):
比如:
如果對(duì)象的編碼是REDIS_ENCODING_RAW
闪幽,那么有兩種情況:
-
字符串長度小于等于20字節(jié),其存儲(chǔ)結(jié)構(gòu)如下:
存儲(chǔ)結(jié)構(gòu) -
如果字符串長度大于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)如圖:
集合對(duì)象 [TYPE=REDIS_RDB_TYPE_SET]
對(duì)應(yīng)的value的編碼是REDIS_ENCODING_HT
腕够,其存儲(chǔ)結(jié)構(gòu)類似列表對(duì)象:
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)如圖:
有序集合對(duì)象 [TYPE=REDIS_RDB_TYPE_ZSET]
對(duì)應(yīng)的value的編碼是REDIS_ENCODING_SKIPLIST
甚淡,其存儲(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)模型,流程圖如下:
當(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):
-
always
每一次事件循環(huán)都進(jìn)行同步 -
everysec
每隔一秒進(jìn)行同步 -
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重寫操作:
- 沒有RDB持久化或AOF持久化在執(zhí)行值朋;
- 沒有AOF重寫在進(jìn)行;
- 當(dāng)前AOF文件大小要大于
server.aof_rewrite_min_size
(默認(rèn)為1MB)巩搏,或者在redis.conf配置了auto-aof-rewrite-min-size
大凶虻恰; - 當(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)志常量:
-
REDIS_MASTER
表示客戶端代表的是一個(gè)主服務(wù)器 -
REDIS_SLAVE
表示客戶端代表是的一個(gè)從服務(wù)器 -
REDIS_LUA_CLIENT
表示客戶端是專門處理Lua腳本的偽客戶端 -
REDIS_BLOCKED
表示客戶端正在被BRPOP
、BLPOP
等命令阻塞 -
REDIS_MULTI
表示客戶端正在執(zhí)行事務(wù) -
REDIS_FORCE_AOF
強(qiáng)制服務(wù)器將當(dāng)前命令寫入AOF文件 -
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ù)工作危队,以下列出部分:
- 如果開啟了慢日志查詢功能聪建,服務(wù)器會(huì)檢查是否要為剛才執(zhí)行的命令添加一條慢查詢?nèi)罩?/li>
- 如果開啟了AOF功能,那么就寫入到AOF緩沖區(qū)
- 如果由其他從服務(wù)器正在復(fù)制當(dāng)前這個(gè)服務(wù)器茫陆,那么服務(wù)器會(huì)把剛才執(zhí)行的命令傳播給所有從服務(wù)器
serverCron函數(shù)
serverCron函數(shù)每100毫秒執(zhí)行一次金麸,負(fù)責(zé)管理服務(wù)器的資源,主要有以下幾個(gè)任務(wù):
- 更新時(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)用解取。 - 更新LRU時(shí)鐘
redisServer
的lruclock
屬性緩存了服務(wù)器的LRU時(shí)鐘,serverCron函數(shù)每10秒更新該屬性的值返顺。Redis通過lruclock
減去每個(gè)redisObject
中的lru
屬性的值就可以得到鍵的空轉(zhuǎn)時(shí)間。 - 更新服務(wù)器每次執(zhí)行命令次數(shù)
Redis通過抽樣計(jì)算的方式把多次抽樣結(jié)果存儲(chǔ)在一個(gè)數(shù)組中蔓肯,通過遍歷數(shù)組計(jì)算平均數(shù)的方式遂鹊,得到過去1秒的平均執(zhí)行命令次數(shù),每100毫秒進(jìn)行一次抽樣蔗包。 - 更新內(nèi)存峰值記錄
每次serverCron函數(shù)執(zhí)行時(shí)都會(huì)對(duì)比之前記錄的內(nèi)存峰值秉扑,如果比之前的大,就設(shè)置為新的內(nèi)存峰值调限。 - 處理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ù)器裆装。 - 管理客戶端資源
serverCron函數(shù)每次執(zhí)行會(huì)對(duì)一定數(shù)量的客戶端進(jìn)行以下檢查: - 如果客戶端在很長一段時(shí)間里都沒有跟服務(wù)端互動(dòng)踱承,那么程序就釋放連接
- 如果客戶端在上一次執(zhí)行命令請(qǐng)求后輸入緩沖區(qū)的大小超過了一定的長度倡缠,那么程序就釋放這塊內(nèi)存并重新創(chuàng)建一個(gè)默認(rèn)大小的輸入緩沖區(qū)
- 管理數(shù)據(jù)庫資源
檢查數(shù)據(jù)庫,刪除過期的鍵茎活,在需要時(shí)也會(huì)對(duì)字典進(jìn)行收縮操作昙沦。 - 執(zhí)行被延遲的BGREWRITEAOF
在服務(wù)器執(zhí)行BGSAVE
命令期間,如果客戶端發(fā)來BGREWRITEAOF
命令载荔,那么服務(wù)器會(huì)在BGSAVE
命令執(zhí)行完畢后延遲執(zhí)行BGREWRITEAOF
命令盾饮。當(dāng)redisServer
的aof_rewrite_scheduled
值為1時(shí),表示有延遲的BGREWRITEAOF
命令需要被執(zhí)行懒熙。 -
檢查持久化操作的運(yùn)行狀態(tài)
serverCron函數(shù)會(huì)檢查當(dāng)前是否有持久化任務(wù)正在執(zhí)行丐谋,如果沒有,那么會(huì)走下面的流程:
檢查持久化操作的運(yùn)行狀態(tài) - 將AOF緩沖區(qū)中的內(nèi)容寫入AOF文件
- 關(guān)閉輸出緩沖區(qū)超過大小限制的客戶端
發(fā)布訂閱
發(fā)布訂閱功能分為兩種煌珊,一種是精確匹配SUBSCRIBE
/UNSUBSCRIBE
号俐,一種是模式匹配PSUBSCRIBE
/PUNSUBSCRIBE
,這兩種訂閱狀態(tài)分別存儲(chǔ)在redisServer
結(jié)構(gòu)體的pubsub_channels
和pubsub_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è)步驟:
- 事務(wù)開始
MULTI
命令標(biāo)志事務(wù)的開始,服務(wù)器會(huì)打開客戶端狀態(tài)的事務(wù)標(biāo)識(shí)亮隙,表示客戶端已進(jìn)入事務(wù)模式途凫。 - 命令入隊(duì)
當(dāng)服務(wù)器接收到處于事務(wù)模式的客戶端發(fā)來的消息后,除非是EXEC
溢吻、DISCARD
维费、WATCH
、MULTI
中的一個(gè)命令促王,否則就把命令放入隊(duì)列犀盟,并且向客戶端回復(fù)QUEUED
消息。 - 事務(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
字段中。
當(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)》