內(nèi)存管理
Redis是一個(gè)基于內(nèi)存的key-value的數(shù)據(jù)庫(kù),其內(nèi)存管理是非常重要的;其針對(duì)不同操作系統(tǒng)的差異杖玲,同時(shí)方便自己實(shí)現(xiàn)相關(guān)的統(tǒng)計(jì)函數(shù),封裝了不同平臺(tái)的實(shí)現(xiàn)淘正,具體可參閱深入redis內(nèi)部--內(nèi)存管理摆马;
Redis支持多種不同的數(shù)據(jù)類型,如下:
- String
- List
- Set
- Hash
- Sorted Set
下面就詳細(xì)來(lái)介紹下其數(shù)據(jù)結(jié)構(gòu)和編碼鸿吆。
數(shù)據(jù)結(jié)構(gòu)
所有的Redis對(duì)象都被封裝在RedisObject這個(gè)結(jié)構(gòu)體當(dāng)中囤采,如下:
typedef struct redisObject {
unsigned type, // 4字節(jié),數(shù)據(jù)類型(String,List,Set,Hash,Sorted Set)
unsigned encoding, // 4字節(jié)惩淳,編碼方式
unsigned lru, // 24字節(jié)
int refcount, // 對(duì)象引用計(jì)數(shù)
void *ptr // 數(shù)據(jù)具體存儲(chǔ)的指向
} robj;
數(shù)據(jù)類型的常用編碼方式如下:
數(shù)據(jù)類型 | 常用 | 少量數(shù)據(jù) | 特殊情況 | 讀 | 寫 |
---|---|---|---|---|---|
String | RAW | EMBSTR | INT | O(1) | O(1) |
List | LinkedList | ZipList | pop:O(1) lset:O(N) |
push:O(1) lindex:O(N) |
|
Set | Hash Table | INTSET(少量整數(shù)) | O(1) | O(1) | |
Hash | Hash Table | ZipList | O(1) | O(1) | |
Sorted Set | SkipList | ZipList | zscore:O(1) zrank:O(logN) |
O(logN) |
編碼方式介紹:
- RAW:RedisObject的ptr指向名為sds的空間斑唬,包含Len和Free頭部和buf的實(shí)際數(shù)據(jù),F(xiàn)ree采用了某種預(yù)分配(若Len<1M黎泣,則Free分配與Len大小一致的空間恕刘;若大于Len>=1M,則Free分配1M空間抒倚;SDS的長(zhǎng)度為L(zhǎng)en+Free+buf+1(額外的1字節(jié)用于保存空字符))
- EMBSTR:與RedisObject在連續(xù)的一塊內(nèi)存空間褐着,省去了多次內(nèi)存分配;條件是字符串長(zhǎng)度<=39
- INT:字符串的特殊編碼方式托呕,若存儲(chǔ)的字符串是整數(shù)時(shí)含蓉,則ptr本身會(huì)等于該整數(shù),省去了sds的空間開銷项郊;實(shí)際上Redis在啟動(dòng)時(shí)會(huì)默認(rèn)創(chuàng)建10000個(gè)RedisObject馅扣,代表0-10000的整數(shù)
- ZipList(壓縮列表):除了一些標(biāo)志性字段外用一塊類似數(shù)組的連續(xù)空間來(lái)進(jìn)行存儲(chǔ),缺點(diǎn)是讀寫時(shí)整個(gè)壓縮列表都需要更改着降,一般能達(dá)到10倍的壓縮比差油。Hash默認(rèn)值為512,List默認(rèn)是64
- Hash Table:默認(rèn)初始大小為4,使用鏈地址法解決hash沖突蓄喇;rehash策略:將原來(lái)表中的數(shù)據(jù)rehash并放入新表发侵,之后替換;大量rehash可能會(huì)造成服務(wù)不可用妆偏,因此Redis使用漸進(jìn)式rehash策略刃鳄,分批進(jìn)行
過(guò)期機(jī)制
Redis為了不影響正常的讀寫操作,一般只會(huì)在必要或CPU空閑的時(shí)候做過(guò)期清理的動(dòng)作钱骂;
- 必要:一次事件循環(huán)結(jié)束叔锐,進(jìn)入事件偵聽前
- CPU空閑:系統(tǒng)空閑時(shí)做后臺(tái)定時(shí)清理任務(wù)(時(shí)間限制為25%的CPU時(shí)間);Redis后臺(tái)清理任務(wù)默認(rèn)100ms執(zhí)行1次见秽,25%限制是表示25ms用來(lái)執(zhí)行key清理
過(guò)期key清理算法:
- 依次遍歷所有db掌腰;
- 從db中隨機(jī)取得20個(gè)key,判斷是否過(guò)期张吉,若過(guò)期,則剔除催植;
- 若有5個(gè)以上的key的過(guò)期肮蛹,則重復(fù)步驟2,否則遍歷下一個(gè)db
- 清理過(guò)程中若達(dá)到了時(shí)間限制创南,則退出清理過(guò)程
持久化
Redis支持四種持久化方式伦忠;如下:
- 定時(shí)快照方式(snapshot)[RDB方式]
- 基于語(yǔ)句追加文件的方式(aof)
- 虛擬內(nèi)存(vm) <font color="red">已被放棄</font>
- Diskstore方式<font color="red">實(shí)驗(yàn)階段</font>
前兩種方式為小數(shù)據(jù)量追加落地方式;后兩種為嘗試存儲(chǔ)數(shù)據(jù)超過(guò)物理內(nèi)存時(shí)稿辙,一次性落地方式昆码;
定時(shí)快照方式(snapshot)
該方式實(shí)際是在Redis內(nèi)部執(zhí)行一個(gè)定時(shí)任務(wù),根據(jù)redis.conf中配置的save的時(shí)間間隔去檢查當(dāng)前數(shù)據(jù)改變次數(shù)和時(shí)間是否滿足配置邻储,如果滿足則從父進(jìn)程fork(copy-on-write機(jī)制)出一個(gè)子進(jìn)程赋咽,通過(guò)該子進(jìn)程遍歷內(nèi)存來(lái)轉(zhuǎn)換成rdb文件;
# Save the DB on disk:
# 設(shè)置sedis進(jìn)行數(shù)據(jù)庫(kù)鏡像的頻率吨娜。
# 900秒(15分鐘)內(nèi)至少1個(gè)key值改變(則進(jìn)行數(shù)據(jù)庫(kù)保存--持久化)脓匿。
# 300秒(5分鐘)內(nèi)至少10個(gè)key值改變(則進(jìn)行數(shù)據(jù)庫(kù)保存--持久化)。
# 60秒(1分鐘)內(nèi)至少10000個(gè)key值改變(則進(jìn)行數(shù)據(jù)庫(kù)保存--持久化)宦赠。
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
# 在進(jìn)行鏡像備份時(shí),是否進(jìn)行壓縮陪毡。yes:壓縮,但是需要一些cpu的消耗勾扭。no:不壓縮毡琉,需要更多的磁盤空間。
rdbcompression yes
# 一個(gè)CRC64的校驗(yàn)就被放在了文件末尾妙色,當(dāng)存儲(chǔ)或者加載rbd文件的時(shí)候會(huì)有一個(gè)10%左右的性能下降桅滋,為了達(dá)到性能的最大化,你可以關(guān)掉這個(gè)配置項(xiàng)身辨。
rdbchecksum yes
# 快照的文件名
dbfilename dump.rdb
# 存放快照的目錄
dir /var/lib/redis
缺陷:快照只是一段時(shí)間的數(shù)據(jù)的體現(xiàn)虱歪,若發(fā)生宕機(jī)蜂绎,數(shù)據(jù)會(huì)丟失
基于語(yǔ)句追加文件的方式(aof)
類似mysql的binlog方式,每個(gè)數(shù)據(jù)發(fā)生改變都會(huì)追加至一個(gè)log文件中笋鄙;
# 是否開啟AOF师枣,默認(rèn)關(guān)閉(no)
appendonly yes
# 指定 AOF 文件名
appendfilename appendonly.aof
# Redis支持三種不同的刷寫模式:
# appendfsync always #每次收到寫命令就立即強(qiáng)制寫入磁盤,是最有保證的完全的持久化萧落,但速度也是最慢的践美,一般不推薦使用。
appendfsync everysec #每秒鐘強(qiáng)制寫入磁盤一次找岖,在性能和持久化方面做了很好的折中陨倡,是受推薦的方式。
# appendfsync no #完全依賴OS的寫入许布,一般為30秒左右一次兴革,性能最好但是持久化最沒(méi)有保證,不被推薦蜜唾。
缺陷:追加log文件可能過(guò)大杂曲,恢復(fù)慢;實(shí)時(shí)寫log會(huì)影響redis本身性能