第一章 數(shù)據(jù)結(jié)構(gòu)與對(duì)象:
Redis數(shù)據(jù)庫(kù)里面每個(gè)鍵值對(duì)都是有 對(duì)象組成的,其中:
1、 數(shù)據(jù)庫(kù)鍵總是一個(gè)字符串對(duì)象
2、 而數(shù)據(jù)庫(kù)鍵的值 可以是 字符串對(duì)象String
、列表對(duì)象List
空繁、 哈希對(duì)象Hash
、集合對(duì)象set
朱庆、有序集合對(duì)象sorted set object
盛泡。 每一種數(shù)據(jù)結(jié)構(gòu),都有它適用的應(yīng)用場(chǎng)景娱颊。
Redis沒(méi)有直接使用C語(yǔ)言傳統(tǒng)字符串表示(以空字符結(jié)尾的字符數(shù)組)饭于,而是自己構(gòu)建一種名為“簡(jiǎn)單動(dòng)態(tài)字符串”(simple dynamic string SDS),用作Redis的默認(rèn)字符串表示。
2维蒙、SDS與C字符串的區(qū)別
C語(yǔ)言使用長(zhǎng)度為N+1的字符數(shù)組來(lái)表示長(zhǎng)度為N的字符串掰吕,并且字符數(shù)組的最后一個(gè)元素總是空字符 ‘\0’
1、常數(shù)復(fù)雜度獲取字符串長(zhǎng)度
在redis颅痊,程序只要訪問(wèn)SDS的len屬性殖熟、就能立即知道SDS的長(zhǎng)度。通過(guò)使用SDS而不是字符串斑响、Redis將獲取字符串長(zhǎng)度所需的復(fù)雜度從O(N)降低到了O(1)菱属。這確保了獲取字符串長(zhǎng)度的工作不會(huì)成為Redis的性能瓶頸。
2舰罚、 杜絕緩沖區(qū)溢出
減少修改字符串時(shí)帶來(lái)的內(nèi)存重新分配次數(shù)
通過(guò)未使用空間纽门,SDS實(shí)現(xiàn)了空間預(yù)分配和惰性空間釋放兩種優(yōu)化策略。
第三章 redis鏈表
作為一種常用的數(shù)據(jù)結(jié)構(gòu)营罢、鏈表內(nèi)置在很多高級(jí)的編程語(yǔ)言里面赏陵,因?yàn)镽edis使用的C語(yǔ)言并沒(méi)有內(nèi)置這種數(shù)據(jù)結(jié)構(gòu)饼齿,所以Redis構(gòu)建了自己的鏈表實(shí)現(xiàn)。
鏈表在Redis中的應(yīng)用非常廣泛蝙搔、比如列表鍵的底層實(shí)現(xiàn)之一就是鏈表缕溉。當(dāng)一個(gè)列表鍵包含很多數(shù)量的元素,Redis就會(huì)用鏈表作為列表建為底層實(shí)現(xiàn)吃型。
鏈表結(jié)構(gòu):
typedef struct listNode{
struct listNode *prev;
struct listNode *next;
void *value;
}listNode;
哈希表
5证鸥、跳躍表
跳躍表(skiplist)是一種有序數(shù)據(jù)結(jié)構(gòu),它通過(guò)對(duì)每個(gè)節(jié)點(diǎn)中維持多個(gè)指向其他節(jié)點(diǎn)的指針勤晚,從而達(dá)到快速訪問(wèn)節(jié)點(diǎn)的目的枉层。 當(dāng)我們?cè)谧?大量數(shù)據(jù)排序顯示的功能,比如“拍賣(mài)行赐写、排行榜”
的時(shí)候鸟蜡,需要維護(hù)一個(gè)多變切有序的數(shù)據(jù)結(jié)構(gòu)。而跳躍表就很符合這種場(chǎng)景血淌。
Redis使用跳躍表作為
有序集合鍵
的底層實(shí)現(xiàn)之一矩欠,如果一個(gè)有序集合中包含的元素?cái)?shù)量比較多财剖。又或者有序集合中元素的成員是比較長(zhǎng)的字符串悠夯,Redis就會(huì)使用跳躍表作為有序集合鍵
的底層實(shí)現(xiàn)。
6躺坟、整數(shù)集合的實(shí)現(xiàn)
整數(shù)集合(intset)是集合鍵
的底層實(shí)現(xiàn)之一 沦补、當(dāng)一個(gè)集合只包含整數(shù)值元素
,并且這個(gè)集合的元素?cái)?shù)量不多時(shí)
咪橙,Redis就會(huì)使用整數(shù)集合作為集合鍵的底層實(shí)現(xiàn)夕膀。(因?yàn)榈讓佑玫氖?code>數(shù)組存儲(chǔ))
集合中不會(huì)出現(xiàn)重復(fù)
的元素且有序
。
壓縮列表
八美侦、對(duì)象
Redis使用對(duì)象來(lái)表示數(shù)據(jù)庫(kù)中的鍵和值产舞、每當(dāng)我們?cè)趓edis數(shù)據(jù)庫(kù)中創(chuàng)建一個(gè)鍵值對(duì),我們至少會(huì)創(chuàng)建兩個(gè)對(duì)象菠剩、一個(gè)對(duì)象為鍵值對(duì)的鍵
易猫,另一個(gè)對(duì)象為鍵值對(duì)的值
。每個(gè)對(duì)象都由一個(gè)redisObject結(jié)構(gòu)表示具壮、該結(jié)構(gòu)中和保存數(shù)據(jù)有關(guān)的三個(gè)屬性分別是 type
准颓、encoding屬性
、prt屬性
.
typedef struct redisObject{
unsigned type:4;
unsigned encoding: 4;
void *ptr;
}
下面為redis的五種對(duì)象類(lèi)型:
每種類(lèi)型的對(duì)象都至少使用兩種不同的編碼棺妓、對(duì)象的ptr指針指向?qū)ο蟮牡讓訉?shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)攘已、而這些數(shù)據(jù)結(jié)構(gòu)由對(duì)象的encoding屬性決定。
通過(guò)encoding屬性來(lái)設(shè)定對(duì)象所使用的編碼怜跑、而不是特定類(lèi)型的對(duì)象關(guān)聯(lián)一種固定的編碼样勃,極大地提升了redis的靈活性和效率,因?yàn)镽edis可以根據(jù)不同使用場(chǎng)景來(lái)為一個(gè)對(duì)象設(shè)置不同的編碼、從而優(yōu)化對(duì)象在某一場(chǎng)景的效率彤灶。
1看幼、因?yàn)閴嚎s列表比雙端列表更節(jié)約內(nèi)存、并且在元素較少的情況下幌陕、在內(nèi)存中連續(xù)方式保存的壓縮列表比起雙端鏈表可以更快的載入緩存诵姜。
2、 隨著列表對(duì)象包含的元素越來(lái)越多搏熄、使用壓縮列表來(lái)保存元素的優(yōu)勢(shì)逐漸消失棚唆、對(duì)象就會(huì)將底層從壓縮列表轉(zhuǎn)向功能更強(qiáng)、更適合保存大量元素的雙端鏈表上面心例。
8.5 集合對(duì)象
集合對(duì)象的編碼可以是intset
或者 hashtable
intset編碼的集合對(duì)象使用整數(shù)集合作為底層實(shí)現(xiàn)宵凌,集合對(duì)象包含的所有元素都被保存在整數(shù)集合里面娃圆。
另一方面昭齐、hashtable編碼的集合對(duì)象使用字典作為底層實(shí)現(xiàn),字典的每個(gè)鍵都是一個(gè)字符串對(duì)象胎源,每個(gè)字符串對(duì)象包含了一個(gè)集合元素译株,而字典的值則全部設(shè)置為NULL.
8.6 有序集合對(duì)象
有序集合編碼可以是 ziplist
或者skiplist
ziplist 編碼的壓縮列表對(duì)象使用壓縮列表作為底層實(shí)現(xiàn)瓜喇,每個(gè)元素使用兩個(gè)緊挨在一起的壓縮列表節(jié)點(diǎn)來(lái)保存,每個(gè)節(jié)點(diǎn)保存的元素的成員歉糜,第二個(gè)元素保存元素的分值(score)乘寒。
壓縮列表內(nèi)的集合元素按分值從小到大進(jìn)行排序,分值較小的元素放置在靠近表頭的方向匪补,而分值較大的元素放置在靠近表尾的方向
第10章 RDB持久化
Redis是內(nèi)存數(shù)據(jù)庫(kù)伞辛、它將自己的數(shù)據(jù)庫(kù)狀態(tài)存儲(chǔ)在內(nèi)存里面,所以如果不想辦法將存儲(chǔ)在內(nèi)存中的數(shù)據(jù)保存在磁盤(pán)里夯缺,一旦服務(wù)器進(jìn)程退出蚤氏,服務(wù)器中的數(shù)據(jù)庫(kù)狀態(tài)也會(huì)消失不見(jiàn)。
為了解決這個(gè)問(wèn)題踊兜,Redis提供了RDB持久化功能
竿滨,這個(gè)功能可以將Redis在內(nèi)存中數(shù)據(jù)庫(kù)狀態(tài)保存在磁盤(pán)里面,避免數(shù)據(jù)意外丟失润文。
RDB文件是一個(gè)經(jīng)過(guò)壓縮的二進(jìn)制文件
姐呐。
RDB文件的創(chuàng)建與載入
有兩個(gè)Redis命令可以用于生成RDB文件,一個(gè)是SAVE
,另一個(gè)是BGSAVE
典蝌。
SAVE命令會(huì)阻塞Redis服務(wù)器進(jìn)程曙砂,直到RDB文件創(chuàng)建完畢為止,在服務(wù)器進(jìn)程阻塞期間骏掀,服務(wù)器不能進(jìn)行任何命令請(qǐng)求鸠澈。
和SAVE命令不同柱告,BGSAVE命令會(huì)派生出一個(gè)子進(jìn)程,然后由子進(jìn)程負(fù)責(zé)創(chuàng)建RDB文件笑陈,服務(wù)器進(jìn)程繼續(xù)處理命令請(qǐng)求际度。
Redis沒(méi)有載入RDB文件的命令,RDB文件的載入工作是在服務(wù)器啟動(dòng)時(shí)自動(dòng)執(zhí)行的涵妥。
另外乖菱,因?yàn)锳OF文件的更新頻率通常比RDB文件的更新頻率高,所以:
如果服務(wù)器開(kāi)啟了AOF持久化功能蓬网,那么服務(wù)器器會(huì)優(yōu)先使用AOF文件來(lái)還原數(shù)據(jù)庫(kù)狀態(tài)窒所。
只有在AOF持久化功能處于關(guān)閉狀態(tài),服務(wù)器才會(huì)使用RDB文件來(lái)還原數(shù)據(jù)庫(kù)狀態(tài)帆锋。
RDB文件自動(dòng)間隔性保存
用戶可以通過(guò)save選項(xiàng)設(shè)置多個(gè)保存條件吵取,但只要其中任意-一個(gè)條件被滿足,服務(wù)器用戶可以
通過(guò)save選項(xiàng)設(shè)置多個(gè)保存條件锯厢,但只要其中任意-一個(gè)條件被滿足皮官,服務(wù)器就會(huì)執(zhí)行BGSAVE命令。
舉個(gè)例子实辑,如果我們向服務(wù)器提供以下配置:
save 900 1
save 300 10
save 60 10000
那么只要滿足以下三個(gè)條件中的任意-一個(gè)捺氢,BGSAVE命令就會(huì)被執(zhí)行:
服務(wù)器在900秒之內(nèi),對(duì)數(shù)據(jù)庫(kù)進(jìn)行了至少1次修改徙菠。
服務(wù)器在300秒之內(nèi)讯沈,對(duì)數(shù)據(jù)庫(kù)進(jìn)行了至少10次修改郁岩。
服務(wù)器在60秒之內(nèi)婿奔,對(duì)數(shù)據(jù)庫(kù)進(jìn)行了至少10000次修改。
當(dāng)Redis服務(wù)器啟動(dòng)時(shí)问慎,用戶可以通過(guò)指定配置文件或者傳人啟動(dòng)參數(shù)的方式設(shè)置save選項(xiàng)萍摊,如果用戶沒(méi)有主動(dòng)設(shè)置save選項(xiàng),那么服務(wù)器會(huì)為save選項(xiàng)設(shè)置默認(rèn)條件:
save 900 1
save 300 10
save 60 10000
AOF持久化
除了RDB持久化功能之外如叼,Redis 還提供了AOF ( Append Only File )持久化功能冰木。與
RDB持久化通過(guò)保存數(shù)據(jù)庫(kù)中的鍵值對(duì)來(lái)記錄數(shù)據(jù)庫(kù)狀態(tài)不同,AOF持久化是通過(guò)保存
Redis服務(wù)器所執(zhí)行的寫(xiě)命令來(lái)記錄數(shù)據(jù)庫(kù)狀態(tài)的笼恰,如圖11-1 所示踊沸。
AOF持久化功能的實(shí)現(xiàn)可以分為 命令追加(append),文件寫(xiě)入,文件同步(sync)三個(gè)步驟
1社证、命令追加逼龟,當(dāng)AOF持久化功能打開(kāi)狀態(tài)時(shí),服務(wù)器在執(zhí)行完一個(gè)寫(xiě)命令后追葡,會(huì)以協(xié)議格式將被執(zhí)行的寫(xiě)命令追加到服務(wù)器狀態(tài)的aof_buf緩沖區(qū)的末尾腺律。
2奕短、文件寫(xiě)入。
因?yàn)榉?wù)器在處理文件事件時(shí)可能會(huì)執(zhí)行寫(xiě)命令匀钧,使得- -些內(nèi)容被追加到aof_ buf 緩沖
區(qū)里面,所以在服務(wù)器每次結(jié)束-個(gè)事件循環(huán)之前翎碑,它都會(huì)調(diào)用flushAppendOnlyFile函數(shù),考慮是否需要將aof_ buf緩沖區(qū)中的內(nèi)容寫(xiě)人和保存到AOF文件里面之斯。
flushAppend0nlyFile函數(shù)的行為由服務(wù)器配置的appendfsync選項(xiàng)的值來(lái)決定日杈,各個(gè)不同值產(chǎn)生的行為如表11-1所示。
AOF文件的載入與數(shù)據(jù)還原
因?yàn)锳OF文件里面包含了重建數(shù)據(jù)庫(kù)狀態(tài)所需的所有寫(xiě)命令佑刷,所以服務(wù)器只要讀人并重新執(zhí)行一-遍 AOF文件里面保存的寫(xiě)命令达椰,就可以還原服務(wù)器關(guān)閉之前的數(shù)據(jù)庫(kù)狀態(tài)。
AOF重寫(xiě)
因?yàn)锳OF持久化是通過(guò)保存被執(zhí)行的寫(xiě)命令來(lái)記錄數(shù)據(jù)庫(kù)狀態(tài)的项乒,所以隨著服務(wù)器運(yùn)行時(shí)間的流逝啰劲,AOF文件中的內(nèi)容會(huì)越來(lái)越多,文件的體積也會(huì)越來(lái)越大檀何,如果不加以控制的話蝇裤,體積過(guò)大的AOF文件很可能對(duì)Redis服務(wù)器、甚至整個(gè)宿主計(jì)算機(jī)造成影響频鉴,并且AOF文件的體積越大栓辜,使用AOF文件來(lái)進(jìn)行數(shù)據(jù)還原所需的時(shí)間就越多。
為了解決AOF文件體積膨脹的問(wèn)題垛孔,Redis提供了AOF文件重寫(xiě)(rewrite)功能藕甩。通過(guò)該功能,Redis 服務(wù)器可以創(chuàng)建-一個(gè)新的AOF文件來(lái)替代現(xiàn)有的AOF文件周荐,新舊兩個(gè)AOF文件所保存的數(shù)據(jù)庫(kù)狀態(tài)相同狭莱,但新AOF文件不會(huì)包含任何浪費(fèi)空間的冗余命令,所以新AOF文件的體積通常會(huì)比舊AOF文件的體積要小得多概作。
雖然Redis將生成新AOF文件替換舊AOF文件的功能命名為“AOF文件重寫(xiě)”腋妙,但實(shí)際上,AOF文件重寫(xiě)并不需要對(duì)現(xiàn)有的AOF文件進(jìn)行任何讀取讯榕、分析或者寫(xiě)人操作骤素,這個(gè)功能是通過(guò)讀取服務(wù)器當(dāng)前的數(shù)據(jù)庫(kù)狀態(tài)來(lái)實(shí)現(xiàn)的。
事務(wù)
Redis通過(guò)MULTI
愚屁、EXEC
济竹、WATCH
等命令來(lái)實(shí)現(xiàn)事務(wù)( transaction)功能。事務(wù)提供了一種將多個(gè)命令請(qǐng)求打包霎槐,然后- -次性送浊、 按順序地執(zhí)行多個(gè)命令的機(jī)制,并且在事務(wù)執(zhí)行期間栽燕,服務(wù)器不會(huì)中斷事務(wù)而改去執(zhí)行其他客戶端的命令請(qǐng)求罕袋,它會(huì)將事務(wù)中的所有命令都放人事務(wù)當(dāng)中改淑,最后由EXEC命令將這個(gè)事務(wù)提交( commit)給服務(wù)器執(zhí)行:
命令入隊(duì)
當(dāng)一個(gè)客戶端處于非事務(wù)狀態(tài)時(shí),這個(gè)客戶端發(fā)送的命令會(huì)立即被服務(wù)器執(zhí)行
與此不同的是浴讯,當(dāng)一個(gè)客戶端切換到事務(wù)狀態(tài)后朵夏,服務(wù)器會(huì)根據(jù)這個(gè)客戶端的命令執(zhí)行不同的操作:
當(dāng)一個(gè)處于事務(wù)狀態(tài)的客戶端向服務(wù)器發(fā)送EXEC命令時(shí),這個(gè)EXEC命令將立即被服務(wù)器執(zhí)行。服務(wù)器會(huì)遍歷這個(gè)客戶端的事務(wù)隊(duì)列榆纽,執(zhí)行隊(duì)列中保存的所有命令仰猖,最后將執(zhí)行命令所得的結(jié)果全部返回給客戶端。
WATCH命令的實(shí)現(xiàn)
WATCH命令是- - 個(gè)樂(lè)觀鎖
( optimistic locking),它可以在EXEC命令執(zhí)行之前奈籽,監(jiān)視任意數(shù)量的數(shù)據(jù)庫(kù)鍵饥侵,并在EXEC命令執(zhí)行時(shí),檢查被監(jiān)視的鍵是否至少有-一個(gè)已經(jīng)被修改衣屏,如果是的話躏升,服務(wù)器將拒絕執(zhí)行事務(wù),并向客戶端返回代表事務(wù)執(zhí)行失敗的空回復(fù)狼忱。
watch命令原理
每個(gè)Redis數(shù)據(jù)庫(kù)都保存著- -個(gè)watched_ keys 字典膨疏,這個(gè)字典的鍵是某個(gè)被WATCH命令監(jiān)視的數(shù)據(jù)庫(kù)鍵,而字典的值則是一個(gè)鏈表钻弄, 鏈表中記錄了所有監(jiān)視相應(yīng)數(shù)據(jù)庫(kù)鍵的客戶端:
通過(guò)watched_keys字典佃却,服務(wù)器可以清楚地知道哪些數(shù)據(jù)庫(kù)鍵正在被監(jiān)視,以及哪
些客窘俺,戶端正在監(jiān)視這些數(shù)據(jù)庫(kù)鍵饲帅。