<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          干掉Redis,看騰訊這個(gè)技術(shù)有多牛逼!

          共 8284字,需瀏覽 17分鐘

           ·

          2021-05-19 06:37

          Redis 作為高性能緩存被廣泛應(yīng)用到各個(gè)業(yè)務(wù), 比如游戲的排行榜, 分布式鎖等場景。經(jīng)過在 IEG 的長期運(yùn)營, 我們也遇到 Redis 一些痛點(diǎn)問題, 比如內(nèi)存占用高, 數(shù)據(jù)可靠性差, 業(yè)務(wù)維護(hù)緩存和存儲的一致性繁瑣。由騰訊互娛 CROS DBA 團(tuán)隊(duì) & 騰訊云數(shù)據(jù)庫團(tuán)隊(duì)聯(lián)合研發(fā)的 Tendis 推出了: 緩存版、混合存儲版和存儲版三種不同產(chǎn)品形態(tài), 針對不同的業(yè)務(wù)需求, 本文主要介紹混合存儲版的整體架構(gòu), 并且詳細(xì)揭秘內(nèi)部的原理。


          本文首先介紹騰訊 IEG 運(yùn)營 Redis 遇到的一些痛點(diǎn)問題, 然后介紹由騰訊互娛 CROS DBA 團(tuán)隊(duì) & 騰訊云數(shù)據(jù)庫團(tuán)隊(duì)聯(lián)合研發(fā)的 Tendis 的三種不同的產(chǎn)品形態(tài)。最后重點(diǎn)介紹冷熱混合存儲版的架構(gòu), 并且重點(diǎn)介紹各個(gè)組件的功能特性。



          1

          背景介紹

           

          Redis 有哪些痛點(diǎn) ?


          在使用的過程中, 主要遇到以下一些痛點(diǎn)問題:


          • 內(nèi)存成本高
            1. 業(yè)務(wù)不同階段對 QPS 要求不同 比如游戲業(yè)務(wù), 剛上線的新游戲特別火爆, 為了支持上千萬同時(shí)在線, 需要不斷的進(jìn)行擴(kuò)容增加機(jī)器。運(yùn)營一段時(shí)間后, 游戲玩家可能變少, 訪問頻率(QPS)沒那么高, 依然占用大量機(jī)器, 維護(hù)成本很高。
            2. 需要為 Fork 預(yù)留內(nèi)存 Redis 保存全量數(shù)據(jù)時(shí), 需要 Fork 一個(gè)進(jìn)程。Linux 的 fork 系統(tǒng)調(diào)用基于 Copy On Write 機(jī)制, 如果在此期間 Redis 有大量的寫操作, 父子進(jìn)程就需要各自維護(hù)一份內(nèi)存。因此部署 Redis 的機(jī)器往往需要預(yù)留一半的內(nèi)存。
          • 緩存一致性的問題 對于 Redis + MySQL 的架構(gòu)需要業(yè)務(wù)方花費(fèi)大量的精力來維護(hù)緩存和數(shù)據(jù)庫的一致性。
          • 數(shù)據(jù)可靠性 Redis 本質(zhì)上是一個(gè)內(nèi)存數(shù)據(jù)庫, 用戶雖然可以使用 AOF 的 Always 來落盤保證數(shù)據(jù)可靠性, 但是會帶來性能的大幅下降, 因此生產(chǎn)環(huán)境很少有使用。另外 不支持 回檔, Master 故障后, 異步復(fù)制會造成數(shù)據(jù)的丟失。
          • 異步復(fù)制 Redis 主備使用異步復(fù)制, 這個(gè)是異步復(fù)制固有的問題。主備使用異步復(fù)制, 響應(yīng)延遲低, 性能高, 但是 Master 故障后, 會造成數(shù)據(jù)丟失。


          Tendis 是什么 ?


          Tendis 是集騰訊眾多海量 KV 存儲優(yōu)勢于一身的 Redis 存儲解決方案, 并 100% 兼容 Redis 協(xié)議和 Redis4.0 所有數(shù)據(jù)模型。作為一個(gè)高可用、高性能的分布式 KV 存儲數(shù)據(jù)庫, 從訪問時(shí)延、持久化需求、整體成本等不同維度的考量, Tendis 推出了 緩存版 、 混合存儲版 和 存儲版 三種不同產(chǎn)品形態(tài),并將存儲版開源。



          Tendis 緩存版適用于對延遲要求特別敏感, 并且對 QPS 要求很高的業(yè)務(wù)?;谏鐓^(qū) Redis 4.0 版本進(jìn)行定制開發(fā)。


          Tendis 存儲版適用于大容量, 延遲不敏感型業(yè)務(wù), 數(shù)據(jù)全部存儲在 磁盤, 適合溫冷數(shù)據(jù)的存儲。Tendis 存儲版是騰訊互娛 CROS DBA 團(tuán)隊(duì) & 騰訊云數(shù)據(jù)庫團(tuán)隊(duì) 自主設(shè)計(jì)和研發(fā)的開源分布式高性能 KV 存儲系統(tǒng)。另外在 可靠性、復(fù)制機(jī)制、并發(fā)控制、gossip 實(shí)現(xiàn)以及數(shù)據(jù)搬遷等做了大量的優(yōu)化, 并且解決了一些 Redis cluster 比較棘手的問題。完全兼容 Redis 協(xié)議, 并使用 RocksDB 作為底層存儲引擎。


          Tendis 冷熱混合存儲版冷熱混合存儲 綜合了緩存版和存儲版的優(yōu)點(diǎn), 緩存層存放熱數(shù)據(jù), 全量數(shù)據(jù)存放在存儲層。這既保證了熱數(shù)據(jù)的訪問性能,同時(shí)保證了全量數(shù)據(jù)的可靠性,同時(shí)熱數(shù)據(jù)支持自動降冷。



          2

          Tendis 冷熱混合存儲版 整體架構(gòu)

           

          Tendis 冷熱混合存儲版主要由 Proxy 、緩存層 Redis、 存儲層 Tendis 存儲版 和 同步層 Redis-sync 組成, 其中每個(gè)組件的功能如下:


          Proxy 組件: 負(fù)責(zé)對客戶端請求進(jìn)行路由分發(fā),將不同的 Key 的命令分發(fā)到正確的分片,同時(shí) Proxy 還負(fù)責(zé)了部分監(jiān)控?cái)?shù)據(jù)的采集,以及高危命令在線禁用等功能。


          緩存層 Redis Cluster: 緩存層 Redis 基于 社區(qū) Redis 4.0 進(jìn)行開發(fā)。Redis 具有以下功能: 1) 版本控制 2) 自動將 冷數(shù)據(jù)從緩存層中淘汰, 將熱數(shù)據(jù)從存儲層加載到緩存層; 3) 使用 Cuckoo Filter 表示全量 Keys, 防止緩存穿透; 4) 基于 RDB+AOF 擴(kuò)縮容方式, 擴(kuò)縮容更加高效便捷。


          存儲層 Tendis Cluster: Tendis 存儲版 是騰訊基于 RocksDB 自研的 兼容 Redis 協(xié)議的 KV 存儲引擎, 該引擎已經(jīng)在騰訊集團(tuán)內(nèi)部運(yùn)營多年, 性能和穩(wěn)定性得到了充分的驗(yàn)證。在混合存儲系統(tǒng)中主要負(fù)責(zé)全量數(shù)據(jù)的存儲和讀取, 以及數(shù)據(jù)備份, 增量日志備份等功能。


          同步層 Redis-sync

          • 并行數(shù)據(jù)導(dǎo)入 存儲層 Tendis

          • 服務(wù)無狀態(tài), 故障重新拉起

          • 數(shù)據(jù)自動路由



          Tendis 冷熱混合存儲的一些重要特性介紹:


          • 緩存層 Redis Cluster 和 存儲層 Tendis Cluster 分別進(jìn)行擴(kuò)縮容, 集群自治管理等。
          • 冷數(shù)據(jù)自動降冷, 降低內(nèi)存成本; 熱數(shù)據(jù)自動緩存, 降低訪問延遲



          3

          緩存層 Redis Cluster


          冷熱混合存儲緩存層 Redis 在社區(qū)版的基礎(chǔ)上增加了以下功能:


          • 版本控制
          • 冷熱數(shù)據(jù)交互
          • Cuckoo Filter 避免緩存穿透
          • 智能淘汰算法
          • 基于 RDB+AOF 擴(kuò)縮容


          下面分別對這幾個(gè)特性進(jìn)行詳細(xì)的講解。


          版本控制


          首先基于社區(qū)版 Redis 改動是版本控制。我們?yōu)槊總€(gè) Key 和 每條 Aof 增加一個(gè) Version , 并且 Version 是單調(diào)遞增的。在每次更新/新增一個(gè) Key 后, 將當(dāng)前節(jié)點(diǎn)的 Version 賦值給 Key 和 Value, 然后對全局的 Version++; 如下所示, 在 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;


          引入版本控制主要帶來以下優(yōu)勢:


          1. 增量 RDB

          社區(qū)版 Redis 主備在斷線重連后, 如果 slave 發(fā)送的 psync_offset 對應(yīng)的數(shù)據(jù)不在當(dāng)前的 Master 的 repl_backlog 中, 則主備需要重新進(jìn)行全量同步。再引入 Version 之后, slave 斷線重連, 給 Master 發(fā)送 帶 Version 的 PSYNC replid psync_offset version命令。如果出現(xiàn)上述情況, Master 將大于等于 Version 的數(shù)據(jù)生成增量 RDB, 發(fā)給 Slave, 進(jìn)而解決需要增量, 同步比較慢的問題。

          1. Aof 的冪等

          如果同步層 Redis-sync 出現(xiàn)網(wǎng)絡(luò)瞬斷(短暫的和緩存層或者存儲層斷開), 作為一個(gè)無狀態(tài)的同步組件, Redis-sync 會重新拉取未同步到 Tendis 的增量數(shù)據(jù), 重新發(fā)送給 Tendis。每條 Aof 都具有一個(gè) Version, Tendis 在執(zhí)行的時(shí)候僅會執(zhí)行比當(dāng)前 Version 大的 Aof, 避免 aof 執(zhí)行多次導(dǎo)致的數(shù)據(jù)不一致。


          冷熱數(shù)據(jù)交互


          冷數(shù)據(jù)的恢復(fù)指當(dāng)用戶訪問的 Key 不在緩存層, 需要將數(shù)據(jù)從存儲層重新加載到緩存層。數(shù)據(jù)恢復(fù)這里是緩存層直接和存儲層直接交互, 當(dāng)冷 Keys 訪問的請求比較大, 數(shù)據(jù)恢復(fù)很容易成為瓶頸, 因此為每個(gè) Tendis 節(jié)點(diǎn)建立一個(gè)連接池, 專門負(fù)責(zé)與這個(gè) Tendis 節(jié)點(diǎn)進(jìn)行冷熱數(shù)據(jù)恢復(fù)。



          用戶訪問一個(gè) Key 的具體流程如下:


          1. 首先判斷 Key 是否在緩存層, 如果緩存層存在, 則執(zhí)行命令; 如果緩存層不存在, 查詢 Cuckoo Filter, 判斷 Key 是否有可能在存儲層;
          2. 如果 Key 可能在存儲層, 則向存儲層發(fā)送 dumpx dbid key withttl 命令嘗試從存儲層獲取數(shù)據(jù), 并且阻塞當(dāng)前請求的客戶端;
          3. 存儲層收到 dumpx , 如果 Key 在存儲層, 則向緩存層返回 RESTOREEX dbid key ttl value; 如果 Key 不在存儲層(Cuckoo Filter 的誤判), 則向緩存層返回 DUMPXERROR key;
          4. 存儲層收到 RESTOREEX 或者 DUMPXERROR 后, 將冷數(shù)據(jù)恢復(fù)。然后就可以喚醒阻塞的客戶端, 執(zhí)行客戶端的請求。


          Key 降冷 與 Cuckoo Filter


          這里主要講解混合存儲從 1:1 版的緩存層緩存全量 Keys, 到 N:M 版的緩存層將 Key 和 Value 同時(shí)驅(qū)逐的演進(jìn), 以及我們引入 Cuckoo Filter 避免緩存穿透, 同時(shí)節(jié)省大量內(nèi)存。


          1. Key 降冷的背景介紹2020 年 6 月份上線的 1:1 版的冷熱混合存儲, 緩存層 Redis 存儲全量的 Keys 和熱 Values(All Keys + Hot values), 存儲層 Tendis 存儲全量的 Keys 和 Values(All Keys + All values)。在上線運(yùn)行了一段時(shí)間后, 發(fā)現(xiàn)全量 Keys 的內(nèi)存開銷特別大, 冷熱混合的收益并不明顯。為了進(jìn)一步釋放內(nèi)存空間, 提高緩存的效率, 我們放棄了 Redis 緩存全量 Keys 的方案, 驅(qū)逐的時(shí)候?qū)?key 和 Value 都從緩存層淘汰。

          2. Cuckoo Filter 解決緩存擊穿和緩存穿透如果緩存層不存儲全量的 Keys, 就會出現(xiàn)緩存擊穿和緩存穿透的問題。為了解決這一問題, 緩存層引入 Cuckoo Filter 表示全量的 keys 。我們需要一個(gè)支持刪除、可動態(tài)伸縮并且空間利用率高的 Membership Query 結(jié)構(gòu), 經(jīng)過我們的調(diào)研和對比分析, 最終選擇 Dynamic Cuckoo Filter。

          3. Dynamic Cuckoo Filter 實(shí)現(xiàn)項(xiàng)目初期參考了 RedisBloom 中 Cuckoo Filter 的實(shí)現(xiàn), 在開發(fā)的過程中也遇到了一些坑, RedisBloom 實(shí)現(xiàn)的 Cuckoo Filter 在刪除的時(shí)候會出現(xiàn)誤刪, 最終給 RedisBloom 提 PR(Fix Cuckoo filter compact cause deleted by mistake #260) 修復(fù)了問題。

          4. Key 降冷的收益最終采用將 Key 和 Value 同時(shí)從緩存層淘汰, 降低內(nèi)存的收益很大。比如現(xiàn)網(wǎng)的一個(gè)業(yè)務(wù), 總共有 6620 W 個(gè) Keys , 在緩存全量 Keys 的時(shí)候 占用 18408 MB 的內(nèi)存, 在 Key 降冷后 僅僅占用 593MB 。


          智能淘汰/加載策略


          作為冷熱混合存儲系統(tǒng), 熱數(shù)據(jù)在緩存層, 全量數(shù)據(jù)在存儲層。關(guān)鍵的問題是淘汰和加載策略, 這里直接影響緩存的效率, 細(xì)分主要有兩點(diǎn): 1) 當(dāng)緩存層內(nèi)存滿時(shí), 選擇哪些數(shù)據(jù)淘汰; 2) 當(dāng)用戶訪問存儲層的數(shù)據(jù)時(shí), 是否需要將其放入緩存層


          1. 首先介紹混合存儲的淘汰策略, 主要有以下兩個(gè)淘汰策略:

          • maxmemory-policy 當(dāng)緩存層 Redis 內(nèi)存使用到達(dá) maxmemory, 系統(tǒng)將按照 maxmemory-policy 的內(nèi)存策略將 Key/Value 從緩存層驅(qū)逐, 釋放內(nèi)存空間。(驅(qū)逐是指將 Key/Value 從緩存層中淘汰掉, 存儲層 和 緩存層的 Cuckoo Filter 依然存在該 Key; )
          • value-eviction-policy 如果配置 value-eviction-policy, 后臺會定期將用戶 N 天未訪問的 Key/Value 被驅(qū)逐出內(nèi)存;

          1. 緩存加載策略 為了避免緩存污染的問題(比如類似 Scan 的訪問, 遍歷存儲層的數(shù)據(jù), 將緩存層真正的熱數(shù)據(jù)淘汰, 從而造成了緩存效率低下) 。我們實(shí)現(xiàn)緩存加載策略: 僅僅將規(guī)定時(shí)間內(nèi)訪問頻率超過某個(gè)閾值的數(shù)據(jù)加載到緩存中, 這里的時(shí)間和閾值都是可配置的。

          基于 RDB+AOF 擴(kuò)縮容


          社區(qū)版 Redis 的擴(kuò)容流程:



          社區(qū)版 Redis 擴(kuò)容存在的一些問題:


          1. importing 和 migrating 的設(shè)置不是原子的

          先設(shè)置目標(biāo)節(jié)點(diǎn) slot 為 importing 狀態(tài), 再設(shè)置源節(jié)點(diǎn)的 slot 為 migrating 狀態(tài)。如果反過來, 由于兩次操作非原子: 源節(jié)點(diǎn)設(shè)置為 migrating , 目標(biāo)節(jié)點(diǎn)還未設(shè)置 migrating 狀態(tài), 請求在這兩個(gè)節(jié)點(diǎn)間反復(fù) Move 。

          1. 搬遷以 Key 為粒度, 效率較低

          Migrate 命令每次搬遷一個(gè)或者多個(gè) Keys, 將整個(gè) Slot 搬遷到目標(biāo)節(jié)點(diǎn)需要多次網(wǎng)絡(luò)交互。

          1. 大 Key 問題

          由于 Migrate 命令是同步命令, 在搬遷過程中是不能處理其他用戶請求的, 因此可能會影響業(yè)務(wù)。(延遲時(shí)間波動較大)


          由于社區(qū)版 Redis 存在的上述問題, 我們實(shí)現(xiàn)了基于 RDB+Aof 的擴(kuò)縮容方式, 大致流程如下:


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



           

          4

          同步層 Redis-sync


          同步層 Redis-sync 模擬 Redis Slave 的行為, 接收 RDB 和 Aof, 然后并行地導(dǎo)入到存儲層 Tendis。同步層主要需要解決以下問題:


          • 并發(fā)地導(dǎo)入到存儲層 Tendis, 如何保證時(shí)序正確 ?
          • 特殊命令的處理, 比如 FLUSHALL/FLUSHDB/SWAPDB/SELECT/MULTI 等 ?
          • 作為一個(gè)無狀態(tài)的同步組件, 如何保證故障后, 數(shù)據(jù)斷點(diǎn)續(xù)傳 ?
          • 緩存層和存儲層 分別進(jìn)行擴(kuò)縮容, 如何將請求路由到正確的 Tendis 節(jié)點(diǎn) ?



          為了解決上述的三個(gè)問題, 我們實(shí)現(xiàn)了下面的功能:


          • Slot 內(nèi)串行, Slot 間并行 針對問題 1, Redis-sync 中采用與 Redis 相同的計(jì)算 Slot 的算法, 解析到具體的命令后, 根據(jù) Key 所屬的 slot, 將其放到對應(yīng)的 隊(duì)列中( slot%QueueSize )。因此同一個(gè) Slot 的數(shù)據(jù)是串行寫入, 不同 slot 的數(shù)據(jù)可以并行寫入, 不會出現(xiàn)時(shí)序錯(cuò)亂的行為。
          • 串并轉(zhuǎn)換 針對問題 2, Redis-sync 會在并行和串行模式之間進(jìn)行轉(zhuǎn)換。比如收到 FLUSHDB 命令, 這是需要將 FLUSHDB 命令 前的命令都執(zhí)行完, 再執(zhí)行 FLUSHDB 命令。
          • 定期上報(bào) 針對問題 3, Redis-sync 會定期將已發(fā)送給存儲層的 aof 的 Version 持久化到 存儲層。如何 Redis-sync 故障, 首先從 存儲層獲取上次已發(fā)送的位置, 然后向?qū)?yīng)的 Redis 節(jié)點(diǎn)發(fā)送 psync, 請求同步。
          • 數(shù)據(jù)自動路由 針對問題 4, Redis-sync 會定期從存儲層獲取 Slot 到 Tendis 節(jié)點(diǎn)的映射關(guān)系, 并且維護(hù)這些 Tendis 節(jié)點(diǎn)的連接池。請求從 緩存層到達(dá), 然后計(jì)算請求所屬的 slot, 然后發(fā)送到正確的 Tendis 節(jié)點(diǎn)。


           

          5

          存儲層 Tendis Cluster


          Tendis 是兼容 Redis 核心數(shù)據(jù)結(jié)構(gòu)與協(xié)議的分布式高性能 KV 數(shù)據(jù)庫, 主要具有以下特性:


          • 兼容 Redis 協(xié)議 完全兼容 redis 協(xié)議,支持 redis 主要數(shù)據(jù)結(jié)構(gòu)和接口,兼容大部分原生 Redis 命令。
          • 持久化存儲 使用 rocksdb 作為存儲引擎,所有數(shù)據(jù)以特定格式存儲在 rocksdb 中,最大支持 PB 級存儲。
          • 去中心化架構(gòu) 類似于 redis cluster 的分布式實(shí)現(xiàn),所有節(jié)點(diǎn)通過 gossip 協(xié)議通訊,可指定 hashtag 來控制數(shù)據(jù)分布和訪問,使用和運(yùn)維成本極低。
          • 水平擴(kuò)展 集群支持增刪節(jié)點(diǎn),并且數(shù)據(jù)可以按照 slot 在任意兩節(jié)點(diǎn)之間遷移,擴(kuò)容和縮容過程中對應(yīng)用運(yùn)維人員透明,支持?jǐn)U展至 1000 個(gè)節(jié)點(diǎn)。
          • 故障自動切換 自動檢測故障節(jié)點(diǎn),當(dāng)故障發(fā)生后,slave 會自動提升為 master 繼續(xù)對外提供服務(wù)。



          來源:jingjunli,騰訊 IEG 后臺開發(fā)工程師


          PS:防止找不到本篇文章,可以收藏點(diǎn)贊,方便翻閱查找哦。


          瀏覽 101
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美性愛手机在线 | 欧美,日韩,国产A黄色电影 | 高清无码片 | 国产偷窥熟女精品视频大全 | 亚洲婷婷免顾视频 |