Redis数据库实现原理(划重点)
Redis服務(wù)器將所有數(shù)據(jù)庫都保存在服務(wù)器狀態(tài)redis.h/redisServer結(jié)構(gòu)的db數(shù)組中,db數(shù)組的每一項都是一個redis.h/redisDb結(jié)構(gòu),每個redisDb結(jié)構(gòu)代表一個數(shù)據(jù)庫,服務(wù)器設(shè)置dbnum屬性為初始數(shù)據(jù)庫的個數(shù),這個屬性一般由數(shù)據(jù)庫服務(wù)器配置conf文件中的database節(jié)點來配置,默認(rèn)情況下這個初始值是16。
struct redisServer{????//數(shù)據(jù)庫????redisDb?*db;????//服務(wù)器數(shù)量????int?dbnum;};數(shù)據(jù)庫切換
前面我們說到默認(rèn)的redis數(shù)據(jù)庫有16個,那么我們?nèi)绾蝸砬袚Q數(shù)據(jù)庫呢?
127.0.0.1:6379> get name"mango"127.0.0.1:6379> select 1OK127.0.0.1:6379[1]> get name(nil)127.0.0.1:6379[1]> select 0OK127.0.0.1:6379> get name"mango"默認(rèn)情況下我們操作的都是第一個數(shù)據(jù)庫,數(shù)據(jù)庫數(shù)組索引為[0],我們通過select命令來切換數(shù)據(jù)庫,通過修改客戶端的指針,來指向第二個數(shù)據(jù)庫。
注意:這里我們值得注意的是,我們在維護(hù)redis的時候執(zhí)行一些敏感的指令時養(yǎng)成一個習(xí)慣就是先切換數(shù)據(jù)庫,然后執(zhí)行。
數(shù)據(jù)庫鍵空間
Redis 中的每個數(shù)據(jù)庫,都由一個 redis.h/redisDb 結(jié)構(gòu)表示,下面我們來看看代碼。
typedef struct redisDb { // 保存著數(shù)據(jù)庫以整數(shù)表示的號碼????int?id; // 保存著數(shù)據(jù)庫中的所有鍵值對數(shù)據(jù),這個屬性也被稱為鍵空間(key space) dict *dict; // 保存著鍵的過期信息 dict *expires;????//?實現(xiàn)列表阻塞原語,如?BLPOP dict *blocking_keys; dict *ready_keys;????//?用于實現(xiàn)?WATCH?命令,在事務(wù)中有提到 dict *watched_keys;} redisDb;因為Redis本身就是一個字典類型的數(shù)據(jù)庫,我們可以看到在RedisDb中,dict保存數(shù)據(jù)庫的所有鍵值對數(shù)據(jù)。
字典的鍵是一個字符串對象(StringObject)
字典的值則可以包括Redis的五種基礎(chǔ)結(jié)構(gòu)的任意一種(字符串、列表、哈希表、集合或有序集),例如:列表就是一個ListObject,哈希就是HashObject等。
如圖所示,每個類型對應(yīng)數(shù)據(jù)結(jié)構(gòu)不同。
關(guān)于Redis數(shù)據(jù)庫的增、刪、改、查操作這里就不過多解釋了,跟java或是c#中的實現(xiàn)是相似的,只不過對于字典擴(kuò)容稍有不同,Redis的擴(kuò)容是一種漸進(jìn)式的方式,一點點的將舊表遷入新表,不會在擴(kuò)容的時候阻塞其他操作。
其他操作
除了增刪該查的鍵值操作之外,還有很多針對數(shù)據(jù)庫本身的命令,也是通過對鍵空間進(jìn)行處理:
? FLUSHDB 命令:刪除鍵空間中的所有鍵值對。
? RANDOMKEY 命令:從鍵空間中隨機(jī)返回一個鍵。
? DBSIZE 命令:返回鍵空間中鍵值對的數(shù)量。
? EXISTS 命令:檢查給定鍵是否存在于鍵空間中。
? RENAME 命令:在鍵空間中,對給定鍵進(jìn)行改名。
過期設(shè)置
數(shù)據(jù)過期操作命令通過EXPIRE、PEXPIRE、EXPIREAT和PEXPIREAT四個命令,客戶端可以給某個存在的鍵設(shè)置過期時間,當(dāng)鍵的過期時間到達(dá)時,鍵就不再可用。當(dāng)然SETEX也是可以的,只不過它是一個限定類型命令(可以說是一個復(fù)合命令),跟其他的過期時間設(shè)置原理一樣。
>set name mango(integer)1>expire?name?5????#設(shè)置過期時間過期時間是一個UNIX時間戳,當(dāng)鍵的過期時間到了,這個鍵就會在數(shù)據(jù)庫中被刪除。那么我們可以通過TTL和PTTL命令來返回距離過期時間還有多長時間
> SETEX key 10086 valueOK> TTL key(integer) 10082> PTTL key(integer) 10068998過期時間保存
前面我們給出的RedisDb里面的一個屬性dict *expires是保存過期時間的,我們可以看到它也是一個dict鍵值的指針類型,其中這個過期字典中的是一個long long類型的整數(shù),這個整數(shù)保存的是過期時間。下面我們來畫一個圖加深印象。
注意:這里的過期時間的字典是指向的鍵空間的,只不過為了區(qū)分才這樣畫的。
如何檢測key是否過期?
檢查這個key是否存在expires中
檢查當(dāng)前時間是否大于過期時間,如果大于則過期,反之未過期
過期key刪除策略
Redis設(shè)置好過期時間后,如果key的過期時間到期,我們的redis沒有及時回收資源可能會導(dǎo)致Redis內(nèi)存溢出,所以我們需要及時清理過期的key來釋放資源。
定時清理:
此方法就是我設(shè)置一個定時器,到點了我就去清理已經(jīng)過期的key。
優(yōu)點:就是方便簡潔,保證key過期及時處理;
缺點:也很明顯,掃描整個字典是一個O(n)的時間復(fù)雜度,使用這種方式的時候會占用大量的cpu可能會導(dǎo)致服務(wù)器卡頓等現(xiàn)象,我們說過Redis盡量避免使用這類的操作,這樣處理并不高效。
惰性清理:
這種方式就是客戶端請求這個key的時候去判斷時間是否過期,過期則清理。
優(yōu)點:對cup很友好,使用的時候去清理
缺點:對內(nèi)存不友好,無效key不及時清理內(nèi)存得不到釋放,積壓的內(nèi)存越來越多,如果過期key特別多而且永遠(yuǎn)不訪問,可能會導(dǎo)致內(nèi)存溢出。
定期清理:
定期刪除是結(jié)合定是清理和惰性清理的特點選擇一個折中處理,一段時間內(nèi)處理一定量的key,這樣減少使用cpu帶來的阻塞,一定程度的減少內(nèi)存積壓。
定期清理的難點在于清理算法,也就是說什么時候清理多少key這個量度是很難把握的。
那么我們介紹了這三種清理方式后,Redis的清理過期key的方式是通過惰性清理和定期清理兩種策略來實現(xiàn)的。通過這兩種方式使Redis在cpu和內(nèi)存之間取得平衡,這兩種方式也是在不同的時期進(jìn)行的。
過期鍵對AOF、RDB和復(fù)制的影響
前面的內(nèi)容討論了過期鍵對cpu和內(nèi)存的影響,那么過期鍵在RDB文件、AOF 文件、AOF重寫以及復(fù)制中的影響。
RDB文件在創(chuàng)建新的RDB文件時,程序會對鍵進(jìn)行檢查,過期的鍵不會被寫入到更新后的RDB文件中,過期鍵對更新后的RDB文件沒有影響。
AOF文件在鍵已經(jīng)過期,但是還沒有被惰性刪除或者定期刪除之前,這個鍵不會產(chǎn)生任何影響,AOF文件也不會因為這個鍵而被修改。當(dāng)過期鍵被惰性刪除、或者定期刪除之后,程序會向AOF文件追加一條DEL命令,來顯式地記錄該鍵已被刪除。
AOF重寫時程序?qū)︽I進(jìn)行檢查,過期的鍵不會被保存到重寫后的AOF文件。過期鍵對重寫后的AOF文件沒有影響。
復(fù)制:當(dāng)服務(wù)器帶有附屬節(jié)點時,過期鍵的刪除由主節(jié)點統(tǒng)一控制:
? ? 如果服務(wù)器是主節(jié)點,那么它在刪除一個過期鍵之后,會顯式地向所有附屬節(jié)點發(fā)送一個DEL命令。
? ? 如果服務(wù)器是附屬節(jié)點,那么當(dāng)它碰到一個過期鍵的時候,它會向程序返回鍵已過期的回復(fù),但并不真正的刪除過期鍵。因為程序只根據(jù)鍵是否已經(jīng)過期、而不是鍵是否已經(jīng)被刪除來決定執(zhí)行流程,所以這種處理并不影響命令的正確執(zhí)行結(jié)果。當(dāng)接到從主節(jié)點發(fā)來的DEL命令之后,附屬節(jié)點才會真正的將過期鍵刪除掉。附屬節(jié)點不自主對鍵進(jìn)行刪除是為了和主節(jié)點的數(shù)據(jù)保持絕對一致,因為這個原因,當(dāng)一個過期鍵還存在于主節(jié)點時,這個鍵在所有附屬節(jié)點的副本也不會被刪除。這種處理機(jī)制對那些使用大量附屬節(jié)點,并且?guī)в写罅窟^期鍵的應(yīng)用來說,可能會造成一部分內(nèi)存不能立即被釋放,但是,因為過期鍵通常很快會被主節(jié)點發(fā)現(xiàn)并刪除
?
總結(jié)一下:
Redis默認(rèn)有16個數(shù)據(jù)庫,但如果沒有切換那么使用的是第一個默認(rèn)的數(shù)據(jù)庫
數(shù)據(jù)庫主要由dict和expires兩個字典構(gòu)成,其中dict保存鍵值對,而expires則保存鍵的過期時間。
數(shù)據(jù)庫的鍵總是一個字符串對象,而值可以是任意一種 Redis 數(shù)據(jù)類型,包括字符串、哈希、集合、列表和有序集。
expires的某個鍵和dict的某個鍵共同指向同一個字符串對象,而expires 鍵的值則是該鍵以毫秒計算的 UNIX 過期時間戳。
Redis使用惰性刪除和定期刪除兩種策略來刪除過期的鍵。
更新后的RDB文件和重寫后的AOF文件都不會保留已經(jīng)過期的鍵。
當(dāng)一個過期鍵被刪除之后,程序會追加一條新的DEL命令到現(xiàn)有AOF文件末尾。
當(dāng)主節(jié)點刪除一個過期鍵之后,它會顯式地發(fā)送一條DEL命令到所有附屬節(jié)點,而從節(jié)點是不會主動刪除某個key,這樣保證主從一致性問題。
?
一名正在搶救的coder
筆名:mangolove
CSDN地址:https://blog.csdn.net/mango_love
GitHub地址:https://github.com/mangoloveYu
?
?
總結(jié)
以上是生活随笔為你收集整理的Redis数据库实现原理(划重点)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DELPHI加密字串(异或运算加密)
- 下一篇: linux cmake编译源码,linu