Redis內(nèi)存模型
Redis內(nèi)存分配
- 數(shù)據(jù) :Redis存儲(chǔ)的數(shù)據(jù)對(duì)象 字符串、哈希、列表、集合、有序集合
- 進(jìn)程本身所需內(nèi)存 : Redis進(jìn)程自己運(yùn)行所需要的內(nèi)存,比如代碼,占用內(nèi)存,常量池等
- 緩存內(nèi)存:
- 客戶端緩沖區(qū) : 連接客戶端輸入輸出的緩存
- 復(fù)制積壓緩沖區(qū): 在主從同步時(shí),非全量復(fù)制時(shí),所需要的緩存區(qū)
- 復(fù)制積壓緩沖區(qū): AOF寫入時(shí)的緩存
- 內(nèi)存碎片:內(nèi)存碎片是Redis在分配、回收物理內(nèi)存過程中產(chǎn)生的
Redis內(nèi)存分配器 (jemalloc)
-
jemalloc 將空間分為 小(Small)、大(Large)、巨大(Huge)三種
image
Redis內(nèi)存統(tǒng)計(jì)
- used_memory (Redis內(nèi)存分配器分配的內(nèi)存)
存儲(chǔ)的數(shù)據(jù)的內(nèi)存
- used_memory_rss (Redis占操作系統(tǒng)的內(nèi)存)
包括存儲(chǔ)的數(shù)據(jù)內(nèi)存還有內(nèi)存碎片以及Redis本身占用內(nèi)存
Redis數(shù)據(jù)的存儲(chǔ)過程
-
RedisObject
RedisObject -
數(shù)據(jù)類型
-
SDS
SDS.jpg- 空間預(yù)分配
sdscat =》給字符串后面再拼接一個(gè)字符串
當(dāng)sdscat 之后內(nèi)存小于 1M,字符串長(zhǎng)度*2+1 (’\0’)
當(dāng)sdscat 之后內(nèi)存大于 1M, 字符串長(zhǎng)度 + 1M + 1(’\0’) - 空間懶分配
如果sdstrim(減少字符串),則不急著回收空間,下次如果需要添加長(zhǎng)度,直接使用多余的空間。
- 空間預(yù)分配
-
List
List.jpg -
Hash
hash.jpg
在字典中存在dictht數(shù)組,表明是兩個(gè)hash表
ht[1]的容量是ht[0]的兩倍
把ht[0]中的元素rehash復(fù)制到ht[1]中 set
zset
-
數(shù)據(jù)存儲(chǔ)過程
RedisObject -> 具體的數(shù)據(jù)類型
Redis內(nèi)存回收策略
- noeviction:返回錯(cuò)誤當(dāng)內(nèi)存限制達(dá)到并且客戶端嘗試執(zhí)行會(huì)讓更多內(nèi)存被使用的命令(大部分的寫入指令,但DEL和幾個(gè)例外)
- allkeys-lru:嘗試回收最少使用的鍵(LRU) ,使得新添加的數(shù)據(jù)有空間存放。
- volatile-lru:嘗試回收最少使用的鍵(LRU) ,但僅限于在過期集合的鍵,使得新添加的數(shù)據(jù)有空間存放。
- allkeys-random:回收隨機(jī)的鍵使得新添加的數(shù)據(jù)有空間存放。
- volatile-random:回收隨機(jī)的鍵使得新添加的數(shù)據(jù)有空間存放,但僅限于在過期集合的鍵。
- volatile-ttl:回收在過期集合的鍵,并且優(yōu)先回收存活時(shí)間(TTL) 較短的鍵,使得新添加的數(shù)據(jù)有空間存放。
Redis的原子性保證
- 單指令原子性
Redis是單線程的,一個(gè)線程只能執(zhí)行一個(gè)指令,因此具有原子性
- Lua原子性
官方解釋來看,Lua腳本和Redis的事務(wù)一樣,被exec/mutl包裹,redis保證每次只能執(zhí)行一個(gè)lua腳本,別的lua腳本不會(huì)被執(zhí)行,由此保證了原子性。
分布式鎖
主要命令
- setnx 不存在key才可以操作
- set 與set相反
鎖續(xù)期
利用Redission,當(dāng)成功獲取一個(gè)鎖的時(shí)候,產(chǎn)生看門狗(watch dog)進(jìn)行鎖續(xù)期,一般來說是10s檢查一次
核心在于Redission使用了Lua腳本
分布式鎖的極端情況
當(dāng)服務(wù)A從Master中獲取鎖,A獲取鎖成功后,還沒來得及同步到從節(jié)點(diǎn),master掛了,從節(jié)點(diǎn)
重新成為master,服務(wù)B過來后,發(fā)現(xiàn)該鎖還未被獲取,于是鎖被重復(fù)獲取
Redis的冷備和熱備
- 熱備 - AOF
- 數(shù)據(jù)文件比RDB更大
- 每秒都去持久化,數(shù)據(jù)丟失少
- 存儲(chǔ)的文件是每條的指令
- 冷備 - RDB
- 需要fork子進(jìn)程,數(shù)據(jù)量大的話會(huì)導(dǎo)致幾秒的延遲,對(duì)于秒殺場(chǎng)景危險(xiǎn)
- 是段時(shí)間保存數(shù)據(jù),一旦發(fā)生宕機(jī),數(shù)據(jù)丟失較多
- RDB恢復(fù)的更快
Redis集群
哨兵監(jiān)控
- 主觀下線
從節(jié)點(diǎn)無(wú)法ping通master,則主觀認(rèn)為master掛了
- 客觀下線(主節(jié)點(diǎn)的下線)
多個(gè)從節(jié)點(diǎn)都無(wú)法ping通master,則從節(jié)點(diǎn)們客觀認(rèn)為master掛了,需要重新選舉
- 定時(shí)任務(wù)
- 錯(cuò)誤轉(zhuǎn)移
- 過濾不健康的節(jié)點(diǎn)
- 選舉出新的節(jié)點(diǎn)
- 讓從節(jié)點(diǎn)成為主節(jié)點(diǎn)
- 讓原來的master成為從節(jié)點(diǎn)
- 哨兵選舉
Raft : 誰(shuí)先申請(qǐng)成為主節(jié)點(diǎn),誰(shuí)就是主節(jié)點(diǎn)
主從同步的過程
- 從節(jié)點(diǎn)向master發(fā)送slaveof獲取主節(jié)點(diǎn)的信息
- 定時(shí)任務(wù)獲取主節(jié)點(diǎn)信息
- 從節(jié)點(diǎn)去ping主節(jié)點(diǎn),主節(jié)點(diǎn)則返回pang和runid等信息
- 從節(jié)點(diǎn)根據(jù)保存的Master runid判斷是不是第一次同步復(fù)制
- 如果是第一次psync?-1,則進(jìn)行全量復(fù)制
- 全量復(fù)制址啟用用RDB生成快照
- 啟動(dòng)RDB會(huì)fork子進(jìn)程,則子進(jìn)程運(yùn)行期間,新命令進(jìn)入到緩存區(qū)
- RDB生成到磁盤,之后在讀取到內(nèi)存,再進(jìn)行數(shù)據(jù)同步
- 快照內(nèi)容同步完以后,再將緩存的命令緩存到從節(jié)點(diǎn)
- 全量復(fù)制址啟用用RDB生成快照
- 如果不是第一次,則進(jìn)行部分復(fù)制,從節(jié)點(diǎn)向master發(fā)送Psync runid offset
- Master收到命令后會(huì)查看,runid是否一致,之后查看偏移量offset是否超過復(fù)制積壓緩存區(qū)
- 如果偏移量超過復(fù)制積壓緩存區(qū),則err,進(jìn)行全量復(fù)制
- 如果未超過,則offset+偏移量+命令長(zhǎng)度進(jìn)行部分復(fù)制
復(fù)制積壓緩存區(qū)
在主從同步的期間,仍然會(huì)有寫命令在執(zhí)行,這時(shí)命令在寫入主節(jié)點(diǎn)的同時(shí)還會(huì)寫入復(fù)制積壓緩存區(qū),同時(shí)記錄偏移量,如果這期間緩存的命令過多,則沒必要再進(jìn)行部分復(fù)制,直接進(jìn)行全量復(fù)制即可
緩存的常見問題
- 緩存穿透
- 惡意訪問不存在的數(shù)據(jù),導(dǎo)致打入數(shù)據(jù)庫(kù)
- 增加認(rèn)證(接口訪問功能)
- 緩存擊穿
- 某熱點(diǎn)數(shù)據(jù)突然失效,打入數(shù)據(jù)庫(kù)
- 設(shè)置null值
- 緩存雪崩
- 大量數(shù)據(jù)同時(shí)失效
- 設(shè)置隨機(jī)時(shí)間種子