本文来自OPPO互联网技术团队,如需要转载,请注明出处及作者。

Parker 是 OPPO 互联网自研的一个基于 RocksDB 的分布式 KV 存储系统,它是一款类 Redis 的存储系统,主要解决的是用户使用 Redis 遇到的内存超限启动恢复时间长,一主多从代价大,硬件成本昂贵,无法存储海量数据等问题。

1. Parker简介

Parker 具有如下特性:支持海量存储:单个集群存储容量可达数百 TB,线上单集群 TPS 峰值可达百万级。

支持水平扩展:存储容量、读写性能都可通过增加机器的方式水平扩展。通过数据分片 (slot) 的方式,将不同的分片散落在不同的节点,保证存储容量和读写性能的可扩展 。

服务高可用:每个数据分片包含两副本,主备副本可在秒级内切换,保证单个数据分片的读写服务的高可用。

兼容部分 Redis 协议:Parker 对外通信协议兼容 Reids Cluster 协议,支持 String 和 Hash 类型及对应的操作函数,即用户可以通过 Redis 客户端来读取 Parker 中的数据。

支持 TTL 特性:一条数据写入后,超过 TTL 指定的时间戳,就过期淘汰,对用户不可见。这就需要 Parker 能及时删除过期数据,回收过期数据占用的磁盘空间。

2. 遇到问题

我们在容量为 5TB 的存储服务器上部署了 8 个 Parker 实例,写入的数据设置为 3 天过期,预计在数据写入速度和过期回收速度平衡的情况下,单实例会保持有 300GB 的存储占用。

实际上 Parker 运行五天后发现磁盘使用率不断攀升,开始写入的前三天,磁盘使用率直线上升,而后虽然上升速度有所减缓,但是总体上升速度还是比较快,与预期效果有严重偏差。虽然数据在 TTL 过期之后无法读取到,但是实际上磁盘空间并没有得到及时回收,这导致磁盘使用率居高不下。

3. 原因分析

3.1 RocksDB 原理

Parker 的底层存储引擎使用的是 RocksDB ,RocksDB 底层数据存储是 LSM 架构。数据分为不同的层,默认是 7 层,compaction styles 默认选择 leveled compaction。如下图所示:

用户写入数据到 RocksDB 时,会先将数据写入到一个 Memtable 中,当一个 Memtable 写满了之后,就会变成 immutable 的 Memtable。RocksDB 在后台会通过一个 flush 线程将这个 Memtableflush 到磁盘,生成一个 Sorted String Table (SST) 文件,放在 Level 0 层。当 Level 0 层的 SST 文件个数超过阈值之后,就会通过 compaction 策略将其合并到 Level 1 层,以此类推。

如果没有 Compaction,那么写入是非常快的,但这样会造成读性能降低,同样也会造成很严重的空间放大问题。为了平衡写入、读取、空间三者的关系,RocksDB 会在后台执行compaction,将不同Level 的 SST 进行合并。

3.2 TTL实现原理

RocksDB 中有一个 CompactionFilter 功能,该功能就是 RocksDB 在 compaction 每一条数据时,都会调用一个 Filter 函数 ,这是一个钩子函数,可以用户自定义。TTL 的实现方法就是在 Filter 函数中实现 TTL 过期删除逻辑,具体实现如下所述:往 Parker 中写入数据时,我们在数据的 value 的尾部添加四个字节的 TTL,以 String 类型举例,其具体的数据编码格式如下图所示:在 Filter 函数中,我们实现了这样的逻辑:取出数据尾部的 TTL,并与当前时间进行比较,如果TTL 小于当前时间的数据,就认为该数据已过期,不再合并到下一层文件中,从而达到删除数据的目的。

3.3 问题分析

Parker 中配置 RocksDB 每一层存储的容量上限如下:

仔细梳理 RocksDB 的 compaction 原理:RocksDB 触发 leveled compaction 的条件是该层文件大小或个数超过上限,例如 Level 2 的文件总大小超过 10GB,则会触发 compaction,从而调用compaction fliter,过滤掉 TTL 过期的数据。

基于我们遇到的问题,结合 RocksDB 的原理进行分析,我们发现:在我们的场景中会有 380GB 左右的数据会落在 Level 4,而这些数据虽然大部分已经过期,但由于这一层的数据一直没能达到该 Level 容量的上限 1TB,所以未触发 compaction filter,所以造成过期数据没有被删除,磁盘空间回收不了。

4. 解决方案

简单的更改 RocksDB 的配置,使得每一层存储的容量上限变小,可以解决上述问题,但是这种方案并不具备普适性。不同的使用场景,不同的存储数据量,存储数据量一旦改变就需要重启服务来更改RocksDB 的配置,这是业务不可接受。

从宏观上分析,回收过期数据磁盘空间的方案主要有两类:业务实现过期删除逻辑,主动删除过期数据。

依赖 RocksDB 的 compaction 机制,在触发 compaction 的时候对过期数据进行删除。

为了实现快速回收磁盘空间删除过期数据,我们结合 RocksDB 的原理,梳理出了以下的几种方案:

4.1 业务实现删除逻辑

通过 Parker 自身逻辑,来主动删除过期数据。业内有赞 KV 就是采用类似的方案。具体做法是,保持现有存储列族不变,另外增加一个列族,专门存储 key 的 TTL,然后通过一个 goroutine 根据当前时间戳,按照过期数据删除策略可以是定时触发,例如凌晨1点,或者每个一段时间触发等。该列族中 key 的编码规则如下:key:时间戳 + key 的类型 + key 值

value:存储一个字节,代表不同数据类型

这个方案优点是回收速度快且回收时间可控,但是缺点就是实现复杂,极端情况下会降低 50% 的TPS。

4.2 OpenDbWithTTL 方案

这个是 RocksDB 本身支持的一种数据过期淘汰方案,该方案是通过特定的 API 打开 DB,对写入该 DB 的全部 key 都遵循一个 TTL 过期策略,例如 TTL 为 3 天,那么写入该 DB 的 key 都会在写入的三天后自动过期。该方案底层也是通过 compaction filter 实现的,也就是说过期数据虽然对用户不可见,但是磁盘空间并不会及时回收,另外该方案不灵活,无法针对每一条 key 设置 TTL。

4.3 主动触发 RocksDB 的 compaction

目前空间无法快速回收的根本原因就是数据堆积在某一层,而该层没有触发 compaction,那么我们可以手动调用 RocksDB 的 CompactionRange 函数,来触发 compaction filter,达到快速回收磁盘空间的目的。但是主动调用 CompactionRange 会导致 RocksDB 自身的 compaction 暂停,这会触发 Write Stall,造成非常严重的后果,所有这种方案也不是非常完美。

4.4 Periodic compaction + dynamic compaction

Periodic compaction 的主要原理是增加一个 periodic_compaction_seconds 参数,并记录每个SST 文件的创建时间,每隔 periodic_compaction_seconds 秒,主动对这个 SST 文件进行 compaction 操作,从而回收沉底的 SST 文件;而 dynamic compaction 则是通过设置level_compaction_dynamic_level_bytes 为 true,进行动态合并,而不是按 level 的顺序合并到下一层,这使得 compaction 更加频繁。这个方案实现方式比较优雅,无须改动现有代码结构,只需要改动一些配置即可。

对以上几个方案进行比较,方案 4 实现较为完美,对实现逻辑改动较小,于是我们对方案 4 进行了实验验证。

5. 实验验证

5.1 机器配置

5.2 RocksDB 新增配置RocksDB 版本:6.4.6

level_compaction_dynamic_level_bytes = true;

periodic_compaction_seconds=3600;

5.3 测试结果

我们向 Parker KV 存储写入字符串数据:总写入数量:30000000000(三百亿)条数据,单条数据170Byte 左右。每条数据的过期时间为写入时间之后的 10800s,即写入 3 小时后过期,写入速度为51MB/s,写入总量预估为 5.32T。该机器磁盘占用情况如下:

5.4 结果分析前 3 个小时因为写入的数据都没有过期,所以磁盘的使用率几乎是线性增长的,大概以 51 MB/s(即约180GB/h) 的速度增加,因为 RocksDB 本身对数据有进行压缩,所以磁盘大概增长了520GB。

第 4 个小时开始,数据开始有过期了,但是因为触发 periodic compaction 的 SST 文件并不多,而且开启了 dynamic_level,本身 compaction 就相对比较频繁一些,未更新时间大于 1h 的 SST文件比较少,另外即使触发了 periodic compaction,需要删除的 key 也比较少,所以在这 1 个小时内,磁盘的增长率有明显下降,但是总体上磁盘的使用还是在增长的,写入的磁盘空间比回收的空间要多。

从第 4 到第 5 个小时这段时间内,过期的 key 开始变多,而且触发 periodic compaction 的 SST文件也增加,这个时候,写入的磁盘空间与删除的磁盘空间大致能达到平衡,磁盘使用增长率大幅度下降,接近于 0。

理想情况下,应该是从第 5 个小时之后,磁盘的使用率应该稳定在某个水平线左右上下波动,当然在第 5 到第 9 小时,这段区间内,基本就是符合理想情况。

从图中可以看到,在第 9 个小时即 20:00 的时候,磁盘使用率骤然下降,通过查看 RocksDB 日志分析发现,在这段时间内有大量的 SST 文件触发 periodic compaction,然后整个被删除,level 6 的 SST 文件数量锐减了接近 500 个。因为这个时候,大多数文件的 key 都是全部过期的,一旦触发 compaction 就全部回收,所以这段时间磁盘回收大于写入的量。

在磁盘使用率骤降之后,触发 periodic compaction 的 SST 文件也会相对减少,导致磁盘使用率又有了一小段的上升,从图中可以看到第 10 和第 11 个小时,磁盘使用率的增长量又增大了。

在之后的很长一段时间内,磁盘的使用率基本维持平衡。这段时间内可以认为有 3 个小时的写入量没有过期,这个没有过期的量约为 600GB,而这个时候磁盘的使用达到了 900GB,也就是说接近 1.5 倍的空间放大。

在 11/7 11:00 到 13:00 这段时间,我们降低了写入速度,整个吞吐量从 58MB/s 下降到10MB/s,可以看到图中磁盘的使用率在这段时间也是骤然下降,也就是说降低了写入的速度,过期的量没有减小,磁盘回收的速度不变,回收速度大于写入速度,磁盘使用率下降。

在 11/7 11:50 的时候停止写入数据,Parker 的写入吞吐量降为 0,而这个时候 Parker 还存在大量过期数据,这个时候磁盘使用率明显在下降,一直降到某个水平线。

按照最理想的情况应该是回收到最开始写入的水平线,然而理想只能是理想,最后磁盘使用率稳定在 10.239%,比最开始的 6.617% 多了 3.622%,也就是有 218GB 的磁盘空间还没有被回收,这部分数据会在重新启动写入的时候被回收。

6. 总结

目前来看,通过 periodic compaction + TTL 过期来回收磁盘空间的方案是可行的,并且我们总结出如下规律:

通过监控发现 periodic compaction 对 CPU 负载有一定影响,也就是说periodic_compaction_seconds 时间越短,CPU 负载越高。其实这也容易理解:RocksDB 更加积极主动的进行 SST 文件的 compaction,必然会消耗更多的 CPU 资源,建议将这个参数调整为12 小时。

从理论上推导,整个 RocksDB 中会有约 periodic_compaction_seconds 时间长度的过期数据延迟回收,从而造成一定的空间放大,所以部署的时需预留有一定的空闲磁盘空间,建议预留 30%的冗余存储空间。

7. 参考资料

rocksdb原理_基于RocksDB实现精准的TTL过期淘汰机制相关推荐

  1. rocksdb原理_[转]Rocksdb Compaction原理

    概述 compaction主要包括两类:将内存中imutable 转储到磁盘上sst的过程称之为flush或者minor compaction:磁盘上的sst文件从低层向高层转储的过程称之为compa ...

  2. rocksdb原理_教你玩转MyRocks/RocksDB—STATISTICS与后台线程篇

    0. Intro 在facebook的MySQL版本(以下称为MyRocks)中,RocksDB是可选的存储引擎.相比于InnoDB引擎,RocksDB的一个重要的优势是它使用更少的磁盘空间.在生产系 ...

  3. 液压支架销轴力学计算分析研究_基于RFID射频精准定位的智能开采研究与应用...

    一.项目背景 近年来随着智能开采技术的不断发展,装备和新工艺不断更新换代,在智能开采中,对采煤机位置的精准定位是能否实现智能开采的关键,只有准确无误地获取煤机的准确位置,才能实现工作面的智能化开采,进 ...

  4. python卡方检验筛选特征原理_基于Python的遥感特征筛选—递归特征消除(RFE)与极限树(Extra-Trees)...

    引言 基于前几篇文章关于筛选方法的介绍,本篇同样给大家介绍两种python封装的经典特征降维方法,递归特征消除(RFE)与极限树(Extra-Trees, ET).其中,RFE整合了两种不同的超参数, ...

  5. rocksdb原理_手摸手学习 RocksDB 的 Write Buffer Manager

    本文翻译自 RocksDB 官方 Wiki<Write Buffer Manager>: https://github.com/facebook/rocksdb/wiki/Write-Bu ...

  6. rocksdb原理_看图了解RocksDB

    编辑推荐: 本文来自于oschina,本文主要介绍RocksDB的写入的流程.读取的层次等相关内容. 它是一个高性能的Key-Value数据库.设计了完善的持久化机制,同时保证性能和安全性.能够良好的 ...

  7. 连通域最小外接矩形算法原理_基于分割的文本检测算法之PSENet/PAN/DBNet

    1. 文本检测难点 文本内包含文本,艺术字体,任意方向 ,曲线文字 ,多语言,其他环境因素等是文本检测中的难点 2. 分割 问题1: 语义分割模型是对pixel进行分类,所以理论上讲,可以检测不规则的 ...

  8. 简述isodata算法的原理_基于UWB技术的室内定位方法简述

    1. UWB室内定位概述: UWB室内定位技术与传统通信技术有极大的差异,它不需要使用传统通信体制中的载波,而是通过发送和接收具有纳秒或纳秒级以下的极窄脉冲来传输数据,从而具有GHz量级的带宽.超宽带 ...

  9. 小波变换原理_基于电压行波原理故障测距的相关问题

    本文首先对基于电流行波和电压行波原理的故障测距进行了比较,接下来着重论证了提取电容式电压互感器(CVT)二次电压行波进行故障测距的可行性,并对CVT的行波传变能力进行了仿真分析,提出了小波变换和高速数 ...

最新文章

  1. Ubuntu 14.04 64bit上安装Scrapy
  2. 人工智能时代,企业的未来离不开云专线
  3. mysql分布式安装可靠读写案列图解,高并发下的分布式锁-mysql篇
  4. 2014 Container技术大会:未来Linux Container会是PaaS平台的核心
  5. 贴花纸怎么贴_陶瓷贴花纸DIY怎么做?
  6. Linux命令之find命令中的-mtime参数
  7. 做更好的“教练”,用对抗训练增强“知识追踪”
  8. centos安装stress安装失败_CentOS安装nginx
  9. (转)常用英语100句
  10. canvas贝塞尔曲线爱心_HTML5 Canvas 绘制贝塞尔曲线 Bezier and quadratic curves
  11. java判断字符串是否是空,java判断字符串是否为空的方法
  12. 简单英译汉SQL脚本
  13. 时空恋旅人 豆瓣影评
  14. 超过1M的网络动图添加到微信表情包
  15. jsp怎么设置页面背景
  16. 我曾被stormzhang拉黑过
  17. Windows系统下CMD命令
  18. docker-compose设置redis密码
  19. 王半仙儿的日记-0008
  20. 摄像头poe供电原理_带你简单了解一下什么是POE供电

热门文章

  1. 一只小蜜蜂,一万天纪念日,杨辉三角,洗牌
  2. powershell使用xming远程图形化linux安装软件
  3. 我相信每个人都有选择自己生活方式的权利 —— 写在美术专栏前面
  4. 为了戒掉网瘾,我用PYTHON决定休息时间的活动……
  5. 旅美作家称莫言作品获诺贝尔文学奖货真价实-诺贝尔奖-莫言-诺贝尔文学奖
  6. 微信h5分享自定义标题图标无效解决方案
  7. 转:四种“水”能喝掉脸上斑点
  8. 锐捷OSPF基础实验配置
  9. 短视频如何写出吸引人的标题?学会这几招,让你的标题更有说服力
  10. 计算机大专函授自我鉴定总结怎么写,计算机专科函授毕业自我鉴定