Tendis 冷热混合存储版架构

一、组件组成
  • Proxy 组件:

    • 负责对客户端请求进行路由分发,将不同的 Key 的命令分发到正确的分片

    • Proxy 还负责了部分监控数据的采集,以及高危命令在线禁用等功能

  • 缓存层 Redis Cluster:

    • 缓存层 Redis 基于 社区 Redis4.0 进行开发

    • 集成的 Redis 基于社区版并新增以下功能:

      • 版本控制
      • 自动将冷数据从缓存层中淘汰, 将热数据从存储层加载到缓存层
      • 使用 Cuckoo Filter 表示全量 Keys, 防止缓存穿透
      • 智能淘汰算法
      • 基于 RDB + AOF 扩缩容方式, 扩缩容更加高效便捷
  • 存储层 Tendis Cluster:

    • Tendis 存储版 是腾讯基于 RocksDB 自研的 兼容 Redis 协议的 KV 存储引擎

    • 该引擎已经在腾讯集团内部运营多年,性能和稳定性得到了充分的验证

    • 在混合存储系统中主要负责全量数据的存储和读取, 以及数据备份, 增量日志备份等功能

  • 同步层 Redis-sync:

    • 并行数据导入 存储层 Tendis
    • 服务无状态, 故障重新拉起
    • 数据自动路由
二、整体架构
  • 整体架构图

    mark

  • 重要特性

    • 缓存层 Redis Cluster 和 存储层 Tendis Cluster 分别进行扩缩容, 集群自治管理
    • 冷数据自动降冷, 降低内存成本; 热数据自动缓存, 降低访问延迟
三、组件特性
缓存层 Redis Cluster
  • 版本控制

    • 首先基于社区版 Redis 改动是版本控制。产品为每个 Key 和 每条 AOF 增加一个 Version , 并且 Version 是单调递增的。在每次更新/新增一个 Key 后, 将当前节点的 Version 赋值给 KeyValue, 然后对全局的 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;
    
    • 版本控制带来的优势

      • 增量 RDB
        社区版 Redis 主备在断线重连后, 如果 slave 发送的 psync_offset 对应的数据不在当前的 masterrepl_backlog 中, 则主备需要重新进行全量同步。再引入 Version 之后, slave 断线重连, 给 master 发送带 VersionPSYNC replid psync_offset version 命令。如果出现上述情况, master 将大于等于 Version 的数据生成增量 RDB, 发给 slave, 进而解决需要增量, 同步比较慢的问题

      • AOF 幂等
        如果同步层 Redis-sync 出现网络瞬断(短暂的和缓存层或者存储层断开), 作为一个无状态的同步组件, Redis-sync 会重新拉取未同步到 Tendis 的增量数据, 重新发送给 Tendis。每条 AOF 都具有一个 Version, Tendis 在执行的时候仅会执行比当前 Version 大的 AOF, 避免 AOF 执行多次导致的数据不一致

  • 冷热数据交互

    冷数据的恢复指当用户访问的 Key 不在缓存层,需要将数据从存储层重新加载到缓存层。数据恢复这里是缓存层直接和存储层直接交互, 当冷 Keys 访问的请求比较大, 数据恢复很容易成为瓶颈, 因此为每个 Tendis 节点建立一个连接池, 专门负责与这个 Tendis 节点进行冷热数据恢复

    mark

  • Key 降冷 与 Cuckoo Filter

    • 为了进一步释放内存空间, 提高缓存的效率, 产品放弃了 Redis 缓存全量 Keys 的方案, 驱逐的时候将 keyValue 都从缓存层淘汰

    • Cuckoo Filter 解决缓存击穿和缓存穿透如果缓存层不存储全量的 Keys,就会出现缓存击穿和缓存穿透的问题。为了解决这一问题, 缓存层引入 Cuckoo Filter 表示全量的 keys 。我们需要一个支持删除、可动态伸缩并且空间利用率高的 Membership Query 结构, 产品最终选择 Dynamic Cuckoo Filter

    • Dynamic Cuckoo Filter 实现项目初期参考了 RedisBloomCuckoo Filter 的实现

    • Key 降冷的收益最终采用将 KeyValue 同时从缓存层淘汰, 降低内存的收益很大。比如现网的一个业务, 总共有 6620WKeys , 在缓存全量 Keys 的时候 占用 18408MB 的内存, 在 Key 降冷后仅仅占用 593MB

  • 智能淘汰 / 加载策略

    • 作为冷热混合存储系统,热数据在缓存层,全量数据在存储层。关键的问题是淘汰和加载策略, 这里直接影响缓存的效率, 细分主要有两点:

      1. 当缓存层内存满时, 选择哪些数据淘汰
      2. 当用户访问存储层的数据时, 是否需要将其放入缓存层
    • 首先介绍混合存储的淘汰策略, 主要有以下两个淘汰策略:

      • maxmemory-policy 当缓存层 Redis 内存使用到达 maxmemory, 系统将按照 maxmemory-policy 的内存策略将 Key/Value 从缓存层驱逐,释放内存空间。(驱逐是指将 Key/Value 从缓存层中淘汰掉, 存储层 和 缓存层的 Cuckoo Filter 依然存在该 Key)
      • value-eviction-policy 如果配置 value-eviction-policy, 后台会定期将用户 N 天未访问的 Key/Value 被驱逐出内存
    • 缓存加载策略

      • 为了避免缓存污染的问题(比如类似 Scan 的访问, 遍历存储层的数据,将缓存层真正的热数据淘汰, 从而造成了缓存效率低下) 。我们实现缓存加载策略:
        • 仅仅将规定时间内访问频率超过某个阈值的数据加载到缓存中,这里的时间和阈值都是可配置的
  • 基于 RDB + AOF 扩缩容

    mark

    • 社区版 Redis 扩容存在的一些问题:

      • importingmigrating 的设置不是原子的
      • 搬迁以 Key 为粒度, 效率较低
      • Key 问题
    • 基于社区版 Redis 的弥补流程是实现基于 RDB + AOF 扩缩容

      • 管控添加新节点, 规划待搬迁 slots

      • 管控端向目标节点下发 slot 同步命令: cluster slotsync beginSlot endSlot [[beginSlot endSlot]...]

      • 目标节点向源节点发送 sync [slot ...], 命令请求同步 slot 数据

      • 源节点生成指定 slot 数据的一致性快照全量数据(RDB), 并将其发送给目标节点

      • 源节点开始持续发送增量数据(AOF)

      • 管控端定位获取源节点和目标节点的落后值 (diff_bytes),如果落后值在指定的阈值内, 管控端向目标节点发送 cluster slotfailover (流程类似 Rediscluster failover, 首先阻塞源节点写入, 然后等待目标节点和源节点的落后值为 0, 最后将 搬迁的 slots 归属目标节点)

        mark

同步层 Redis-sync

同步层 Redis-sync 模拟 Redis Slave 的行为, 接收 RDBAOF,然后并行地导入到存储层 Tendis。同步层主要需要解决以下问题:

  • 并发地导入到存储层 Tendis, 如何保证时序正确?

  • 特殊命令的处理, 比如 FLUSHALL/FLUSHDB/SWAPDB/SELECT/MULTI 等?

  • 作为一个无状态的同步组件, 如何保证故障后, 数据断点续传?

  • 缓存层和存储层 分别进行扩缩容, 如何将请求路由到正确的 Tendis 节点?

    mark

解决方案:

  • Slot 内串行, Slot 间并行 针对问题 1, Redis-sync 中采用与 Redis 相同的计算 Slot 的算法, 解析到具体的命令后, 根据 Key 所属的 slot, 将其放到对应的 队列中( slot % QueueSize )。因此同一个 Slot 的数据是串行写入, 不同 slot 的数据可以并行写入, 不会出现时序错乱的行为。

  • 串并转换 针对问题 2, Redis-sync 会在并行和串行模式之间进行转换。比如收到 FLUSHDB 命令, 这是需要将 FLUSHDB 命令 前的命令都执行完, 再执行 FLUSHDB命令。

  • 定期上报 针对问题 3, Redis-sync 会定期将已发送给存储层的 aofVersion 持久化到 存储层。如何 Redis-sync 故障, 首先从 存储层获取上次已发送的位置, 然后向对应的 Redis 节点发送 psync, 请求同步。

  • 数据自动路由 针对问题 4, Redis-sync 会定期从存储层获取 SlotTendis 节点的映射关系, 并且维护这些 Tendis 节点的连接池。请求从缓存层到达, 然后计算请求所属的 slot, 然后发送到正确的 Tendis 节点。

存储层 Tendis Cluster

Tendis 是兼容 Redis 核心数据结构与协议的分布式高性能 KV 数据库, 主要具有以下特性:

  • 兼容 Redis 协议 完全兼容 redis 协议,支持 redis主要数据结构和接口,兼容大部分原生 Redis 命令。

  • 持久化存储 使用 rocksdb 作为存储引擎,所有数据以特定格式存储在 rocksdb 中,最大支持 PB 级存储。

  • 去中心化架构 类似于 redis cluster 的分布式实现,所有节点通过 gossip 协议通讯,可指定 hashtag 来控制数据分布和访问,使用和运维成本极低。

  • 水平扩展集群支持增删节点,并且数据可以按照 slot 在任意两节点之间迁移,扩容和缩容过程中对应用运维人员透明,支持扩展至 1000 个节点。

  • 故障自动切换 自动检测故障节点,当故障发生后,slave 会自动提升为 master 继续对外提供服务。
    mark

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值