Todis 和 TairDB 读写 strings 性能对比

1. Todis 和 Tair

  • Todis 是 Redis On ToplingDB,兼容 Redis 的 “外存版 Redis” 分布式数据库,可以使用 Redis 客户端存取、操作数据
  • Tair 是阿里云的 “外存版 Redis”,同样兼容 Redis,可以使用 Redis 客户端存取、操作数据

2. 为什么与 Tair 对比

  • 因为市面上有很多种 “外存版 Redis”,而阿里云 Tair 是其中的最先进代表
  • 我们的内测版是运行在阿里云之上的,用户很容易重现测试结果

3. 相比 Tair,Todis 有什么优势?

Todis 使用了多种先进技术,资源利用率更高,即便使用规格更低的机器,无论是读还是写,性能都远远超过 阿里云的 Tair

在测试中,TodisTair 读写相同原始数据,用同样的 Redis 客户端程序,同样的命令行选项,进行性能对比测试。

4. 测试数据:Wikipedia 英文版

总尺寸 109 GB
总条数 3850 万条
平均长度 2.8 KB

5. 全量数据对比测试结果

在各个测试中,Todis 使用的云主机规格均低于 Tair,具体规格在下面图示中给出。

对于 strings 类型,测试顺序写、随机写、顺序读与随机读性能。

  • 写数据使用多线程并发 mset,每次写 32 条数据,相比 set 每次写一条数据,减小了网络往返开销
  • 读数据使用多线程并发 mget,每次读 32 条数据,相比 get 每次读一条数据,减小了网络往返开销
  • 写完数据之后,读取数据之前,不执行手动 compact,这样更接近真实的业务场景

Strings 类型测试使用工具分别向 Todis 和 Tair 中写入测试数据(Strings 类型)数据,然后读。

每组写测试相互独立,读测试使用写测试中写入的数据,其中随机读测试以循环读的方式取第三轮结果。

下面以条形图来显示平均速度,以折线图来显示瞬时速度。

5.1 平均速度

平均速度的条形图简单直观,可以从中清楚地看到对比结果。

5.1.1 写数据

Todis 的写性能远高于 Tair,这是因为 Todis 利用了弹性分布式 Compact,将 Compact 负载转移到专用的 Compact 计算集群,从而:

  • Todis 结点只需要低规格的机器即可实现很高的性能,所以使用内存型结点(如果有“密集内存型”结点就更好了)
  • Compact 是计算密集型的,内存需求不高,其结点可以使用“计算型”,甚至“密集计算型”结点
  • Compact 集群是多用户多实例共享的,从而可以对多用户多实例的 Compact 负载进行平峰错谷,提高资源利用率
    • 如果一段时间内没有执行写操作,就没有 Compact
    • 类似 Tair 这样的 DB 需要为 Compact 预留算力(需要使用高规格的结点)
    • Todis DB 结点不需要为 Compact 预留算力
  • Compact 集群的单个结点不需要长期稳定运行,可以随时终止,从而可以使用“空闲算力”,降低总体成本
    • 大厂的“空闲算力”总是有的,需要的是找到可以利用这些“空闲算力”的应用
    • 云厂商的“空闲算力”非常便宜,最低可以到正常价格的一折

先看 顺序写:顺序写的写放大较低,所以性能更高

再看 随机写:随机写的写放大较低,所以性能较低

5.1.2 读数据

Todis 的读性能也远高于 Tair,因为 Todis 使用了“可检索内存压缩”技术

  • 直接在压缩的数据上执行搜索,大大提升了内存利用率
  • 这种压缩算法的读性能非常好,所以对 CPU 的需求很低

读数据用的是 mget,取的是第二轮读的平均速度,即相对稳定后的速度

先看 顺序读:顺序读缓存命中率极高,所以性能都很好

再看 随机读:随机读缓存命中率很低,所以性能都比顺序读要差很多

5.2. 瞬时速度

每个时刻的瞬时速度,我们用折线图来表达,其坐标横轴是时间,纵轴是瞬时速度。因为读写的数据量相同,Todis 速度快,很快就结束了,所以 Todis 横轴长度很短,Tair 的横轴长度很长。

5.2.1 写数据

写数据 Todis 和 Tair 都有是先快后慢,并且有一定的波动,其中低规格 Tair 波动的周期性非常明显。

先看 顺序写

再看 随机写

5.2.2 读数据

读数据用的是 mget,记录了循环读两轮的结果,即下面是读整个数据库两遍的数据。

  • Todis 在读取测试之前清空了操作系统 Cache,从而相当于是冷启动的,并且关闭了操作系统预读(madvise random)
  • Tair 没有提供清空 Cache 的命令,也无法登录 Tair 机器去清空 Cache,相当于是热启动的,合理猜测 Tair 应该也关闭了预读

先看 顺序读:图中 Todis 8C64G 规格在横轴 500 多秒处有个突发的跳跃,这是因为第一遍读取结束以后,所有数据都装进了缓存,第二遍读取时缓存全部命中,所以性能极高,表现在折线图上就是一个突发的跳跃。另一个有趣的点是在跳跃点之前的速度非常稳定,这是因为顺序读的时候,Cache 命中率是稳定不变的(每次加载一个 4K Page,包含 4~5 条数据,命中率就是 70~80%),从而其性能就和 IOPS 一样稳定了。

再看 随机读:图中 Todis 8C64G 的跳跃点在视觉上比顺序读要靠前很多,但其实也是在横轴 500 多秒处的(因为横坐标的最大值变大了 4 倍)。真正的不同是其在跳跃点之前的部分相比顺序读是逐渐提高的,这是因为数据总量有限,冷启动随机读的时候 Cache 命中率是逐渐 提高(一阶导数) 的,并且随着 Cache 的填充,Cache 命中率 提高的速度(二阶导数) 也是逐渐加快的。

6. 热数据对比读测试结果

该对比测试中不包括 Todis 8C64G 的规格,因为 8C64G 可以将全部 109G 数据都装入内存(采用内存压缩技术),就没有必要进行此项对比了。

  • 实际上,如果写完数据再执行一次手动 Compact,Todis 4C32G 的规格也可以将全量数据装入内存
    • 在真实业务中一般不会频繁执行手动 Compact,所以我们该测试中我们没有这么做
    • 对于真正有需要的业务,手动 Compact 可以大幅提升性能

在真实的业务场景中,数据有冷热之分,这也是我们之所以选取 Todis 或 Tair 的理由,在该测试中,我们将一部分数据作为热数据进行测试,下表中热数据占比是相对于全量数据的百分比:

数据库 规格 热数据占比(顺序读) 热数据占比(随机读)
Todis 4C32G 40% 10%
Todis 2C16G 20% 5%
Tair 16C64G 40% 10%
Tair 8C32G 20% 5%
Tair 4C16G 10% 3%

5.1 平均速度

先看 顺序读

再看 随机读:Tair 16C64G 的性能比 Tair 8C32G 的性能高了十几倍,从这里可以推断出来,16C64G 可以将全部热数据都装入内存,而 8C32G 无法将全部数据装入内存,再进一步推断:按照热数据的数据量,32G 是可以轻轻松松装下 5% 的热数据的,为什么这个性能结果却显示没有装下呢?这是因为 Tair 中 RocksDB 的 BlockBasedTable 中,一次 Cache Miss 在加载有效数据的同时,还会引入很多无效数据,如果 BlockSize 设置得较大,加载的无效数据就会过多,这些无效数据占用了宝贵的 Cache 空间!