(14)美团万亿级 KV 存储架构与实践
2.1 Squirrel 节点容灾 2.2 跨地域容灾 2.3 智能迁移 2.4 持久化重构 2.5 热点 Key
3.1节点容灾 3.2跨地域容灾 3.3强一致 3.4智能迁移 3.5快慢列队 3.6热点 Key
一、美团点评 KV 存储发展历程
1.1第一代(左侧)
客户端内做一致性哈希,后端部署很多Memcached实例,实现最基本 KV 存储分布式设计。
问题:宕机摘除节点/扩容,一致性哈希丢数据
1.2第二代(右侧)
客户端同上,服务器Redis组成的主从结构。
问题:哨兵完成 Failover,实现高可用。但扩缩容,一致性哈希仍丢数据
1.3第三代
这时发现成熟KV 存储开源项目:阿里 Tair。
开源版本架构主要分成三部分:
1)存储节点:上报心跳到它的中心节点
2)中心节点:两个配置管理节点,会监控所有的存储节点,宕机或者扩容时,集群拓扑重新构建
3)客户端:启动时,直接从中心节点拉路由表。根据路由表(集群数据分布图)直接读写存储节点。有数据迁移机制保证数据完整性。
使用遇到问题:Tair 解决了一些问题,但无法完全满足
1)没有仲裁,网络分割有可能发生“脑裂”的,给业务造成过较大影响。
2)容灾扩容时,数据迁移影响到业务可用性
3)Redis数据结构特别丰富,Tair 还不支持
1.4自研
Squirrel:基于Redis Cluster(2015 年发布),演进出全内存、高吞吐、低延迟的 KV 存储。
迭代:自研和社区并重,尽量兼容官方。
应用:数据量小,对延迟敏感
Cellar:基于 Tair,演进出持久化、大容量、数据高可靠KV 存储。
迭代:完全靠自研(四五年没更新)。和 Squirrel 在解决同样的问题时也选取了不同的设计方案。
应用:数据量大,对延迟不特别敏感
目前美团内部每天的调用量均已突破万亿,请求峰值突破每秒亿级
二、内存 KV Squirrel 架构和实践
共通:数据分布一样(Key怎么分布到存储节点上)。拿Key 哈希到哈希值,哈希值对 Slot 数取模得Slot id,都是预分片16384个Slot。路由表就是一个 Slot 到存储节点对照表
Squirrel 架构:
集群跟Redis一致(中间):主从,通过Gossip 协议去通信。
添加集群调度平台(右):调度服务、扩缩容服务和高可用服务等,管理整个集群,把结果作为元数据更新到zk。客户端会订阅zk元数据变更,实时获取到集群拓扑状态,直接读写Redis集群
2.1 Squirrel 节点容灾
1)Redis集群节点宕机已有完备处理机制:从宕机到被标记为 FAIL 摘除,一般30秒。主库摘除可能影响数据完整性,谨慎一些。但从库,我们认为这个过程完全没必要。
2)内存KV 存储数据量一般比较小。业务量大公司,会有很多集群。如交换机故障,影响很多集群,宕机补副本非常麻烦。
我们做了 HA 高可用服务,解决这两个问题:1)从库摘除时间从 30 秒降到5 秒。2)通过 HA 自动申请容器实例加入集群的方式,把宕机补副本变成分钟级自动操作,不需人工介入
实时监控集群的所有节点。网络抖动/宕机(比如说Redis2 )实时更新zk,去摘除Redis 2:1)客户端收到消息后,读流量就直接路由到Redis3上。
2)如只是几十秒的网络抖动, HA 节点监控到恢复后,重新加回
3)如HA 判断属于永久性宕机,HA 节点会直接从 Kubernetes 集群申请新的 Redis 4 容器实例,加到集群里。拓扑结构又变成一主两从,HA 节点更新完集群拓扑之后,写 ZK通知客户端去更新路由,客户端就能读新从库 Redis 4
2.2 Squirrel 跨地域容灾
1、跨地域于单节点不同:
1)跨地域专线不稳定,相对于同地域机房间网络;
2)带宽有限且昂贵,同样一份数据要传输两次,巨大带宽浪费
3)官方主从同步满足不了,单元化部署和异地多活架构。基于此,我们做了集群间复制方案
2、下图:北京主集群、上海从集群,把北京数据同步上海:
1)向同步调度模块,下发“建立同步链路”任务,
2)同步调度模块,根据集群结构,把任务下发到同步集群,
3)同步集群收到,扮成 Redis 的 Slave,通过 Redis 复制协议,从主集群上的从库拉数据,包括 RDB及后续增量变更
4)同步机收到数据后,把它转成客户端写命令,写上海从集群主节点里。
ps:同样,异地多活,再加一个反向同步链路,实现集群双向同步
如何做好微观角度高可用,保持端到端的高成功率。 Squirrel 三个影响成功率的问题:
1)数据迁移造成超时抖动
2)持久化造成超时抖动
3)热点 Key 请求导致单节点过载
2.3 Squirrel 智能迁移
数据迁移三个问题:
1)Redis Cluster 虽能迁移,但不管要迁哪些 Slot,从哪迁到哪
2)想越快越好,但迁移过快又可能影响业务正常请求
3)Redis 的 Migrate 命令会阻塞工作线程,尤其迁移大 Value 时候会阻塞特别久
解决这些问题,做新的迁移服务
1)生成迁移任务,的核心是“就近原则”,同机房迁移肯定比跨机房快。
2)任务生成后,下发任务到一批迁移机上
迁移机迁移时特点:
1.并发,同时给 Redis 1、Redis 3 下发迁移命令
2.每个 Migrate 命令会迁移一批 Key
3.监控实时采集客户端成功率、耗时,服务端负载、QPS 等,把状态反馈到迁移机。
4.迁移过程类似 TCP 慢启动,速度一直加,若请求成功率下降,降速度,速度动态平衡中稳定,最快速迁移,最小影响业务正常请求
3)大 Value 的迁移,异步实现 Migrate 命令,执行时,Redis 主线程继续处理正常请求。如有迁移请求,直接返回错误。保证业务请求处理,同时不阻塞主线程
2.4 Squirrel 持久化重构
背景:RDB 过程调用 Fork 产生子进程去写数据到硬盘,虽然有操作系统COW 机制,但内存用量达到 10 /20 G 时,秒级阻塞。在线业务来无法接受。可靠性要求高业务开启 AOF,开 AOF 可能因 IO 抖动进程阻塞,影响请求成功率。
改进:
1)写时:先写 DB ,然后写内存 Backlog,跟官方一样。同时把请求发异步线程,把变更刷到硬盘 Backlog 。Backlog 过多,做 RDB(业务低峰期) ,把 RDB 之前Backlog 删除。
2)找同步点时
1.从内存 Backlog 里找,
2.没有去硬盘 Backlog 找(由于硬盘空间很大,存储多,很少会找不到)。
3.如硬盘 Backlog 没有,触发全量重传, 直接用硬盘已存RDB 及之后硬盘 Backlog 完成全量重传。
优点:1.不需当场生成 RDB,减少很多全量重传
2.控制在低峰区生成 RDB ,减少RDB 造成的抖动。同时避免了写 AOF 造成的抖动。ps:写 AOF 完全异步,比官方可靠性差一些,但可用性提升,非常值得的。
2.5 Squirrel 热点 Key
解决方案如下图,普通主、从是正常集群中节点,热点主、从游离于正常集群外节点。它们之间怎么发生联系
实时热点监控,流控止损: 读写普通节点时,节点内同时做请求 Key 统计,某Key 达到一定访问量或者带宽占用量,自动触发流控以限制热点 Key 访问,防止节点被打满。
自动热点隔离,迁移和快速扩容: 同时,监控服务周期性去Redis 实例查统计热点 Key。把热点 Key 所在 Slot 上报到迁移服务,把热点主从节点加到集群中,热点 Slot 迁移到这个热点主从上。因为热点主从只有热点 Slot 请求,处理能力大幅提升
三、持久化 KV Cellar 架构和实践
跟开源Tair两不同:
OB: 跟 ZooKeeper 的 Observer类似作用,查询Cellar 中心节点元数据。与中心节点 Master 实时同步路由表,客户端路由表从 OB 拿。
这样好处:1)把大量的业务客户端跟集群的大脑 Master 做了天然的隔离,防止路由表请求影响集群的管理。2)因为 OB 只供路由表查询,不参与集群的管理,所以它可以进行水平扩展,极大地提升路由表查询能力
ZK:做分布式仲裁,解决Master、Slave 网络分割的“脑裂”问题,元数据存zk,保证高可靠
最新Cellar 架构图3.1 Cellar 节点容灾
集群节点宕机、网络抖动一般是临时的,很快恢复,重新加入集群。因为临时离开就彻底摘除,并做数据副本补全,消耗大,影响业务请求。所以,实现Handoff解决节点短时故障带来的影响:
A 宕机触发 Handoff :1)中心节点通知客户端 A故障,2)让客户端把分片 1 请求也打到 B 上。3)B 处理完,把应写入 A的 1&2 数据写本地 Log 中
A 节点宕机3~5 分钟,或网络抖动 30~50 秒恢复:
1)A 上报心跳到中心节点,通知 B 节点 A恢复
2)B把本地 Log 回写A 上,A 有故障期全量数据后
3)中心节点告诉客户端,客户端重新把分片 1 请求打回 A 节点
好处:1)秒级摘除,恢复后加回,只需补少量增量数据。
2)主动触发 Handoff 机制,静默升级:如A 升级,中心节点通过主动 Handoff 把 A 流量切到 B ,A 升级后回写增量 Log,切回流量加入集群
3.2 Cellar 跨地域容灾
Cellar 跟 Squirrel 跨地域容灾问题一样,解决方案同样也是集群间复制。
北京主集群、上海从:客户端写到北京A 节点,A 正常集群内复制到 B 和 D ,同时到从的 H。H处理完集群间复制写,做集群内复制到I 、K 上。保证最低跨地域带宽占用。集群间两个节点双向复制,达到双向同步异地多活
3.3 Cellar 强一致
做好节点及跨地域容灾后,业务提出更高要求:强一致存储。
之前异步复制数据,故障摘除时,可能故障节点数据没复制,导致丢失。支付场景不容许,业界主流基于 Paxos 或 Raft ,最终选Raft因为:
1)Raft 论文详实,工程化高
2)业界不少成熟Raft 开源实现,可作研发基础,缩短研发周期
Cellar 集群 Raft 复制模式架构图,中心节点做 Raft 组调度,决定每个 Slot 三副本存在哪些节点上
Slot 1 在存储节点 1、2、4 上,Slot 2 在存储节点2、3、4上。
每个 Slot 组成一个 Raft 组,客户端去 Raft Leader 读写。
预分配16384 个 Slot,集群小时,存储节点上有数百上千个 Slot 。这时如每个 Raft 复制组都有自己复制线程、请求和 Log等,资源消耗大,写性能很差
解决:Multi Raft 实现
1)"写性能"不因 Raft 组过多变差:Cellar 把同一节点上所有Raft 复制组写一份 Log,用同一组线程复制,不同 Raft 组间复制包按照目标节点做整合
2)Raft 任何节点宕机,都可选举新主节点,但中心节点仍要管理 Raft 组:
例:集群部分节点几轮宕机恢复,集群节点流量很不均衡,因为保证数据强一致,客户端读写流量又必须发到 Raft Leader
如:Slot 1 存储节点 1、2、4,1 是 Leader挂了, 2 选成了 Leader。1 恢复并重新加入集群,中心节点这时会让 2 把 Leader 还给1 。节点间 Leader 数目均衡
看Cellar 如何保证它端到端高成功率。这里讲三个影响成功率问题:
1、2)Cellar 遇到数据迁移和热点 Key 问题与 Squirrel 一样,但解决方案不一样。因为 Cellar自研,不用考虑与官方版本兼容性,对架构改动更大。
3)慢请求阻塞服务队列导致大面积超时,这是 Cellar 网络、工作多线程模型设计下会遇到不同问题
3.4 Cellar 智能迁移
Cellar 智能迁移架构图
桶的迁移分三个状态:
1、正常(不迁移):Slot 2 从 A 迁到 B节点,A 给2 打快照,把快照全量发到 B 上。
2、迁移时:B 节点回包带回 B 节点状态(引擎的压力、网卡流量、队列长度等)。A 根据 B 状态调整自己迁移速度。像 Squirrel 一样,调整后,迁移速度动态平衡,达最快迁移,同时尽可能小影响业务正常请求
3、迁移完:进入Slot 3 状态,客户端这时可能没更新路由表,当请求到A 节点,会代理到 B 上,B 响应包再返回客户端。同时告诉客户端,更新路由表,解决客户端路由更新延迟造成请求错误。
3.5 Cellar 快慢列队
下图上方:标准线程队列。网络线程池接 收 网络流量 解析出 请求包,把请求放工作队列,工作线程池会从工作队列取 请求来 处理,响应包放回 网络线程池 发出
一批超时请求:往往只有一两个是引擎处理慢导致,大部分因为在队列等待过久。慢只有 1/20
解法:拆线程池、拆队列。网络线程在收到包之后,根据请求特点,读/写,快/慢,分到四个队列,互不影响
快慢怎么分开?Key 数、Value大小、数据结构元素区分
带来问题,线程池从一变四,线程数变四倍?并不是,空闲时帮其它处理。把服务 TP999 延迟降低 86%,大幅降低超时率
3.6 Cellar 热点 Key
Cellar 热点 Key 解决方案的架构图
中心节点加一职责:管理热点数据分布,不只负责正常分布
1) C、D 放热点区域。通过读写看运转:写到A ,处理完,根据实时热点统计结果判断写入Key 是否为热点
2)如是,集群内、热点区域C、D 同时复制。返回时告诉客户端,Key 是热点,客户端缓存Key。这样只对热点数据做扩容,不像 Squirrel 整个 Slot 迁出做扩容。
3)有必要的话,中心节点也可把热点区域放所有节点上,热点读请求就均衡分。
好处:实时热点数据复制,解决类似客户端缓存热点 KV 方案一致性问题
四、发展规划和业界趋势
按照服务、系统、硬件三层阐述。
服务层三点:
1、Redis Gossip 协议优化。Gossip 协议在集群变大后,消息量剧增,Failover 时间变长。达到 TB 级,集群可用性受很大影响,后面做优化
2、已经Cellar 存储节点数据副本间做 Raft 复制,保证强一致,后面在中心点内部也做,不依赖zk仲裁、存元数据储了,架构变简单、可靠
3、Squirrel 和 Cellar 在 SDK 层做整合:虽都是 KV 存储,但 API 和访问协议不同,后端不同存储集群,业务侧用一套 SDK 访问
系统层面
调研并落地Kernel Bypass 技术,像 DPDK、SPDK 网络和硬盘的用户态 IO 技术。绕过内核,轮询访问这些设备,极大提升系统IO。存储作为 IO 密集型服务,性能大幅提升。
硬件层面
1、支持 RDMA 智能网卡能大幅降低网络延迟和提升吞吐;还有像 3D XPoint 这样的闪存技术,如英特尔新AEP 存储,访问延迟接近内存,以后闪存跟内存间的界限变模糊;
2、通过在闪存上加 FPGA 卡,原本CPU 做(数据压缩、解压),下沉到卡上执行,解放 CPU, 降低响应延迟
https://tech.meituan.com/2020/07/01/kv-squirrel-cellar.html