Mallocator 大战: RocksDB、InnoDB 以及 glibc malloc, jemalloc, tcmalloc 的性能对比

2025年4月11日,星期五

Mallocator 大战

如果你使用 RocksDB 并且想避免 OOM,那么请使用 jemalloc 或 tcmalloc,避免使用 glibc malloc。这在 2015 年是正确的,在 2025 年仍然如此(参见这里)。问题在于 RocksDB 可能会成为 allocator 的压力测试,因为它在从存储读取数据块时会进行分配(调用 malloc),然后在驱逐数据块时进行释放(调用 free)。这些分配的生命周期非常不同,因为某些数据块会在缓存中保留很长时间,这会导致使用 glibc malloc 时 RSS 比预期大得多。幸运的是,jemalloc 和 tcmalloc 在容忍这种分配模式方面表现更好,不会导致 RSS 过大。我还没有注意到 InnoDB 存在类似的问题,部分原因是它在进程启动时会为 InnoDB 缓冲池进行一些大的分配,并且不会为从存储读取的每个数据块都进行 malloc/free 操作。

最近,一位 MySQL 性能专家 Dimitri Kravtchuk 声称,使用 InnoDB 和 jemalloc 时,RSS 或 VSZ 可能会变得过大。我不知道他的设置的所有细节,而且我没有在我的设置上重现他的结果。公平地说,我在这里展示了 InnoDB + jemalloc 的 VSZ 可能比你预期的要大,但这并不是一个问题,这只是 jemalloc 的一个可能会令人困惑的特性。但是 InnoDB 使用 jemalloc 的 RSS 与我从 tcmalloc 获得的结果相似。

总结:

先前的研究

我有多篇关于将 jemalloc 与 MyRocks 一起使用的博客文章。

构建、配置和硬件

我从源代码编译了上游 MySQL 8.0.40 用于 InnoDB。我还从源代码编译了 FB MySQL 8.0.32 用于 MyRocks。对于 FB MySQL,我使用了截至 2024 年 10 月 23 日的源代码,git 哈希值为 ba9709c9,RocksDB 版本为 9.7.1。

服务器是 Hetzner 的 ax162-s,具有 48 核 (AMD EPYC 9454P),128G 内存,并且 AMD SMT 已禁用。它使用 Ubuntu 22.04,存储是 ext4,通过 SW RAID 1 使用 2 个本地连接的 NVMe 设备。更多细节在这里。按照标价,Google Cloud 上类似的服务器比 Hetzner 贵 10 倍。

对于 malloc,服务器使用:

配置文件InnoDBMyRocks。对于InnoDB,我使用了80G缓冲池。我尝试对MyRocks使用50G缓冲池,但是glibc malloc出现OOM,所以我使用10G缓冲池重复了所有测试。使用MyRocks的glibc malloc,我可能已经能够避免OOM,缓冲池大小在30G到40G之间-但我不想花费更多时间来弄清楚,因为真正的答案是使用jemalloc或tcmalloc。

Benchmark

我使用了 sysbench,我的用法在这里解释。为了节省时间,我只运行了 42 个微基准测试中的 27 个,并且大多数测试仅测试 1 种类型的 SQL 语句。测试运行使用 16 个表和 50M 行/表。有 256 个客户端线程,每个微基准测试运行 1200 秒。通常我不运行 (client threads / cores) >> 1,但我在这里这样做是为了产生更大的压力,并复制我认为 Dimitri 所做的事情。

通常,当我运行 sysbench 时,我会对其进行配置,以使测试表适合缓冲池(块缓存),但我在这里不这样做,因为我希望 MyRocks 进行 IO,因为每次存储读取的分配都会为 allocator 带来很多问题。

运行所有测试的命令行是:bash r.sh 16 50000000 1200 1200 md2 1 0 256

峰值 VSZ 和 RSS

下表显示了基准测试期间来自 mysqld 的 VSZ 和 RSS 的峰值。最后一列是比率(峰值 RSS / 缓冲池大小)。我不确定在此工作中比较 InnoDB 和 MyRocks 之间的这些比率是否公平,因为 InnoDB 的缓冲池大小要大得多。无论如何,glibc malloc 的 RSS 比 MyRocks 缓冲池大 3 倍以上,这是一个问题。

InnoDB 具有 80G 缓冲池的峰值

| alloc | VSZ | RSS | RSS/80 | | -------- | ---- | ---- | ------ | | glibc | 88.2 | 86.5 | 1.08 | | tcmalloc | 88.1 | 85.3 | 1.06 | | jemalloc | 91.5 | 87.0 | 1.08 |

MyRocks 具有 10G 缓冲池的峰值

| alloc | VSZ | RSS | RSS/10 | | -------- | ---- | ---- | ------ | | glibc | 46.1 | 36.2 | 3.62 | | tcmalloc | 15.3 | 13.1 | 1.31 | | jemalloc | 45.6 | 12.2 | 1.22 |

性能:InnoDB

从这里的结果来看,tcmalloc 和 jemalloc 之间的 QPS 大多相似,但是有一些微基准测试中 tcmalloc 比 jemalloc 好得多,并且这些已被突出显示。

read-only_range=10000 的结果是一个异常值(tcmalloc 比 jemalloc 快得多),并且从 vmstat metrics here 我看到 jemalloc 的 CPU/operation (cpu/o) 和 context switches /operation (cs/o) 比 tcmalloc 大得多。

这些结果使用相对 QPS,它是以下内容,其中 $allocator 是 tcmalloc 或 jemalloc。当该值大于 1.0 时,使用 tcmalloc 或 jemalloc 的 QPS 较大。

(使用 $allocator 的 QPS) / (使用 glibc malloc 的 QPS)

相对于使用 glibc malloc 的结果

| col-1 | col-2 | | -------- | -------- | | | | | col-1col-2 | | | 0.99 | 1.02 | hot-points_range=100 | | 1.05 | 1.04 | point-query_range=100 | | 0.96 | 0.99 | points-covered-pk_range=100 | | 0.98 | 0.99 | points-covered-si_range=100 | | 0.96 | 0.99 | points-notcovered-pk_range=100 | | 0.97 | 0.98 | points-notcovered-si_range=100 | | 0.97 | 1.00 | random-points_range=1000 | | 0.95 | 0.99 | random-points_range=100 | | 0.99 | 0.99 | random-points_range=10 | | 1.04 | 1.03 | range-covered-pk_range=100 | | 1.05 | 1.07 | range-covered-si_range=100 | | 1.04 | 1.03 | range-notcovered-pk_range=100 | | 0.98 | 1.00 | range-notcovered-si_range=100 | | 1.02 | 1.02 | read-only-count_range=1000 | | 1.05 | 1.07 | read-only-distinct_range=1000 | | 1.07 | 1.12 | read-only-order_range=1000 | | 1.28 | 1.09 | read-only_range=10000 | | 1.03 | 1.05 | read-only_range=100 | | 1.05 | 1.08 | read-only_range=10 | | 1.08 | 1.07 | read-only-simple_range=1000 | | 1.04 | 1.03 | read-only-sum_range=1000 | | 1.02 | 1.02 | scan_range=100 | | 1.01 | 1.00 | delete_range=100 | | 1.03 | 1.01 | insert_range=100 | | 1.02 | 1.02 | read-write_range=100 | | 1.03 | 1.03 | read-write_range=10 | | 1.01 | 1.02 | update-index_range=100 | | 1.15 | 0.98 | update-inlist_range=100 | | 1.06 | 0.99 | update-nonindex_range=100 | | 1.03 | 1.03 | update-one_range=100 | | 1.02 | 1.01 | update-zipf_range=100 | | 1.18 | 1.05 | write-only_range=10000 |

性能:MyRocks

从这里的结果来看,tcmalloc 和 jemalloc 之间的 QPS 大多相似,jemalloc 略有优势,但是有一些微基准测试中 jemalloc 比 tcmalloc 好得多,并且这些已被突出显示。

下面的热点结果很奇怪(jemalloc 比 tcmalloc 快得多),并且从 vmstat metrics here 我看到 tcmalloc 的 CPU/operation (cpu/o) 和 context switches /operation (cs/o) 都大得多。

这些结果使用相对 QPS,它是以下内容,其中 $allocator 是 tcmalloc 或 jemalloc。当该值大于 1.0 时,使用 tcmalloc 或 jemalloc 的 QPS 较大。

(使用 $allocator 的 QPS) / (使用 glibc malloc 的 QPS)

相对于使用 glibc malloc 的结果

| col-1 | col-2 | | -------- | -------- | | | | | col-1col-2 | | | 0.68 | 1.00 | hot-points_range=100 | | 1.04 | 1.04 | point-query_range=100 | | 1.09 | 1.09 | points-covered-pk_range=100 | | 1.00 | 1.09 | points-covered-si_range=100 | | 1.09 | 1.09 | points-notcovered-pk_range=100 | | 1.10 | 1.12 | points-notcovered-si_range=100 | | 1.08 | 1.08 | random-points_range=1000 | | 1.09 | 1.09 | random-points_range=100 | | 1.05 | 1.10 | random-points_range=10 | | 0.99 | 1.07 | range-covered-pk_range=100 | | 1.01 | 1.03 | range-covered-si_range=100 | | 1.05 | 1.09 | range-notcovered-pk_range=100 | | 1.10 | 1.09 | range-notcovered-si_range=100 | | 1.07 | 1.05 | read-only-count_range=1000 | | 1.00 | 1.00 | read-only-distinct_range=1000 | | 0.98 | 1.04 | read-only-order_range=1000 | | 1.03 | 1.03 | read-only_range=10000 | | 0.96 | 1.03 | read-only_range=100 | | 1.02 | 1.04 | read-only_range=10 | | 0.98 | 1.07 | read-only-simple_range=1000 | | 1.07 | 1.09 | read-only-sum_range=1000 | | 1.02 | 1.02 | scan_range=100 | | 1.05 | 1.03 | delete_range=100 | | 1.11 | 1.07 | insert_range=100 | | 0.96 | 0.97 | read-write_range=100 | | 0.94 | 0.95 | read-write_range=10 | | 1.08 | 1.04 | update-index_range=100 | | 1.08 | 1.07 | update-inlist_range=100 | | 1.09 | 1.04 | update-nonindex_range=100 | | 1.04 | 1.04 | update-one_range=100 | | 1.07 | 1.04 | update-zipf_range=100 | | 1.03 | 1.02 | write-only_range=10000 |