-
Notifications
You must be signed in to change notification settings - Fork 74
Home
- Todis 是 Redis On ToplingDB,兼容 Redis 的分布式数据库
- Todis 是基于公有云的托管 Redis 服务,对用户可以即刻交付,立马可用
- Todis 性能优异,弹性伸缩,按量付费,价格低廉(内测期间,完全免费)
- Todis 包含非常丰富、完善的 Grafana 指标监控以及存储引擎观测界面
目前市面上有很多种大容量、持久化的 Redis 实现,基本上都基于 RocksDB。
基于 RocksDB 的数据库,它们的问题主要是 RocksDB 的问题,其次才是其 Redis 实现本身的问题。
先说结论:RocksDB 的写负载中,有 80% 以上的 CPU 用在 Compact 上。
前台线程将数据写进 RocksDB,后台线程通过 Flush/Compact 将数据压缩并落盘。压缩数据会占用 CPU,不压缩会占用 SSD 空间和 IO 带宽,RocksDB 为此有很多配置选项可以调节。
然而数据还是经常写得太快,Compact 跟不上,导致 L0 数据积压,从而引发读放大。为此 RocksDB 加入了很多限流策略:主动降低写速度。然而这只是转移了矛盾————牺牲写性能,换取读性能!
能不能三者都要呢?既要写得快,又要读得快,还要压缩率高,当然可以,花钱呗,增加 CPU 核心,用傲腾内存+DAX文件系统……
但是,在实际的业务场景中,负载不是一成不变的,负载高的时候,256核都不够用,负载低的时候,机器在空转。
例如某互联网巨头,为了在流量高峰时仍维持较低的延时(较快的响应速度),让高配服务器负载常年维持在 10% 以下,当然,那个巨头富得流油,有钱没处花,不在乎成本,不在乎高配低能。但是,我们相信,大部分用户很在乎这一点。
RocksDB 的一个核心组件是 BlockBasedTable,其核心是内存中的 BlockCache + 外存中的压缩文件(sst)。其工作方式为:每个 BlockBasedTable 的 sst 文件由多个(压缩的) Block 组成,每个 Block 中保存多条数据,sst 中同时存储 Block 的索引,搜索时:
- 数据先定位到具体的 sst 文件
- 在 sst 中,从 Block 索引定位到 Block
- 去 BlockCache 中搜索该 Block,如果找到了,直接返回
- 如果没找到,按照索引中保存的 Block offset + len 去文件中读取该(压缩的)Block
- 解压该 Block,并将解压后的 Block 存入 BlockCache
- 存入 BlockCache 时,如果 BlockCache 达到容量上限,需(按照某种策略)丢弃一个(解压的)Block
- 从解压的 Block 中搜索需要的数据
大体如此,当然其中有很多繁杂的细节。主要的问题有两点:
- 数据(使用传统压缩算法)按 Block 压缩
- 而压缩算法只有在数据上下文足够大时,才能有较高的压缩率
- Block 尺寸设得大了,压缩率就高了,同一 Block 中保存的数据就更多,BlockCache 未命中时,需要加载并解压的数据就更多,而所需的数据可能只有一条,这可以认为是另一种读放大
- Block 尺寸设得小了,压缩率就低了,同一 Block 中保存的数据就更少,BlockCache 未命中时,需要加载并解压的数据就更少,这个意义上的读放大就更小
- 从外存(SSD)中的文件读取数据时,操作系统先将外存中的数据读取到内存(PageCache)中
- 将数据从 PageCache 拷贝到用户提供的 buffer 中(如使用 mmap 可略过这一步)
- 将读取到的数据解压到另一个用户 buffer(单个解压的 Block) 中,并放入 BlockCache
其中,同一份数据在内存中存在两份,一份是操作系统的 PageCache(压缩的形式),一份是用户空间的 BlockCache(解压的形式)。
Todis 最主要的是,使用 ToplingDB 代替 RocksDB 作为存储引擎,其次,在 Redis 实现的层面上有一些优化。
ToplingDB 是兼容 RocksDB 的存储引擎,解决了 RocksDB 的诸多问题。
既然 Compact 占了那么多(80% 以上) CPU,那如果我把 Compact 从存储引擎中移走,CPU 不就释放出来了。我们把这个叫做“前台计算与后台计算分离”,这有诸多优点:
- Compact 计算集群,其单个结点可以随时回收(Compact进程可以随时终止),不会对 DB 产生影响
- Compact 计算集群,可以使用“计算型”结点,而前面的 DB 结点,使用“内存型”结点
- Compact 计算集群,可以多实例共享,即多个 DB 结点共享一个 Compact 集群
- Compact 计算集群,可以使用“空闲算力”,任何有空闲算力的结点,闲着也是闲着,那就执行点 Compact 吧
- 大厂的数据中心,从来不缺算力,而是大量的“空闲算力”苦于无处利用
- 相比一般大厂,公有云厂商的资源利用率要高得多,然而他们的“空闲算力”仍然卖得非常便宜,甚至可以低至 1折
topling-zip (fork 自 terark-zip)实现了诸多此类算法,曾经的 terarkdb 就利用 terark-zip 解决了上述 RocksDB 读取数据的问题(2.2)。
这类压缩算法相比传统的通用(RocksDB 使用的)压缩算法,其压缩(Compact)过程中的 CPU 消耗更大,如果说使用传统压缩算法,写负载中 Compact 占用 80% 的 CPU,那么如果使用这类压缩算法,Compact 将占用 90% 的 CPU。
相反,这类压缩算法的“解压”过程非常高效,使得 DB 结点的负载大大降低。
这类压缩算法的这两个特点,结合弹性分布式 Compact,简直是天作之合。
除了性能/成本方面的考虑,ToplingDB 同时也进行了充分的“人性化”设计。
目前,在 DB 领域,RocksDB 就是存储引擎的事实标准。只要兼容了 RocksDB,就融入了这个生态圈。
RocksDB 自身实现了非常基本的插件机制,但其插件机制非常原始,用户必须侵入性地修改代码,才能使用“自定义”的“插件”。
ToplingDB 通过 旁路插件 解决了这个问题。
依托旁路插件,在 ToplingDB 内部集成了一个 Http WebServer,对外展示引擎的各种内部信息。
内部集成的 Http WebServer,将 ToplingDB 的各种指标(ticker, histogram 等等),以 Prometheus 的格式开放出去,从而可以在 grafana 中进行监控。
prometheus 包含基本的 histogram 方案,但是不够全面,不够完美,我们通过定制 grafana panel,将 histogram 包含的信息,更全面,更完美地展示出来。