阿里、蚂蚁、晟腾、中科加禾精彩分享 AI 基础设施洞见,现购票可享受 9 折优惠 |AICon 了解详情
写点什么

节约 60%-80% 成本,携程 kvrocks (Redis On SSD) 实践

  • 2020-06-17
  • 本文字数:4252 字

    阅读完需:约 14 分钟

节约60%-80%成本,携程 kvrocks (Redis On SSD) 实践

一、背景

2019 年,随着携程 G2 战略和国际化的推进,有一些大容量的 Redis 集群需要出海对海外客户提供服务,相比私有云的单 GB 成本,公有云上的 Redis 要贵 10 倍左右,这迫切需要我们寻找一种能替代 Redis 的廉价方案部署在海外,我们开始着手调研 Redis On SSD 的可行性。

二、调研和选型

携程大部分 Redis 数据是通过 xpipe 同步到海外(图 1),而 xpipe 是实现了 Redis 复制协议的伪 slave, 为了让海外基于 SSD 存储的替代 Redis 的方案能够顺利落地,需要兼容 Redis 的协议,Redis 的协议基本分为两大块:


1)面向客户端的协议,如 ping/set/get 等客户端的命令。


2)复制协议,slave 同步 master 时需要用到。



图 1


我们调研了目前绝大部分 Redis 的替代方案,如 Redislabs 的 Redis On Flash(https://redislabs.com/lp/redis-enterprise-flash/), 360 的 pika(https://github.com/Qihoo360/pika)和美图的 kvrocks(https://github.com/bitleak/kvrocks),其中 Redis On Flash 是商业化的产品,无开源代码,pika 市面上使用的公司比较多,但缺点也很明显:


1)面向客户端的协议是 Redis 的二进制协议,而面向复制的却是基于 google 的 protobuf 格式,语义层面有割裂感。


2)复制是基于 Rsync 的多进程模式,这种复制模式比较重,出现问题也不好定位。


3)代码风格比较乱,此外网络类库也用的是 360 自家的 pink,二次开发比较困难。


而 kvrocks 很好的避免了 pika 的这些问题,语义和复制上与 Redis 原生的更加接近,缺点是刚刚开源,几乎无任何公司来使用 kvrocks,经过权衡,我们发现 kvrocks 的整体框架和代码比较好把握,我们还是决定基于 kvrocks 做二次开发。


为什么要二次开发呢?因为面向客户端的协议 pika/kvrocks 可以达到 90%以上的 Redis 兼容,但复制协议都不兼容,究其原因在于,无论 pika 还是 kvrocks,其底层的存储引擎都是 rocksdb,而 rocksdb 是基于磁盘的 KV 存储方案,数据已经落盘成文件的无需再像 Redis 那样复制时将 Master 内存的数据保存到文件中再发送到 slave 端,直接传输文件更高效。为了让业务和中间件少改动,我们基于 kvrocks 进行二次开发,用来支持 Redis 的 SYNC/PSYNC 协议,也就等于支持了 xpipe,最终的同步模式也就如图 2 所示。



图 2

三、二次开发

二次开发前,必须厘清,一个 kvrocks 实例要成为一个 Redis 的 slave,有以下几个步骤:


1)执行 slaveof 后的 Redis slave 状态机模拟,如下图 3 所示。


2)对于全量同步的逻辑,也就是图三中的 REPL_STATE_TRANSFER 状态,会接受来自 master 的 RDB 文件,接受完成后,需要解析 RDB。


3)完成 RDB 文件解析后,模仿正常客户端命令写入 Rocksdb 中。


4) 进入 CommandPropogate 阶段后,死循环接受 Master 传来的增量命令,每一秒 ACK 一次当前的 offset。



图 3


此外,还需要区分 kvrocks 复制 kvrocks 和 kvrocks 复制 Redis,得益于 kvrocks 良好的代码风格,其 Replication 模块已经实现了一个 kvrocks 复制 kvrocks 的状态机,其中 psync_steps_和 fullsync_steps_表示 kvrocks 复制 kvrocks 的增量同步和全量同步,这样我们的思路就很清晰了:


1)将目前的 slaveof 的语义改为复制 Redis,并将 kvrocks 自身的复制重命名为 kslaveof,这样在输入端,我们就知道客户端需要执行的是复制 kvrocks 还是复制 Redis,当然为了对哨兵透明,也可以统一 slaveof,而在 slaveof 后续步骤出错时根据返回值来判断是否回退到起始状态,执行另外一种复制逻辑。


2)kvrocks 的 server 类添加 Redis 复制的一些特有标识,比如 repl_offset,repl_id 等,并添加一个字段 slave_mode 来区分当前是作为 Redis/kvrocks 的 slave。


3)Replication 复制类添加一个类似的状态机 redis_steps_,在此状态机中完成上面图 3 状态切换的函数封装。最终简化成以下的逻辑:



图 4


完成了上面的流程,我们就获得了一个同步 Redis 的 kvrocks slave,解析 master 传播过来的 RDB 文件,对于每个 key,遍历其是否过期,然后根据类型 (string,hash,set,zset) 选择对应的插入命令,将其导入到 rocksdb 中。RDB 解析完成后,会进入 Command Propogate 阶段,而对于 PSYNC 的支持,只需要保存 master 的 repli_id 和 offset,在传送 RDB 之前根据 master 返回是否是+CONTINUE 来区分是增量同步还是全量同步。


如果是增量则直接进入 Command Propogate 阶段,此时只需要循环接受 master 传过来的命令,累加 repl_offset,并每一秒 ack 一次当前的 repl_offset,kvrocks 就可以一直 online 并且对外提供服务,而对于 master/客户端/中间件来说,它跟真正的 Redis 无任何差别。


除了上面的这些步骤外,为了监控需要,我们完善了一些 Redis 上支持的,但 kvrocks 暂时还没支持或无法支持的命令或统计信息,如 role,instantaneous_ops_per_sec 等,这里就不再一一赘述。

3.1 数据

我们经过将近 100 个版本和线上 2 个月的生产测试,总结的数据主要分为以下几个方面(除了从 master 同步的命令外,面向客户端的基本都是读操作,大部分操作为 hget/get, value<1024byte,单个实例 QPS<20K):


1)kvrocks 和普通 Redis 的区别;


2)线程数和响应时间的关系;


3)kvrocks 跑在傲腾 SSD 和普通 SSD 上的区别;


4)kvrocks 适用场景;


5)成本节约多少?


kvrocks VS Redis



图 5


从图 5 上我们可以看到,基于 SSD 的 kvrocks 和基于内存的 Redis 性能没有明显差别,而且这是基于 rocksdb 的配置比较低的情况(4 线程处理 client 命令,1 线程复制,metadata/subkey 的 block_cache_size 为 128M,write_buffer_size 64M,wal_size 2G)。


线程数和响应时间



图 6


我们固定其他参数,只开放处理 client 命令的线程,图 6 中是 4 线程和 1 线程的对比,从图上来看,这个差距还是比较明显的,但是否线程数越多越好?也不是,如图 7 所示,4 线程和 8 线程的平均响应时间无任何差别,因此实际上线上版本我们固定为 4 线程处理 client 命令。



图 7


傲腾 SSD VS 普通 SSD


我们除了在普通 SSD 上测试,还测试了傲腾 SSD 的场景,这种情况下,傲腾 SSD 是用来当硬盘而不是当内存用。从结果来看,傲腾 SSD 相比普通 SSD 的优势是全方位的领先,首先用 redis-benchmark 来测试 SET 的性能,傲腾的 100%响应时间约为普通 SSD 的 1/3(图 8,9),而 QPS 却是 3 倍(图 10,11)。



图 8



图 9



图 10



图 11


kvrock 的实际场景也证实了压测的数据(图 12),延迟和抖动方面傲腾 SSD 有明显的优势。



图 12


四线程的 kvrocks 跑在傲腾上甚至比 Redis 的性能更要好,这点也比较出乎我们的意料,如图 13 所示:



图 13


随着下半年 PCI-4.0 的傲腾量产和 kvrocks 自身固有的落盘优势,重启实例不会丢失数据和全量同步,完全可以畅想下 kvrocks 未来在傲腾上的应用场景。


kvrocks 适用场景


上面这些数据说明了 kvrocks 代替 Redis 的可行性,但并不是所有场景都合适,主要原因在于 rocksdb 自身的一些限制,这里可以认为是将 Redis 的内存密集型转换成了 CPU/IO 密集型,尤其是 CPU(图 14,15),在写入量大的情况下相比 Redis 有 7-8 倍的提升。


这主要是由于 rocksdb 为了防止空间放大和读放大,定时会 compaction,而写入的越频繁,compaction 也就越频繁并且单次 compaction 的 CPU 就越高,所以就形成了图 15 这种脉冲式的波峰。



图 14



图 15


从我们测试的经验来看,单个实例 QPS<1 万情况下,用 kvrocks 替换 Redis 是比较合适的,如果 QPS 过高,会导致 CPU 过高,我们甚至无法选择到合适的宿主机来存放这种类型的实例,因为这时候 CPU 内存的配比是 2:1 或者更高的关系。


成本能节约多少


这实际上需要在 CPU/内存/磁盘中做各种 tradeoff,我们需要在保证响应延迟的情况下尽可能地降低 CPU/内存的使用率。以我们线上某实际的集群为例,经过 rocksdb 各种参数调整后,该集群单个 Redis 实例所用内存为 6G,而这些数据全部跑在 kvrocks 中,大概 CPU 为 100%,内存为 1G 左右如图 16,17 所示。按这样的关系换算我们之前选用的 Redis 宿主机机型和计划选用的 kvrocks 宿主机机型,用 kvrocks 大概能将成本节约 63%,并且实例越大,节省越多,整体能节约 60-80%的成本。



图 16



图 17

四、一些坑

二次开发过程中,遇到各种奇怪的坑,有些是为了支持 Redis 复制协议或者跑在容器上才出现的,有些是 kvrocks 固有的。


1)编译时 jemalloc 必须指定–with-jemalloc-prefix=je_,否则无法在容器中运行 ,具体可见https://github.com/bitleak/kvrocks/issues/54


2)在新的 CPU 机器上编译后无法在老的机器上运行,会报非法指令错误,这个现象在 pika 上同样存在,考虑都使用了 Rocksdb,启用 snappy 压缩,高度怀疑 snappy 压缩在高级 CPU 上采用某些指令集有关。


3)rocksdb 在某些虚拟机虚拟出来的文件系统上无法工作,这个现象在 pika 上同样存在,猜测是跟 linux 的底层系统调用没有实现有关系(根据 pika 开发者反馈是 access 系统调用),具体可见https://github.com/bitleak/kvrocks/issues/56 ,切换到 xfs 文件系统解决。


4)对于 setbit 操作,Redis 认为 value 是个 string,但 kvrocks 认为是个 bitmap,所以如果一个 setbit 操作的 string 在全量同步阶段被同步到 kvrocks 中,再有命令传播的 setbit/getbit 操作的话,kvrocks 会报类型不匹配的错误,该问题已经提交给官方,官方会试图将这两种类型统一。而作为暂时的解决方案,setbit 操作的 key 加上指定的前缀比如"bit_" ,这样程序就认识到此 string 为 bitmap 类型,而选择对应的数据导入方式,而如果 kvrocks 复制 kvrocks 的话则不会有这种问题。


5)兼容 Redis 的时发现有个断错误,多抓取 core 文件发现是二次开发的代码导致,多线程访问了 libevent 的同一个 evbuffer(图 18),同时读写操作同一个 evbuffer 会导致无法预期的错误,解决方法是 evbuffer 加锁。



图 18


6)kvrocks pub/sub 方面的一个死锁,堆栈如图 19,提给官方后很快修复,具体可见 https://github.com/bitleak/kvrocks/issues/68



图 19

五、未来展望

目前我们已经将公有云上 50%+的实例都替换成为了 kvrocks,未来我们计划将公有云上所有可以替换的 Redis 都替换成 kvrocks 来降低成本,除此之外,支持 Redis slaveof kvrocks,之后再考虑开源。


作者介绍


布莱德,携程技术专家,负责 Redis 和 Mongodb 的容器化和服务化工作,喜欢深入分析系统疑难杂症。


向晨,携程资深数据库工程师,专注于数据库和缓存智能运维工作。


本文转载自公众号携程技术(ID:ctriptech)。


原文链接


https://mp.weixin.qq.com/s/zUjXCGOFZWoKrVRH9aJUDw


2020-06-17 10:004723

评论

发布
暂无评论
发现更多内容

2024杭州国际智慧城市与安防产品展览会

AIOTE智博会

智慧城市展 安防展

提示找不到某些组件?

矩视智能

深度学习

定制开发软件的步骤

Geek_16d138

软件定制 app定制开发 软件开发定制

前端安全沙箱技术会不会成为安全的标配?

Onegun

小程序 小程序容器 安全沙箱

PyTorch从精通到入门05:基于ResNet迁移学习和微调,实现图像分类

王玉川

神经网络 深度学习 迁移学习 图像分类 resnet

PyTorch从精通到入门06:基于LSTM实现文本分类

王玉川

神经网络 深度学习 RNN 文本分类 LSTM

智能低代码洪流涌动程序员节,华为云Astro触发1024的乘法效应!

YG科技

C++高并发异步定时器的实现

1412

开源 定时器 异步 workflow C++

阿里Java一面,难度适中!(下篇)

王磊

Java 面试

私人服务器有什么用?

Geek_f19a80

服务器

观测云产品更新 | 数据转发、监控器告警策略等优化

观测云

监控告警 日志备份

数智化竞价, 企业数智化助力企业实现增收增利

用友BIP

降本增效 数智营销

MySQL 数据库表格创建、数据插入及获取插入的 ID:Python 教程

小万哥

Python 程序员 软件 后端 开发

亚马逊云EC2哪些端口默认不能用

孤虹

亚马逊云 EC2 Amazon EC2 云服务器

20行JS代码实现屏幕录制

南城FE

JavaScript 前端 浏览器 屏幕录制

凡泰极客亮相香港金融科技周,投身大湾区数字化建设

FinClip

LeetCode题解:171. Excel 表列序号,哈希表,TypeScript,详细注释

Lee Chen

JavaScript LeetCode

深入浅出了解华为云API网关的Gzip功能

YG科技

强大好用的shell:shell的工作原理

小齐写代码

质量诊所案例分享之Junior QA常见问题诊断

QE_LAB

质量 质量保障

使用VUE开发有什么好处呢?

Onegun

Vue 前端框架

为什么在财务共享中心建设初期人员不降反增?

用友BIP

财务共享

Linux awk命令

二哈侠

华为云全新上线Serverless应用中心,支持一键构建文生图应用

YG科技

亚马逊云科技海外服务器初体验

三掌柜

亚马逊云科技

节约60%-80%成本,携程 kvrocks (Redis On SSD) 实践_开源_布莱德_InfoQ精选文章