騰訊云超火開源數(shù)據庫產品架構揭秘
Redis&Tendis
Tendis混合存儲版整體架構

緩存層Redis Cluster
一、版本控制
如下所示, 在 redisObject 中添加 64bits, 其中 48bits 用于版本控制。
typedef struct redisObject {unsigned type:4;unsigned encoding:4;unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or* LFU data (least significant 8 bits frequency* and most significant 16 bits access time). */int refcount;/* for hybrid storage */unsigned flag:4; /* OBJ_FLAG_... */unsigned reserved:4;unsigned counter:8; /* for cold-data-cache-policy */unsigned long long revision:REVISION_BITS; /* for value version */void *ptr;} robj;

2020 年 6 月份上線的 1:1 版的冷熱混合存儲, 緩存層 Redis 存儲全量的 Keys 和熱 Values(All Keys + Hot values), 存儲層 Tendis 存儲全量的 Keys 和 Values(All Keys + All values)。在上線運行了一段時間后, 發(fā)現(xiàn)全量 Keys 的內存開銷特別大, 冷熱混合的收益并不明顯。為了進一步釋放內存空間, 提高緩存的效率, 我們放棄了 Redis 緩存全量 Keys 的方案, 驅逐的時候將 key 和 Value 都從緩存層淘汰。
如果緩存層不存儲全量的 Keys, 就會出現(xiàn)緩存擊穿和緩存穿透的問題。為了解決這一問題, 緩存層引入 Cuckoo Filter 表示全量的 keys 。我們需要一個支持刪除、可動態(tài)伸縮并且空間利用率高的 Membership Query 結構, 經過我們的調研和對比,最終選擇 Dynamic Cuckoo Filter。
項目初期參考了 RedisBloom 中 Cuckoo Filter 的實現(xiàn), 在開發(fā)的過程中也遇到了一些坑,RedisBloom 實現(xiàn)的 Cuckoo Filter 在刪除的時候會出現(xiàn)誤刪, 最終給 RedisBloom 提 PR 修復了問題。
最終采用將 Key 和 Value 同時從緩存層淘汰, 降低內存的收益很大。比如現(xiàn)網的一個業(yè)務, 總共有 6620 W 個 Keys , 在緩存全量 Keys 的時候 占用 18408 MB的內存, 在 Key 降冷后 僅僅占用 593MB 。
當緩存層 Redis 內存使用到達 maxmemory, 系統(tǒng)將按照 maxmemory-policy 的內存策略將 Key/Value 從緩存層驅逐, 釋放內存空間。(驅逐是指將 Key/Value 從緩存層中淘汰掉, 存儲層 和 緩存層的 Cuckoo Filter 依然存在該 Key;
如果配置 value-eviction-policy, 后臺會定期將用戶 N 天未訪問的 Key/Value 被驅逐出內存;

2) 管控端向目標節(jié)點下發(fā)slot同步命令: cluster slotsync beginSlot endSlot [[beginSlot endSlot]...]
3) 目標節(jié)點向源節(jié)點發(fā)送 sync [slot ...], 命令請求同步slot數(shù)據
4) 源節(jié)點生成指定 slot 數(shù)據的一致性快照全量數(shù)據(RDB), 并將其發(fā)送給目標節(jié)點
5) 源節(jié)點開始持續(xù)發(fā)送增量數(shù)據(Aof)
6) 管控端定位獲取源節(jié)點和目標節(jié)點的落后值 (diff_bytes), 如果落后值在指定的閾值內, 管控端向目標節(jié)點發(fā)送 cluster slotfailover (流程類似 Redis 的 cluster failover, 首先阻塞源節(jié)點寫入, 然后等待目標節(jié)點和源節(jié)點的落后值為 0, 最后將 搬遷的 slots 歸屬目標節(jié)點)

同步層 Redis-sync

針對問題 2, Redis-sync 會在并行和串行模式之間進行轉換。比如收到 FLUSHDB 命令, 這是需要將 FLUSHDB 命令 前的命令都執(zhí)行完, 再執(zhí)行 FLUSHDB 命令。
針對問題 3, Redis-sync 會定期將已發(fā)送給存儲層的 aof 的 Version 持久化到 存儲層。如何 Redis-sync 故障, 首先從 存儲層獲取上次已發(fā)送的位置, 然后向對應的 Redis 節(jié)點發(fā)送 psync, 請求同步。
針對問題 4, Redis-sync 會定期從存儲層獲取 Slot 到 Tendis 節(jié)點的映射關系, 并且維護這些 Tendis 節(jié)點的連接池。請求從 緩存層到達, 然后計算請求所屬的 slot, 然后發(fā)送到正確的 Tendis 節(jié)點。
存儲層 Tendis Cluster
Tendis 是兼容 Redis 核心數(shù)據結構與協(xié)議的分布式高性能 KV 數(shù)據庫, 主要具有以下特性:

評論
圖片
表情
