极光笔记|百亿级 KV 存储在极光的运维实践之路

前言
极光从某种意义上讲,是一家数据公司。在整个公司的技术运营体系中,需要存储大量的 KV 数据。根据数据量、KV 结构特点、数据更新频率、数据冷热、读写请求量和比例等因素,在极光逐步形成了 CouchBase、Redis 和 Pika 三种不同的 KV 解决方案。
以下我们主要是从大规模数据量/请求量场景下碰到的运维难题,以及服务的性能、可扩容性、数据备份与安全、运维保障工具等方面介绍一下极光在使用以上 KV 组件的实践经验。
一、CouchBase 实践
极光 CouchBase 规模

CouchBase 当前总使用量大约在 6.5T 左右;单个集群最大 25 台(16 核 128G 内存)。
CouchBase 是一个非关系型数据库,它实际上是由 couchdb+membase 组成,所以它既能像 couchdb 那样存储 json 文档,也能像 membase 那样高速存储键值对。
在极光我们主要用它做 KV 存储,主要有以下几个特点:
速度快
由于是放在内存中的数据库,所有的读写操作都是直接操作内存,因此速度非常快。极光数据量最大的集群规模约 25 台机器(16 核 128G),可以提供 1.5TB 的可用内存。读写压力最大的集群,大概是 8 台机器,但是需要抗住 100 万的 QPS,单机可以抗住 10 万以上的 QPS,性能相当强悍了。

与官网宣称的性能与节点数的线性关系不同,实际使用中发现,超过 40 个节点的集群,在扩缩容、故障节点替换、集群稳定性方面存在明显的问题,在扩缩容、替换节点的时候,集群容易进入假死的状态,在控制台上没有任何反应,客户端断断续续出现超时或者错误的返回结果。这个过程无法快速自动恢复,在极光类似的故障最长的一次持续了 3 个多小时,最终我们是停掉了业务才修复的。
另外有一次故障,发现主要原因 CouchBase 内存占用只增不减,导致机器内存爆满,最终被 oom。最终通过进行集群拆分暂时解决了问题,新集群也同步使用更新的版本。


高可用
主要有两个方面的高可用:一个是它自带集群方案,支持多副本模式,我们一般是选择 2 副本模式,类似一主一备的方案,这样可以确保没有单点故障的问题;
另一个是它自带持久化方案,可以设置定时把数据异步写到文件系统上,所以在一般情况下单一节点的宕机,对集群的可用性和性能影响有限,但是一次性故障超过 2 个节点,就有出现数据丢失的可能性了。

配置使用方便
CouchBase 安装后自带 web 管理台,可以在管理台上对集群、桶、索引、搜索等进行管理和操作,大大简化运维的工作。这也是一般开源软件所普遍不具备的能力,商业化的产品对于用户体验确实做得很好。

当然,CouchBase 也有一些明显的缺点:
1、只能提供简单 KV 存储模型,不支持复杂数据结构(value 支持 json 格式)
2、为了提升查询效率,KV 中的 Key 全部都缓存在内存,需要消耗大量内存,特别是每一对 KV 所附加的 MetaData(一个 KV 存储至少消耗 56Bytes 的 MetaData)同样缓存在内存中。在极光的实践过程中,经常碰到 MetaData 超过 50%内存占用的情况,对内存容量提出高要求,也导致成本较高。

3、本身是闭源产品,文档较少,出现异常故障处理手段有限,存在一定的风险;我们碰到过好几次的集群节点假死,还有数据量大的集群无法进行备份的情况,没有找到任何有效的方案。那么对于核心、关键数据,放在 CouchBase 上进行存储是不可靠的,这块是需要根据业务场景来判断的,目前极光使用 CouchBase 的数据都是中间层数据,一旦发生数据丢失,都可以从其他渠道恢复回来。
数据备份问题:
对于数据量达到一定规模,使用 CouchBase 自带的备份工具进行备份时就会出现备份失败的情况。与 CouchBase 官方沟通也没有回复。


针对 CouchBase 大规模集群下的 rebalance 会存在假死的问题,后面我们把大集群拆解成了数个小集群,实现了压力的分散和运维管理的分散,不过需要在业务侧做数据的分片处理,需要业务介入,对业务方存在一定的侵入。
监控和告警生态相对比较成熟,通过对应的 Exporter 获取数据上传到 Prometheus,整体比较简单方便

我们设置了以下的主要告警规则,便于及时发现问题:

1、bucket 内存使用超过 85%
2、CouchBase 集群节点 CPU 使用超过 90%
3、xdcr 同步失败
4、CouchBase 节点发生异常/重启
二、Redis 实践
Redis 是知名的开源 KV 存储,在业界使用极为广泛,极光也在大量使用 Redis。
极光 Redis 规模
当前极光 Redis 资源使用量大约在 30TB 左右,实例总数大概在 7K

生态丰富
Redis 因本身的知名度,实践案例非常多,网上的文章和教程十分丰富,有一个很好的生态,运维和研发人员也非常熟悉。这块对比 CouchBase 和 Pika 来说,真的非常重要,不但可以节省大量的沟通成本,还可以直接复用很多的成熟解决方案,排查故障和优化工作也更加简便。
在选择基础组件这块,成熟、通用、生态丰富真的非常重要。
高性能
同样因为全内存操作,Redis 表现出相当高的性能,我们实际使用中单 Redis 实例峰值性能可以到达 5 万 QPS(这是公司的峰值标准,达到该值我们就认为已经达到瓶颈了,但实际性能测试要高于此),如果按照单机 8 个实例,理论上可以达到 40 万 QPS,这个数据相当惊人了。当然,由于 redis 的单进程+阻塞模型,碰到慢指令就会导致延迟整体上涨,这块我们在应用架构设计和运维方面需要特别考虑,比如在业务低峰期再进行一些高延迟的操作。
redis 性能测试:

主从模式下,我们一般都使用哨兵模式,这个一般来讲不会有什么问题;但是有一次我们怀着提高可用性的目的,把一套哨兵从 3 节点扩容到 5 节点,之后发现该哨兵监管的 redis 实例出现大量的延迟告警,后来排查发现和扩容到 5 节点密切相关,进一步排查 slowlog 发现是哨兵发送的 INFO 指令导致 redis 实例响应延迟增加。在这块,要特别注意哨兵的探活导致的资源消耗,在高 QPS 压力下,任何一点新增的压力都十分敏感。
自带集群模式
Redis 自带了 Redis-Cluster 架构,在一定程度上解决了超大规模数据集的存储问题。
Redis cluster 性能测试:

不过,由于其无中心的架构模型,在大规模集群下运维和数据迁移较为困难。在极光,我们当前最大的 Redis-Cluster 集群达到 244 个主从节点,总共超过 800G 的数据量,峰值 QPS 可以达到 8M(八百万 QPS),压力相当大,带来的运维压力也是相当大。
首先:扩容比较困难,扩容导致的 slot 迁移在大 QPS 压力下,导致客户端收到大量 MOVED 返回,最终导致业务端大量报错;
其次:扩容时间非常长,从 244 个节点扩容到 272 个节点,花费了我们一个星期时间,浪费了我们很多人力资源。


逐渐地我们形成了一套 Redis-Cluster 的应用最佳实践
首先、严格控制 Redis-Cluster 集群规模,主节点不超过 100 个。
其次、前期我们通过在业务端做数据切割,将数据分布到不同的 Redis-Cluster 集群;逐渐地公司通过自研 JCache 组件实现宏集群,将多个原生的 Redis-Cluster 组合成一个宏集群;在 JCache 做基于 Key 规则的 hash,相当于多了一层代理。当然,这样也带来一些新的问题需要解决,比如后端 Redis-Cluster 的分裂和数据重新分片的问题,也增加了 JCache 的复杂度。
JCache 宏集群方案:

监控同样是使用 Exporter 收集数据到 Prometheus,这块生态十分完善,直接复用现成的方案即可。


我们针对 Redis 设置了如下的告警规则:
1、内存使用率告警(每个 redis 应用自定义,默认 90%)
2、客户端连接数告警(默认 2000)
3、redis 实例 down 告警
4、哨兵发生主从切换告警
5、应用整体 qps 阈值告警
6、特定 redis key 监控

性能优化:
1、关闭 THP
2、高危命令屏蔽(keys/flushall/flushdb 等)
3、部分数据频繁过期的 Redis 应用,通过调整 hz 参数,加快过期 key 清理,尽快回收内存,有一些从默认每秒清理 10 次提高到 100 次,但是在大 QPS 请求下,会有性能影响
4、对部分内存需求高,性能要求稍低的 Redis 应用,通过调整数据的底层存储结构,来节省内存,如调整 hash-max-ziplist-entries/hash-max-ziplist-value
三、Pika 实践
Pika 是国内开源的一套兼容 Redis 协议的 KV 存储,底层基于 RocksDB 存储引擎,支持多线程,属于磁盘 KV 存储系统,比较适用于数据量特别大(例如 TB 级别)但是 QPS 要求不是特别高的场景。
极光 Pika 规模

Pika 当前数据量大约在 160T 左右。
性能较高
从我们使用 NVMe SSD 作为存储介质实测下来,单机(8 核 32G)峰值达到 18W QPS 是没有问题的,实际使用场景下峰值 QPS 可以达到 10W 而不影响业务。
当前公司自建 IDC 使用 NVMe SSD 最大的 ops 在 7.6W 左右

生态不算成熟
国内开源软件,受众不算大,软件不是特别成熟,功能迭代变化较快,从 2.X 版本快速迭代到 3.X 版本,根据社区反馈而做了很多改进,目前我们基本稳定使用 3.2.X 的版本。
注:新应用基本都是基于 3.2.x 版本,还有部分遗留的历史应用由于数据比较重要,暂时没有升级
最近一年因为项目变更,Pika 似乎捐献给了国内某个开源基金会,不再由 360 公司继续负责维护,从 QQ 群的反馈来看,核心开发人员似乎越来越少,项目前景有一点暗淡。这也是很多小众开源软件的常见问题,软件本身很容易因为公司政策或者某些核心人员的变动而受到较大影响。

Pika 最近一次发版本还是在 2020 年 12 月,已经快过去一年时间了。
代理层
因为 Pika 只有主从模式,而我们的数据量动不动就好几个 T,QPS 数十万,所以我们在实际使用时需要在前面加了一层代理层来做 hash 分片工作。目前单个实例最大达到了 2T,实际 QPS 达到 10 万,读写实例比例 1:3,满足写少读多的场景。

标配 SSD
无论从官方建议还是实际使用,Pika 的磁盘我们都标配 NVMe SSD 磁盘,相比普通 SSD 磁盘,提供更高的 IOPS 和吞吐能力,更低的读写延迟。Pika 生产环境,NVMe SSD 我们峰值 IOPS 可以达到 5 万,而普通的 SAS SSD 磁盘也就 1 万,性能提升还是很明显的,对比价格的区别,性能提升非常多,更不用说相比内存了。

串联主从问题
Redis 可以支持和实现主-从-从的串联关系,我们在使用 Pika 的时候也做过类似的部署,结果出现了严重问题,导致我们丢失了数据。Pika 在串联主从这种模式下对于数据同步状态的管理十分简单粗暴,只能看到 slave 和 master 已建立连接,但是数据是否同步完毕、是否有数据缺失都需要应用层去处理。这也是后面我们研究代码得出来的,一方面是文档的缺失,另一方面也说明了在使用开源软件时不要理所当然地认为是怎么样,而是要经过严格的确认和测试,否则带来的代价是非常大的。
注:Pika 从 3.2.X 版本后就不再支持串联主从的设置了,所有 Slave 都必须从 Master 同步数据,会对 Master 节点造成一定的读压力。
监控和告警
监控信息还是可以通过 Exporter 搜集并上传到 Prometheus,比较简单清晰


Pika 的告警规则设置如下:
1、Pika 实例进程告警
2、Pika 连接数告警
3、哨兵主从切换告警
4、JCache 慢查询告警
5、Pika master 写数据失败告警
性能参数:
1、auto-compact 自动压缩参数,针对存在大量过期数据的应用
2、root-connection-num 保证客户端把连接数占满后仍然能通过 pika 本地连接上
3、压缩算法改为 lz4,性能好,压缩比高
四、后续规划
极光目前的 KV 存储经历了这么多年的演进,满足当前需求是足够的,但是仍然存在扩容不方便、不平滑,大规模集群性能瓶颈、性价比等问题;未来将重点投入在对业务的赋能上,将 KV 存储做成一套适应性更强、更灵活的存储服务,可能考虑引入类似存算分离、冷热分层等技术,在扩缩容、性能优化、性价比等维度上更上一层楼。
评论