美文网首页
(14)美团万亿级 KV 存储架构与实践

(14)美团万亿级 KV 存储架构与实践

作者: hedgehog1112 | 来源:发表于2021-06-25 00:10 被阅读0次

    一、美团点评 KV 存储发展历程

    二、内存 KV Squirrel 架构和实践

    2.1 Squirrel 节点容灾    2.2 跨地域容灾    2.3 智能迁移   2.4 持久化重构  2.5 热点 Key

    三、持久化 KV Cellar 架构和实践

    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

    相关文章

      网友评论

          本文标题:(14)美团万亿级 KV 存储架构与实践

          本文链接:https://www.haomeiwen.com/subject/czaidltx.html