《redis 设计与实现》读书笔记
大家好,我是烤鴨:
????《redis 設(shè)計(jì)與實(shí)現(xiàn)》,讀書筆記。
第一部分 數(shù)據(jù)結(jié)構(gòu)與對象
第2章 簡單動(dòng)態(tài)字符串
Redis 使用SDS 作為字符串表示。
O(1) 復(fù)雜度獲取字符串長度。
杜絕緩沖區(qū)溢出。
減少修改字符串長度時(shí)所需的內(nèi)存重分配次數(shù)。
二進(jìn)制安全。
兼容部分C字符串函數(shù)。
第3章 鏈表
每個(gè)鏈表節(jié)點(diǎn)由一個(gè)listNode結(jié)構(gòu)標(biāo)識,每個(gè)節(jié)點(diǎn)都有一個(gè)指向前置接點(diǎn)和后置節(jié)點(diǎn)的指針,實(shí)現(xiàn)是雙端鏈表。
每個(gè)鏈表用list結(jié)構(gòu)標(biāo)識,結(jié)構(gòu)帶有表頭節(jié)點(diǎn)指針、表尾節(jié)點(diǎn)指針,鏈表長度等。
第4章 字典
Redis中的字典使用哈希表作為底層實(shí)現(xiàn),每個(gè)字典表有兩個(gè)哈希表,一個(gè)平時(shí)使用,另一個(gè)僅在進(jìn)行rehash時(shí)使用。
哈希表使用鏈地址法來解決鍵沖突,被分配到同一個(gè)索引上的多個(gè)鍵值對會連接成一個(gè)單向鏈表。
對哈希表進(jìn)行擴(kuò)展或者收縮操作時(shí),程序需要現(xiàn)有哈希表包含的現(xiàn)有鍵值對rehash到新哈希表里面,rehash過程是漸進(jìn)式的。
dict中的rehashidx( -1 表示不在進(jìn)行rehash)
rehash的index = hash & ht[0].size - 1
哈希表擴(kuò)展操作的條件:
- 沒有執(zhí)行BGSAVE或者BGREWRITEAOF命令,哈希表的負(fù)載因子大于等于1。
- 服務(wù)器目前正在執(zhí)行BGSAVE命令或者BGREWRITEAOF命令,并且哈希表的負(fù)載因子(ht[0].used/ht[0].size)大于等于5。
第5章 跳躍表
跳表效率跟平衡樹差不多,實(shí)現(xiàn)更簡單,平均O(logN),最壞O(N)復(fù)雜度處理節(jié)點(diǎn)。
每次創(chuàng)建新跳表節(jié)點(diǎn),隨機(jī)生成1-32之間的一個(gè)數(shù)作為層高。
Redis的跳躍表實(shí)現(xiàn)由zskiplist和zskiplistNode兩個(gè)結(jié)構(gòu)組成,其中zskiplist用于保存跳躍表信息(比如表頭節(jié)點(diǎn)、表尾節(jié)點(diǎn)、長度),而
zskiplistNode則用于表示跳躍表節(jié)點(diǎn)。
跳躍表中的節(jié)點(diǎn)按照分值大小進(jìn)行排序,當(dāng)分值相同時(shí),節(jié)點(diǎn)按照成員對象的大小進(jìn)行排序。
第6章 整數(shù)集合
整數(shù)集合(intset)是Redis用于保存整數(shù)值的集合抽象數(shù)據(jù)結(jié)構(gòu),它可以保存類型為int16_t、int32_t或者int64_t的整數(shù)值,并且保證
集合中不會出現(xiàn)重復(fù)元素,底層實(shí)現(xiàn)是數(shù)組。
新元素添加到整數(shù)集合里面,并且新元素的類型比整數(shù)集合現(xiàn)有所有元素的類型都要長時(shí),整數(shù)集合需要先進(jìn)行升級。
不支持降級。
第7章 壓縮列表
壓縮列表(ziplist)是列表鍵和哈希鍵的底層實(shí)現(xiàn)之一。
壓縮列表是Redis為了節(jié)約內(nèi)存而開發(fā)的,是由一系列特殊編碼的連續(xù)內(nèi)存塊組成的順序型(sequential)數(shù)據(jù)結(jié)構(gòu)。一個(gè)壓縮列表可
以包含任意多個(gè)節(jié)點(diǎn)(entry),每個(gè)節(jié)點(diǎn)可以保存一個(gè)字節(jié)數(shù)組或者一個(gè)整數(shù)值。
ziplistPush等命令的平均復(fù)雜度僅為O(N),最壞復(fù)雜度為O(N2)。
第8章 對象
Redis使用對象來表示數(shù)據(jù)庫中的鍵和值,叫做鍵對象和值對象。
類型有 字符串、列表、哈希、集合、有序集合。
字符串編碼:int、raw或者embstr。
- int編碼使用整數(shù)集合作為底層實(shí)現(xiàn)。
- 釋放embstr編碼的字符串對象只需要調(diào)用一次內(nèi)存釋放函數(shù),而釋放raw編碼的字符串對象需要調(diào)用兩次內(nèi)存釋放函數(shù)。
列表的編碼:ziplist或者linkedlist。
- ziplist編碼使用壓縮列表作為底層實(shí)現(xiàn),每個(gè)壓縮列表節(jié)點(diǎn)(entry)保存了一個(gè)列表元素
- linkedlist編碼使用雙端鏈表作為底層實(shí)現(xiàn)。
哈希的編碼:ziplist或者h(yuǎn)ashtable(使用字典作為底層實(shí)現(xiàn))。
集合的編碼:intset或者h(yuǎn)ashtable。
有序集合的編碼:ziplist或者skiplist。有序集合每個(gè)元素的成員都是一個(gè)字符串對象,而每個(gè)元素的分值都是一個(gè)double類型的浮點(diǎn)數(shù)。
相同對象類型各個(gè)編碼之間會轉(zhuǎn)換,字符串對象是Redis五種類型的對象中唯一一種會被其他四種類型對象嵌套的對象。
服務(wù)器在執(zhí)行某些命令之前,會先檢查給定鍵的類型能否執(zhí)行指定的命令,而檢查一個(gè)鍵的類型就是檢查鍵的值對象的類型。
Redis的對象系統(tǒng)帶有引用計(jì)數(shù)實(shí)現(xiàn)的內(nèi)存回收機(jī)制,當(dāng)一個(gè)對象不再被使用時(shí),該對象所占用的內(nèi)存就會被自動(dòng)釋放。
Redis會共享值為0到9999的字符串對象。
對象會記錄自己的最后一次被訪問的時(shí)間,這個(gè)時(shí)間可以用于計(jì)算對象的空轉(zhuǎn)時(shí)間。(LRU的實(shí)現(xiàn))
第二部分
第9章 數(shù)據(jù)庫
redis默認(rèn)創(chuàng)建16個(gè)數(shù)據(jù)庫,客戶端默認(rèn)是0號數(shù)據(jù)庫。
數(shù)據(jù)庫主要由dict和expires兩個(gè)字典構(gòu)成,其中dict字典負(fù)責(zé)保存鍵值對,而expires字典則負(fù)責(zé)保存鍵的過期時(shí)間。
Redis使用惰性刪除和定期刪除兩種策略來刪除過期的鍵:惰性刪除策略只在碰到過期鍵時(shí)才進(jìn)行刪除操作,定期刪除策略則每隔一段
時(shí)間主動(dòng)查找并刪除過期鍵。
執(zhí)行SAVE命令或者BGSAVE命令所產(chǎn)生的新RDB文件和執(zhí)行BGREWRITEAOF命令所產(chǎn)生的重寫AOF文件不會包含已經(jīng)過期的鍵。
當(dāng)一個(gè)過期鍵被刪除之后,服務(wù)器會追加一條DEL命令到現(xiàn)有AOF文件的末尾,顯式地刪除過期鍵。
當(dāng)主服務(wù)器刪除一個(gè)過期鍵之后,它會向所有從服務(wù)器發(fā)送一條DEL命令,顯式地刪除過期鍵。
從服務(wù)器即使發(fā)現(xiàn)過期鍵也不會自作主張地刪除它,而是等待主節(jié)點(diǎn)發(fā)來DEL命令,這種統(tǒng)一、中心化的過期鍵刪除策略可以保證主從服務(wù)器數(shù)據(jù)的一致性。
當(dāng)Redis命令對數(shù)據(jù)庫進(jìn)行修改之后,服務(wù)器會根據(jù)配置向客戶端發(fā)送數(shù)據(jù)庫通知。
第10章 RDB持久化
RDB持久化功能所生成的RDB文件是一個(gè)經(jīng)過壓縮的二進(jìn)制文件,通過該文件可以還原生成RDB文件時(shí)的數(shù)據(jù)庫狀態(tài)。
SAVE命令由服務(wù)器進(jìn)程直接執(zhí)行保存操作,所以該命令會阻塞服務(wù)器,BGSAVE令由子進(jìn)程執(zhí)行保存操作,所以該命令不會阻塞服務(wù)器。
服務(wù)器狀態(tài)中會保存所有用save選項(xiàng)設(shè)置的保存條件,當(dāng)任意一個(gè)保存條件被滿足時(shí),服務(wù)器會自動(dòng)執(zhí)行BGSAVE命令。
第11章 AOF持久化
AOF文件通過保存所有修改數(shù)據(jù)庫的寫命令請求來記錄服務(wù)器的數(shù)據(jù)庫狀態(tài)。
命令請求會先保存到AOF緩沖區(qū)里面,之后再定期寫入并同步到AOF文件。只要載入并重新執(zhí)行保存在AOF文件中的命令,就可以還原數(shù)據(jù)庫本來的狀態(tài)。
AOF持久化的效率和安全性:
- 當(dāng)appendfsync的值為always時(shí),效率最慢最安全,宕機(jī)只會丟失一個(gè)事件循環(huán)中所產(chǎn)生的命令數(shù)據(jù)。
- 當(dāng)appendfsync的值為everysec時(shí),宕機(jī)只會丟失一秒中所產(chǎn)生的命令數(shù)據(jù)。
- 當(dāng)appendfsync的值為no時(shí),效率最高最不安全,同步由操作系統(tǒng)控制,宕機(jī)會丟失上次同步之后所產(chǎn)生的命令數(shù)據(jù)。
在執(zhí)行BGREWRITEAOF命令時(shí),Redis服務(wù)器會維護(hù)一個(gè)AOF重寫緩沖區(qū),該緩沖區(qū)會在子進(jìn)程創(chuàng)建新AOF文件期間,記錄服務(wù)器執(zhí)行的所有寫命令。當(dāng)子進(jìn)程完成創(chuàng)建新AOF文件的工作之后,服務(wù)器會將重寫緩沖區(qū)中的所有內(nèi)容追加到新AOF文件的末尾,使得新舊兩個(gè)AOF文件所保存的數(shù)據(jù)庫狀態(tài)一致。最后,服務(wù)器用新的AOF文件替換舊的AOF文件,以此來完成AOF文件重寫操作。
第12章 事件
Redis服務(wù)器是一個(gè)事件驅(qū)動(dòng)程序,服務(wù)器處理的事件分為時(shí)間事件和文件事件兩類。雖然文件事件處理器以單線程方式運(yùn)行,但通過使用I/O多路復(fù)用程序來監(jiān)聽多個(gè)套接字。
文件事件處理器基于Reactor模式實(shí)現(xiàn)的網(wǎng)絡(luò)通信程序,由四個(gè)組成部分,它們分別是套接字、I/O多路復(fù)用程序、文件事件分派器(dispatcher),以及事件處理器。
文件事件分為AE_READABLE事件(讀事件)和AE_WRITABLE事件(寫事件)兩類。
時(shí)間事件分為定時(shí)事件和周期性事件:定時(shí)事件只在指定的時(shí)間到達(dá)一次,而周期性事件則每隔一段時(shí)間到達(dá)一次。
文件事件和時(shí)間事件之間是合作關(guān)系,服務(wù)器會輪流處理這兩種事件,并且處理事件的過程中也不會進(jìn)行搶占。(對文件事件和時(shí)間事件的處理都是同步、有序、原子地執(zhí)行的,服務(wù)器不會中途中斷事件處理,也不會對事件進(jìn)行搶占,因此,不管是文件事件的處理器,還是時(shí)間事件的處理器,它們都會盡可地減少程序的阻塞時(shí)間,并在有需要時(shí)主動(dòng)讓出執(zhí)行權(quán),從而降低造成事件饑餓的可能性。比如說,在命令回復(fù)處理器將一個(gè)命令回復(fù)寫入到客戶端套接字時(shí),如果寫入字節(jié)數(shù)超過了一個(gè)預(yù)設(shè)常量的話,命令回復(fù)處理器就會主動(dòng)用break跳出寫入循環(huán),將余下的數(shù)據(jù)留到下次再寫;另外,時(shí)間事件也會將非常耗時(shí)的持久化操作放到子線程或者子進(jìn)程執(zhí)行。)
第13章 客戶端
服務(wù)器狀態(tài)結(jié)構(gòu)使用clients鏈表連接起多個(gè)客戶端狀態(tài),新添加的客戶端狀態(tài)會被放到鏈表的末尾。
輸入緩沖區(qū)記錄了客戶端發(fā)送的命令請求,這個(gè)緩沖區(qū)的大小不能超過1GB。
命令的參數(shù)和參數(shù)個(gè)數(shù)會被記錄在客戶端狀態(tài)的argv和argc屬性里面,而cmd屬性則記錄了客戶端要執(zhí)行命令的實(shí)現(xiàn)函數(shù)。
客戶端的輸出緩沖區(qū)有固定大小緩沖區(qū)和可變大小緩沖區(qū)兩種緩沖區(qū)可用,其中固定大小緩沖區(qū)的最大大小為16KB,而可變大小緩沖區(qū)的最大大小不能超過服務(wù)器設(shè)置的硬性限制值。
當(dāng)一個(gè)客戶端通過網(wǎng)絡(luò)連接連上服務(wù)器時(shí),服務(wù)器會為這個(gè)客戶端創(chuàng)建相應(yīng)的客戶端狀態(tài)。網(wǎng)絡(luò)連接關(guān)閉、發(fā)送了不合協(xié)議格式的命令請求、成為CLIENT KILL命令的目標(biāo)、空轉(zhuǎn)時(shí)間超時(shí)、輸出緩沖區(qū)的大小超出限制(硬性限制和軟性限制),以上這些原因都會造成客戶端被關(guān)閉。
處理Lua腳本的偽客戶端在服務(wù)器初始化時(shí)創(chuàng)建,這個(gè)客戶端會一直存在,直到服務(wù)器關(guān)閉。
載入AOF文件時(shí)使用的偽客戶端在載入工作開始時(shí)動(dòng)態(tài)創(chuàng)建,載入工作完畢之后關(guān)閉。
第14章 服務(wù)器
一個(gè)命令請求從發(fā)送到完成主要包括以下步驟:1)客戶端將命令請求發(fā)送給服務(wù)器;2)服務(wù)器讀取命令請求,并分析出命令參數(shù);3)命令執(zhí)行器根據(jù)參數(shù)查找命令的實(shí)現(xiàn)函數(shù),然后執(zhí)行實(shí)現(xiàn)函數(shù)并得出命令回復(fù);4)服務(wù)器將命令回復(fù)返回給客戶端。
serverCron函數(shù)默認(rèn)每隔100毫秒執(zhí)行一次,它的工作主要包括更新服務(wù)器狀態(tài)信息,處理服務(wù)器接收的SIGTERM信號,管理客戶端資源和數(shù)據(jù)庫狀態(tài),檢查并執(zhí)行持久化操作等等。
服務(wù)器從啟動(dòng)到能夠處理客戶端的命令請求需要執(zhí)行以下步驟:1)初始化服務(wù)器狀態(tài);2)載入服務(wù)器配置;3)初始化服務(wù)器數(shù)據(jù)結(jié)構(gòu);4)還原數(shù)據(jù)庫狀態(tài);5)執(zhí)行事件循環(huán)。
第三部分
第15章 復(fù)制
部分重同步可以解決斷線重新同步的問題,是通過復(fù)制偏移量、復(fù)制積壓緩沖區(qū)、服務(wù)器運(yùn)行ID三個(gè)部分來實(shí)現(xiàn)。
在復(fù)制操作剛開始的時(shí)候,從服務(wù)器會成為主服務(wù)器的客戶端,并通過向主服務(wù)器發(fā)送命令請求來執(zhí)行復(fù)制步驟,而在復(fù)制操作的后期,主從服務(wù)器會互相成為對方的客戶端。
主服務(wù)器通過向從服務(wù)器傳播命令來更新從服務(wù)器的狀態(tài),保持主從服務(wù)器一致,而從服務(wù)器則通過向主服務(wù)器發(fā)送命令來進(jìn)行心跳檢測,以及命令丟失檢測。
第16章 Sentinel
Sentinel哨兵模式,高可用的解決方案之一,由一個(gè)或多個(gè)Sentinel實(shí)例(instance)組成的Sentinel系統(tǒng)(system)可以監(jiān)視任意多個(gè)主從服務(wù)器。在主節(jié)點(diǎn)下線情況下,自動(dòng)將從節(jié)點(diǎn)升級。
Sentinel通過向主服務(wù)器發(fā)送INFO命令來獲得主服務(wù)器屬下所有從服務(wù)器的地址信息,并為這些從服務(wù)器創(chuàng)建相應(yīng)的實(shí)例結(jié)構(gòu),以及連向這些從服務(wù)器的命令連接和訂閱連接。
Sentinel默認(rèn)以每十秒一次的頻率向被監(jiān)視的主服務(wù)器和從服務(wù)器發(fā)送INFO命令,當(dāng)主服務(wù)器處于下線狀態(tài),或者Sentinel正在對主服務(wù)器進(jìn)行故障轉(zhuǎn)移操作時(shí),Sentinel向從服務(wù)器發(fā)送INFO命令的頻率會改為每秒一次。
Sentinel只會與主服務(wù)器和從服務(wù)器創(chuàng)建命令連接和訂閱連接,Sentinel與Sentinel之間則只創(chuàng)建命令連接。
Sentinel以每秒一次的頻率向?qū)嵗l(fā)送ping做心跳檢測。
當(dāng)Sentinel將一個(gè)主服務(wù)器判斷為主觀下線時(shí),它會向同樣監(jiān)視這個(gè)主服務(wù)器的其他Sentinel進(jìn)行詢問,看它們是否同意這個(gè)主服務(wù)器已經(jīng)進(jìn)入主觀下線狀態(tài)。
當(dāng)Sentinel收集到足夠多的主觀下線投票之后,它會將主服務(wù)器判斷為客觀下線,并發(fā)起一次針對主服務(wù)器的故障轉(zhuǎn)移操作。
第17章 集群
Redis集群是Redis提供的分布式數(shù)據(jù)庫方案,集群通過分片(sharding)來進(jìn)行數(shù)據(jù)共享,并提供復(fù)制和故障轉(zhuǎn)移功能。
節(jié)點(diǎn)通過握手來將其他節(jié)點(diǎn)添加到自己所處的集群當(dāng)中。
集群中的16384個(gè)槽可以分別指派給集群中的各個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)都會記錄哪些槽指派給了自己,而哪些槽又被指派給了其他節(jié)點(diǎn)。
CRC16(key)語句用于計(jì)算鍵key的CRC-16校驗(yàn)和,而&16383語句則用于計(jì)算出一個(gè)介于0至16383之間的整數(shù)作為鍵key的槽號。
def slot_number(key):return CRC16(key) & 16383節(jié)點(diǎn)在接到一個(gè)命令請求時(shí),會先檢查這個(gè)命令請求要處理的鍵所在的槽是否由自己負(fù)責(zé),如果不是的話,節(jié)點(diǎn)將向客戶端返回一個(gè)MOVED錯(cuò)誤,MOVED錯(cuò)誤攜帶的信息可以指引客戶端轉(zhuǎn)向至正在負(fù)責(zé)相關(guān)槽的節(jié)點(diǎn)。
重新分片由redis-trib或者redis-cli負(fù)責(zé)執(zhí)行。
# 5.0 之前 redis-trib.rb create --replicas # 5.0 之后 redis-cli -a redis-pw --cluster create --cluster-replicass遷移過程中鍵被查找,返回 ASK 指引到相關(guān)節(jié)點(diǎn)。
集群里的從節(jié)點(diǎn)用于復(fù)制主節(jié)點(diǎn),并在主節(jié)點(diǎn)下線時(shí),代替主節(jié)點(diǎn)繼續(xù)處理命令請求。
集群中的節(jié)點(diǎn)通過發(fā)送和接收消息來進(jìn)行通信,常見的消息包括MEET、PING、PONG、PUBLISH、FAIL五種。
第四部分
第18章 發(fā)布與訂閱
頻道訂閱和模式訂閱:
-
服務(wù)器狀態(tài)在pubsub_channels字典保存了所有頻道的訂閱關(guān)系:SUBSCRIBE命令負(fù)責(zé)將客戶端和被訂閱的頻道關(guān)聯(lián)到這個(gè)字典里面,而UNSUBSCRIBE命令則負(fù)責(zé)解除客戶端和被退訂頻道之間的關(guān)聯(lián)。
-
服務(wù)器狀態(tài)在pubsub_patterns鏈表保存了所有模式的訂閱關(guān)系:PSUBSCRIBE命令負(fù)責(zé)將客戶端和被訂閱的模式記錄到這個(gè)鏈表中,而PUNSUBSCRIBE命令則負(fù)責(zé)移除客戶端和被退訂模式在鏈表中的記錄。
PUBLISH命令通過訪問pubsub_channels字典來向頻道的所有訂閱者發(fā)送消息,通過訪問pubsub_patterns鏈表來向所有匹配頻道的模式的訂閱者發(fā)送消息。
PUBSUB命令的三個(gè)子命令都是通過讀取pubsub_channels字典和pubsub_patterns鏈表中的信息來實(shí)現(xiàn)的。
ps:一般流量不大的可以使用redis的發(fā)布訂閱做IM,比如聊天室。
第19章 事務(wù)
redis的事務(wù)是提供打包執(zhí)行命令,先進(jìn)先出(FIFO)順序執(zhí)行。
事務(wù)在執(zhí)行過程中不會被中斷,當(dāng)事務(wù)隊(duì)列中的所有命令都被執(zhí)行完畢之后,事務(wù)才會結(jié)束。
帶有WATCH命令的事務(wù)會將客戶端和被監(jiān)視的鍵在數(shù)據(jù)庫的watched_keys字典中進(jìn)行關(guān)聯(lián),當(dāng)鍵被修改時(shí),程序會將所有監(jiān)視被修改鍵的客戶端的REDIS_DIRTY_CAS標(biāo)志打開,此時(shí)服務(wù)器將拒絕執(zhí)行客戶端提交的事務(wù)(樂觀鎖CAS的方式)。
Redis的事務(wù)總是具有ACID中的原子性、一致性和隔離性,當(dāng)服務(wù)器運(yùn)行在AOF持久化模式下,并且appendfsync選項(xiàng)的值為always時(shí),事務(wù)也具有耐久性。(如果其中某一條命令出錯(cuò),其他命令會繼續(xù)執(zhí)行,這個(gè)跟DB有點(diǎn)差異,redis作者認(rèn)為這種場景生產(chǎn)環(huán)境很少出現(xiàn)且和redis高效的設(shè)計(jì)理念不相符,所以沒有回滾功能)
第20章 Lua腳本
Redis服務(wù)器在啟動(dòng)時(shí),會對內(nèi)嵌的Lua環(huán)境,使用一個(gè)偽客戶端來執(zhí)行Lua腳本中包含的Redis命令。
命令相關(guān):
-
EVAL:為客戶端輸入的腳本在Lua環(huán)境中定義一個(gè)函數(shù),并通過調(diào)用這個(gè)函數(shù)來執(zhí)行腳本。
-
EVALSHA:通過直接調(diào)用Lua環(huán)境中已定義的函數(shù)來執(zhí)行腳本。
-
SCRIPT FLUSH:會清空服務(wù)器lua_scripts字典中保存的腳本,并重置Lua環(huán)境。
-
SCRIPT EXISTS:接受一個(gè)或多個(gè)SHA1校驗(yàn)和為參數(shù),并通過檢查lua_scripts字典來確認(rèn)校驗(yàn)和對應(yīng)的腳本是否存在。
-
SCRIPT LOAD:接受一個(gè)Lua腳本為參數(shù),為該腳本在Lua環(huán)境中創(chuàng)建函數(shù),并將腳本保存到lua_scripts字典中。
服務(wù)器在執(zhí)行腳本之前,會為Lua環(huán)境設(shè)置一個(gè)超時(shí)處理鉤子,當(dāng)腳本出現(xiàn)超時(shí)運(yùn)行情況時(shí),客戶端可以通過向服務(wù)器發(fā)送SCRIPT KILL命令來讓鉤子停止正在執(zhí)行的腳本,或者發(fā)送SHUTDOWN nosave命令來讓鉤子關(guān)閉整個(gè)服務(wù)器。
主服務(wù)器在復(fù)制EVALSHA命令時(shí),如果從服務(wù)器返回腳本未找到,主服務(wù)器會將EVALSHA命令轉(zhuǎn)換成等效的EVAL命令,并通過傳播EVAL命令來獲得相同的腳本執(zhí)行效果。
第21章 排序
Redis的SORT命令可以對列表鍵、集合鍵或者有序集合鍵的值進(jìn)行排序。通過將被排序鍵包含的元素載入到數(shù)組里面,然后對數(shù)組進(jìn)行排序來完成對鍵進(jìn)行排序的工作。
在默認(rèn)情況下,排序鍵是數(shù)字值,如果使用了ALPHA選項(xiàng),排序鍵是字符串。SORT命令的排序操作由快速排序算法實(shí)現(xiàn)。默認(rèn)升序,DESC降序。
redis> RPUSH numbers 3 2 1 4 5 redis> SORT numbers DESC當(dāng)SORT命令使用了BY選項(xiàng)時(shí),命令使用其他鍵的值作為權(quán)重來進(jìn)行排序操作。
redis> SADD fruits "apple" "banana" "cherry" (integer) 3 redis> SORT fruits ALPHA 1) "apple" 2) "banana" 3) "cherry"當(dāng)SORT命令使用了LIMIT選項(xiàng)時(shí),命令只保留排序結(jié)果集中LIMIT選項(xiàng)指定的元素。
redis> SADD alphabet a b c d e f (integer) 6 #集合中的元素是亂序存放的 redis> SMEMBERS alphabet 1) "d" 2) "c" 3) "a" 4) "b" 5) "f" 6) "e" #對集合進(jìn)行排序,并返回所有排序后的limit元素 redis> SORT alphabet ALPHA LIMIT 0,4 1) "a" 2) "b" 3) "c" 4) "d"當(dāng)SORT命令使用了GET選項(xiàng)時(shí),命令會根據(jù)排序結(jié)果集中的元素,以及GET選項(xiàng)給定的模式,查找并返回其他鍵的值,而不是返回被排序的元素。
#設(shè)置peter、jack、tom的全名 redis> SET peter-name "Peter White" OK redis> SET jack-name "Jack Snow" OK redis> SET tom-name "Tom Smith" OK # SORT命令首先對students集合進(jìn)行排序,得到排序結(jié)果 # 1) "jack" # 2) "peter" # 3) "tom" #然后根據(jù)這些結(jié)果,獲取并返回鍵jack-name、peter-name和tom-name的值 redis> SORT students ALPHA GET *-name 1) "Jack Snow" 2) "Peter White" 3) "Tom Smith"當(dāng)SORT命令使用了STORE選項(xiàng)時(shí),命令會將排序結(jié)果集保存在指定的鍵里面。
當(dāng)SORT命令同時(shí)使用多個(gè)選項(xiàng)時(shí),命令先執(zhí)行排序操作(可用的選項(xiàng)為ALPHA、ASC或DESC、BY),然后執(zhí)行LIMIT選項(xiàng),之后執(zhí)行GET選項(xiàng),再之后執(zhí)行STORE選項(xiàng),最后才將排序結(jié)果集返回給客戶端。除了GET選項(xiàng)之外,調(diào)整選項(xiàng)的擺放位置不會影響SORT命令的排序結(jié)果。
第22章 二進(jìn)制位數(shù)組
Redis提供了SETBIT、GETBIT、BITCOUNT、BITOP四個(gè)命令用于處理二進(jìn)制位數(shù)組(bit array,又稱“位數(shù)組”)。
SDS使用逆序來保存位數(shù)組,這種保存順序簡化了SETBIT命令的實(shí)現(xiàn),使得SETBIT命令可以在不移動(dòng)現(xiàn)有二進(jìn)制位的情況下,對位數(shù)組進(jìn)行空間擴(kuò)展。(順序的話,高位添加,需要移動(dòng)原有的數(shù)組位置)
BITCOUNT命令使用了查表算法和variable-precision SWAR算法來優(yōu)化命令的執(zhí)行效率。(漢明重量)
- 未處理的二進(jìn)制位大于等于128位,variable-precision SWAR算法。反之查表算法。
BITOP命令的所有操作都使用C語言內(nèi)置的位操作來實(shí)現(xiàn)。
第23章 慢查詢?nèi)罩?/h3>
Redis的慢查詢?nèi)罩竟δ苡糜谟涗泩?zhí)行時(shí)間超過指定時(shí)長的命令。
- slowlog-log-slower-than:執(zhí)行時(shí)間超過多少微秒(1秒等于1000 000微秒)的命令請求會被記錄到日志上。(默認(rèn) 10000,10毫秒)
- slowlog-max-len:服務(wù)器最多保存多少條慢查詢?nèi)罩尽?默認(rèn) 128)
Redis服務(wù)器將所有的慢查詢?nèi)罩颈4嬖诜?wù)器狀態(tài)的slowlog鏈表中,每個(gè)鏈表節(jié)點(diǎn)都包含一個(gè)slowlogEntry結(jié)構(gòu),每個(gè)slowlogEntry結(jié)構(gòu)代表一條慢查詢?nèi)罩尽?/p>
新的慢查詢?nèi)罩緯惶砑拥絪lowlog鏈表的表頭,打印和刪除慢查詢?nèi)罩究梢酝ㄟ^遍歷slowlog鏈表來完成。
第24章 監(jiān)視器
monitor 命令,服務(wù)器實(shí)時(shí)監(jiān)控當(dāng)前處理的命令。
redis> MONITOR OK 1378822099.421623 [0 127.0.0.1:56604] "PING" 1378822105.089572 [0 127.0.0.1:56604] "SET" "msg" "hello world"當(dāng)一個(gè)客戶端從普通客戶端變?yōu)楸O(jiān)視器時(shí),該客戶端的REDIS_MONITOR標(biāo)識會被打開。
服務(wù)器將所有監(jiān)視器都記錄在monitors鏈表中。
每次處理命令請求時(shí),服務(wù)器都會遍歷monitors鏈表,將相關(guān)信息發(fā)送給監(jiān)視器。
總結(jié)
以上是生活随笔為你收集整理的《redis 设计与实现》读书笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ConcurrentHashMap底层原
- 下一篇: 千峰python资料下载_千锋Pytho