Redis 作为高性能缓存被广泛应用到各个业务,比如游戏的排行榜,分布式锁等场景。
但是腾讯对 Redis 并不满意,一是 Redis 版本更新节奏慢,很多早期大厂期待的功能,迟迟得不到更新,于是乎不少大厂都对 Redis 进行了魔改;二是 Redis 有一些痛点并不满足大厂的业务功能。于是乎,腾讯推出了 Tendis,我们一起来看看 Tendis 有哪些优点。
❝ 经过在 IEG 的长期运营,我们也遇到 Redis 一些痛点问题,比如内存占用高, 数据可靠性差,业务维护缓存和存储的一致性繁琐。 由腾讯互娱 CROS DBA 团队 & 腾讯云数据库团队联合研发的 Tendis 推出了: 缓存版、混合存储版和存储版三种不同产品形态,针对不同的业务需求,本文主要介绍混合存储版的整体架构,并且详细揭秘内部的原理。 ❞
本文首先介绍腾讯 IEG 运营 Redis 遇到的一些痛点问题, 然后介绍由 腾讯互娱 CROS DBA 团队 & 腾讯云数据库团队联合研发的 Tendis 的三种不同的产品形态。最后重点介绍冷热混合存储版的架构,并且重点介绍各个组件的功能特性。
在使用的过程中,主要遇到以下一些痛点问题:
Tendis 是集腾讯众多海量 KV 存储优势于一身的 Redis 存储解决方案, 并 100% 兼容 Redis 协议和 Redis4.0 所有数据模型。作为一个高可用、高性能的分布式 KV 存储数据库, 从访问时延、持久化需求、整体成本等不同维度的考量, Tendis 推出了缓存版、混合存储版和存储版三种不同产品形态,并将存储版开源。
Tencent/Tendis
Tendis 缓存版 适用于对延迟要求特别敏感, 并且对 QPS 要求很高的业务。基于社区 Redis 4.0 版本进行定制开发。
Tendis 存储版 适用于大容量, 延迟不敏感型业务, 数据全部存储在 磁盘, 适合温冷数据的存储。Tendis 存储版是腾讯互娱 CROS DBA 团队 & 腾讯云数据库团队 自主设计和研发的开源分布式高性能 KV 存储系统。另外在 可靠性、复制机制、并发控制、gossip 实现以及数据搬迁等做了大量的优化, 并且解决了一些 Redis cluster 比较棘手的问题。完全兼容 Redis 协议, 并使用 RocksDB 作为底层存储引擎。
Tendis 冷热混合存储版 冷热混合存储 综合了缓存版和存储版的优点, 缓存层存放热数据, 全量数据存放在存储层。这既保证了热数据的访问性能,同时保证了全量数据的可靠性,同时热数据支持自动降冷。
Tendis 冷热混合存储版主要由 Proxy 、缓存层 Redis、存储层 Tendis 存储版 和 同步层 Redis-sync 组成, 其中每个组件的功能如下:
Proxy 组件 : 负责对客户端请求进行路由分发,将不同的 Key 的命令分发到正确的分片,同时 Proxy 还负责了部分监控数据的采集,以及高危命令在线禁用等功能。
缓存层 Redis Cluster : 缓存层 Redis 基于 社区 Redis 4.0 进行开发。Redis 具有以下功能:
存储层 Tendis Cluster : Tendis 存储版 是腾讯基于 RocksDB 自研的 兼容 Redis 协议的 KV 存储引擎, 该引擎已经在腾讯集团内部运营多年, 性能和稳定性得到了充分的验证。在混合存储系统中主要负责全量数据的存储和读取, 以及数据备份, 增量日志备份等功能。
同步层 Redis-sync :
存储层 Tendis
;Redis Cluster和Tendis Cluster
Tendis 冷热混合存储的一些重要特性介绍:
缓存层 Redis Cluster
和 存储层 Tendis Cluster
分别进行扩缩容, 集群自治管理等。冷热混合存储缓存层 Redis 在社区版的基础上增加了以下功能:
下面分别对这几个特性进行详细的讲解。
首先基于社区版 Redis 改动是版本控制。我们为每个 Key 和 每条 Aof 增加一个 Version , 并且 Version 是单调递增的。在每次更新/新增一个 Key 后, 将当前节点的 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;
引入版本控制主要带来以下优势:
❝ 社区版 Redis 主备在断线重连后, 如果 slave 发送的 psync_offset 对应的数据不在当前的 Master 的 repl_backlog 中, 则主备需要重新进行全量同步。再引入 Version 之后, slave 断线重连, 给 Master 发送 带 Version 的
PSYNC replid psync_offset version
命令。如果出现上述情况, Master 将大于等于 Version 的数据生成增量 RDB, 发给 Slave, 进而解决需要增量, 同步比较慢的问题。 ❞
❝ 如果同步层 Redis-sync 出现网络瞬断(短暂的和缓存层或者存储层断开), 作为一个无状态的同步组件, Redis-sync 会重新拉取未同步到 Tendis 的增量数据, 重新发送给 Tendis。每条 Aof 都具有一个 Version, Tendis 在执行的时候仅会执行比当前 Version 大的 Aof, 避免 aof 执行多次导致的数据不一致。 ❞
冷数据的恢复指当用户访问的 Key 不在缓存层, 需要将数据从存储层重新加载到缓存层。数据恢复这里是缓存层直接和存储层直接交互, 当冷 Keys 访问的请求比较大, 数据恢复很容易成为瓶颈, 因此为每个 Tendis 节点建立一个连接池, 专门负责与这个 Tendis 节点进行冷热数据恢复。
冷热数据交互
用户访问一个 Key 的具体流程如下:
dumpx dbid key withttl
命令尝试从存储层获取数据, 并且阻塞当前请求的客户端;RESTOREEX dbid key ttl value
; 如果 Key 不在存储层(Cuckoo Filter 的误判), 则向缓存层返回 DUMPXERROR key
;这里主要讲解混合存储从 1:1 版的缓存层缓存全量 Keys, 到 N:M 版的缓存层将 Key 和 Value 同时驱逐的演进, 以及我们引入 Cuckoo Filter 避免缓存穿透, 同时节省大量内存。
Dynamic Cuckoo Filter
。作为冷热混合存储系统, 热数据在缓存层, 全量数据在存储层。关键的问题是淘汰和加载策略, 这里直接影响缓存的效率, 细分主要有两点:
maxmemory-policy
当缓存层 Redis 内存使用到达 maxmemory, 系统将按照 maxmemory-policy
的内存策略将 Key/Value 从缓存层驱逐, 释放内存空间。(驱逐是指将 Key/Value 从缓存层中淘汰掉, 存储层 和 缓存层的 Cuckoo Filter 依然存在该 Key; )value-eviction-policy
如果配置 value-eviction-policy
, 后台会定期将用户 N 天未访问的 Key/Value 被驱逐出内存;社区版 Redis 的扩容流程:
Redis 的扩容流程
社区版 Redis 扩容存在的一些问题:
❝ 先设置目标节点 slot 为 importing 状态, 再设置源节点的 slot 为 migrating 状态。如果反过来, 由于两次操作非原子: 源节点设置为 migrating , 目标节点还未设置 migrating 状态, 请求在这两个节点间反复 Move 。 ❞
❝ Migrate 命令每次搬迁一个或者多个 Keys, 将整个 Slot 搬迁到目标节点需要多次网络交互。 ❞
❝ 由于 Migrate 命令是同步命令, 在搬迁过程中是不能处理其他用户请求的, 因此可能会影响业务。(延迟时间波动较大) ❞
由于社区版 Redis 存在的上述问题, 我们实现了基于 RDB+Aof 的扩缩容方式, 大致流程如下:
cluster slotsync beginSlot endSlot [[beginSlot endSlot]...]
sync [slot ...]
, 命令请求同步 slot 数据cluster slotfailover
(流程类似 Redis 的 cluster failover, 首先阻塞源节点写入, 然后等待目标节点和源节点的落后值为 0, 最后将 搬迁的 slots 归属目标节点)RDB + Aof的扩缩容方式
同步层 Redis-sync 模拟 Redis Slave 的行为, 接收 RDB 和 Aof, 然后并行地导入到存储层 Tendis。同步层主要需要解决以下问题:
FLUSHALL/FLUSHDB/SWAPDB/SELECT/MULTI
等?Redis-sync
为了解决上述的三个问题, 我们实现了下面的功能:
Slot
到 Tendis 节点
的映射关系, 并且维护这些 Tendis 节点的连接池。请求从 缓存层到达, 然后计算请求所属的 slot, 然后发送到正确的 Tendis 节点。Tendis 是兼容 Redis 核心数据结构与协议的分布式高性能 KV 数据库, 主要具有以下特性:
TendisPlus