为提高效率,提问时请提供以下信息,问题描述清晰可优先响应。
【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
【我的疑问】:
- 是否有参数或其它方式可以控制 TiDB 启动后耗费在 NewCMSketch 的记忆体用量? 因为在这种状况下,随着 Table 数量越来越多,TiDB 会佔用越来越多的 memory,且横向扩充没有办法解决这个问题
- CMSketch 应该是用在点查的估算,预估某个 key 在 index/column 内的数量? 但我测试起来,非 primary key 的 UNIQUE Index 似乎也会计算 CMSketch,可是 UNIQUE Index 不就保证每个 key 只有一笔吗? 不知是否我的理解有错误?
- 将每个使用者的资料放在不同 table,总 table 数量在百万左右的使用方式是否适合 TiDB? 是否有建议整个环境内的 table 数量不要超过某个数量?
- 如果将所有使用者资料塞在同一张 table, 能否有做法可让 select * from table where use_id=‘xxx’ 的效能与每个 user 一张 table 接近?
以下是透过上面脚本产生 10000 张 table 后,所撷取的 pprof heap 档
pprof.tidb-server.heap.pb.gz (42.9 KB)