TiDB Server 在大量 Table 情境下 CMSketch占用内存过多

为提高效率,提问时请提供以下信息,问题描述清晰可优先响应。

【TiDB 版本】: v3.0.12

【问题描述】:

我们目前的业务设计採用 Multi‐Tenant Data Architecture 中 Database-per-tenant 的架构

也就是帮每个使用者建立独立的 database 与 table,因此在我们的应用情境中,TiDB 内可能会有上百万张 table,每张 table 内的资料量平均约数十万个 row

会选择帮每个使用者建立 table 而非透过新增类似 user_id 这种 column 让所有使用者共用同一张 table 的原因在于,我们的业务会执行类似 select * from table 的操作

当採用每个 user 独立 table 的做法时,对应到 TiKV 的操作,会是连续读取某一个区段的 key range,读取效率好

但是如果是将所有使用者资料合在一张 table 的话,所有使用者的资料在 TiKV 会合在一起

当执行 select * from table where use_id=‘xxx’ 时,会需存取很大量的资料才能收集完该 user 的资料,效率会差很多

以上是我对 tikv 的理解,如果有错误再麻烦老师指正

目前我们在测试大量建表时,發现 TiDB 启动后的 memory 用量与 table (准确说起来是 index) 数量成正比,每个 index 大概消耗掉 40KB,因此想来询问一下是否有解决方法

透过 golang 的 pprof 分析,TiDB 主要的 memroy 耗费在 tidb/statistics.NewCMSketch

从 source code 看起来,TiDB 启动时,就会把 mysql.stats_histograms 中,所有 is_index > 0 的 row 的 cm_sketch 读进 memory 内

以下是一个简单 reproduce 的脚本 (建立 10000 个 table, 每个 table 一个 index,在我们环境中, 这样 TiDB 启动后会被 NewCMSketch 佔用掉约 10000 * 40KB = 400MB, 如果将 1 万改成 10万则会佔用 4GB)

for i in `seq 10000`; do 
    mysql -uroot -P4000 -h0 test -e "CREATE TABLE mem$i(foo int, bar int, UNIQUE INDEX(foo, bar));"; 
    mysql -uroot -P4000 -h0 test -e "ANALYZE table mem$i;"; 
done

脚本执行完后,确认 stats_histograms is_index > 0 的 row 数量为 10000 行

> select count(*) from mysql.stats_histograms where is_index > 0 and cm_sketch is NOT NULL;
+----------+
| count(*) |
+----------+
|    10000 |
+----------+

将 TiDB 重新启动,等待五分钟,期间不發送任何 request,完成后查看 memory 用量接近 400MB

Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 445.63MB, 94.85% of 469.82MB total
Dropped 65 nodes (cum <= 2.35MB)
Showing top 10 nodes out of 62
      flat  flat%   sum%        cum   cum%
  394.55MB 83.98% 83.98%   394.55MB 83.98%  github.com/pingcap/tidb/statistics.NewCMSketch

【我的疑问】:

  1. 是否有参数或其它方式可以控制 TiDB 启动后耗费在 NewCMSketch 的记忆体用量? 因为在这种状况下,随着 Table 数量越来越多,TiDB 会佔用越来越多的 memory,且横向扩充没有办法解决这个问题
  2. CMSketch 应该是用在点查的估算,预估某个 key 在 index/column 内的数量? 但我测试起来,非 primary key 的 UNIQUE Index 似乎也会计算 CMSketch,可是 UNIQUE Index 不就保证每个 key 只有一笔吗? 不知是否我的理解有错误?
  3. 将每个使用者的资料放在不同 table,总 table 数量在百万左右的使用方式是否适合 TiDB? 是否有建议整个环境内的 table 数量不要超过某个数量?
  4. 如果将所有使用者资料塞在同一张 table, 能否有做法可让 select * from table where use_id=‘xxx’ 的效能与每个 user 一张 table 接近?

以下是透过上面脚本产生 10000 张 table 后,所撷取的 pprof heap 档

pprof.tidb-server.heap.pb.gz (42.9 KB)

你好,关于 CMSketch 的内存使用问题。我们先内部确认一下。关于第三点 目前 TiDB 是没有做 table 的数量的限制的。第四点的话,如果合并到一个表的话,一次性查询 10W 个 row 涉及到的回表以及多一次 rpc 所以肯定会比原来的分表的 table_scan 要慢的。 具体要看业务能否接受。

2 个赞

好的, 问题 1 跟问题 2 再麻烦你们查看一下 谢谢 ~

你好,

问题1:目前还没有提供参数控制 CMSketch 的内存,关于 Table 数量多导致的 NewCMSketch 内存占用,我们内部评估下能否进行优化

问题2:CMSketch 用于等值查询的估算,包括 unique index 和非 unique index

1 个赞

感谢老师回答,问题一再麻烦评估一下了,依照我们的业务情境,如果能降低 CMSketch 的内存用量对我们帮助很大

关于问题二,有点好奇,想再请教一下老师我的理解对不对。 当 CMSketch 运用在等值查询的估算时, 非 unique index 由于会有重複的栏位,所以用 CMSketch 可以估算出符合的行数,蛮合理的 但 unique index 不会有重複的栏位,所以估算出来理论上不是 0 就是 1,这种情况是不是用 bloom filter 会更加合适?

感谢

好的,稍等我们确认下答复,多谢

你好,unique key 确实可以用 bloom filter 来优化内存。如果你有时间的话,可以在 github 上提一个 issue,谢谢。

1 个赞

CMSKETCH DEPTH 和 CMSKETCH WIDTH 都可以通过 SQL 语句调整的,详见 https://pingcap.com/docs-cn/stable/reference/performance/statistics/#全量收集 默认 DEPTH 是 5,WIDTH 是 2048。

1 个赞

把 DEPTH 调整为 1 就等同于 bloom filter 了。但是对于 unique key,还是应该默认用 depth=1,这个我们可以改进。

1 个赞

第二个问题补充一下: CM-Sketch 是用来进行等值查询的 row count 估算,不只是点查会用,非 primary index 的普通二级索引也会用。

1 个赞

感谢说明,但后来想到 bloomfilter 的元素只能加入, 不能删除, 所以只能在 ANALYZE 时重新计算, 无法用在动态更新

如果要进行动态更新可能要用 bloomfilter 的变形, 例如 Counting Bloom Filter, 但这样感觉内存用量应该会与 CMSKETCH 差不多

好的,感谢说明, 不过系统自动运行的 ANALYZE 还是会用预设值对吧? 好像没有找到 config 可以调整

感谢说明, 不果感觉上好像不太一样, 以 WIDTH 2048 来说, 只要超过 2048 个 row 所有 block 的值就可能大于 0, 就没有办法提供有效率的估算了? 不知道我的理解正不正确

Auto Analyze 还是用预设值。感谢提示,这个应该可以提个 issue,你可以提一下。

bloom filter 和 depth=1 的 CM-Sketch 都有 hash 冲突的问题,无论是否超过 2048,都会估大。对于 unique key,没有值也会以为有值。

但两者还是不太一样,bloom filter 用多个 hash 函数,但 depth=1 的 CM-Sketch 只有一个。可以认为 bloom filter 相对更准确一些。对于大表,可以适当调大 width 或 depth。

我们之前已经有过优化统计信息的 issue 了, https://github.com/pingcap/tidb/issues/16572

对于 unique index,不需要 CM-Sketch 也能接受。

感谢回答, 这个 issue 的优化方向很符合我们的需求

后续我再追踪这条 issue 就好

感谢帮忙 ~

感谢反馈:handshake:

1 个赞

相同场景。关注中。

:call_me_hand::call_me_hand::call_me_hand:

此话题已在最后回复的 1 分钟后被自动关闭。不再允许新回复。