TiDB Server 内存用量持续上升

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

  • 【TiDB 版本】: v3.0.12
  • 【问题描述】:

我们目前的使用情境会在 TiDB 上建立大量的 database (数量约 1 万个)

每个 database 裡面大约有 10 张 table, 总 Regions 数量目前约 8 万个

stress 一阵子后我们发现 TiDB Server 的 memory 用量会持续上升

透过 pprof 查看主要 memory 上升的部分为 statistics 的 handle.tableDeltaMap.update

Fetching profile over HTTP from http://10.244.3.4:10080/debug/pprof/heap
Saved profile in /root/pprof/pprof.tidb-server.alloc_objects.alloc_space.inuse_objects.inuse_space.099.pb.gz
File: tidb-server
Type: inuse_space
Time: Apr 21, 2020 at 2:36am (UTC)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 56.76GB, 91.76% of 61.85GB total
Dropped 965 nodes (cum <= 0.31GB)
Showing top 10 nodes out of 82
      flat  flat%   sum%        cum   cum%
   43.20GB 69.84% 69.84%    43.20GB 69.84%  github.com/pingcap/tidb/statistics/handle.tableDeltaMap.update
    5.16GB  8.34% 78.17%     5.16GB  8.34%  github.com/pingcap/tidb/statistics.NewCMSketch
    1.75GB  2.82% 81.00%     1.75GB  2.82%  github.com/pingcap/tidb/statistics/handle.errorRateDeltaMap.update

以下是透过 list 查看 handle.tableDeltaMap.update 的结果

(pprof) list github.com/pingcap/tidb/statistics/handle.tableDeltaMap.update
Total: 61.85GB
ROUTINE ======================== github.com/pingcap/tidb/statistics/handle.tableDeltaMap.update in /home/jenkins/agent/workspace/tidb_v3.0.12/go/src/github.com/pingcap/tidb/statistics/handle/update.go
   43.20GB    43.20GB (flat, cum) 69.84% of Total
         .          .     48:func (m tableDeltaMap) update(id int64, delta int64, count int64, colSize *map[int64]int64) {
         .          .     49:   item := m[id]
         .          .     50:   item.Delta += delta
         .          .     51:   item.Count += count
         .          .     52:   if item.ColSize == nil {
    7.47GB     7.47GB     53:           item.ColSize = make(map[int64]int64)
         .          .     54:   }
         .          .     55:   if colSize != nil {
       1MB        1MB     56:           for key, val := range *colSize {
      10GB       10GB     57:                   item.ColSize[key] += val
         .          .     58:           }
         .          .     59:   }
   25.72GB    25.72GB     60:   m[id] = item
         .          .     61:}
         .          .     62:
         .          .     63:type errorRateDelta struct {
         .          .     64:   PkID         int64
         .          .     65:   PkErrorRate  *statistics.ErrorRate

我们的理解是每个 session 都会建立自己的 SessionStatsCollector

如果这个 session 处理过很多张 table, 则这个 SessionStatsCollector 裡面的 mapper 就会长得比较大, 直到 session close 才会把内存释放出来

我们有尝试让 client connection 每隔 30 分钟重新建立连线,让 session 不会一直使用

理论上这样 session 被关闭后应该会把 memory 放出来才对? 但实际测试起来 memory 还是持续升高

虽然一晚的测试中间有一度往下降 (20:00 & 03:30),但后续还是一直爬升

如果把测试停止 (client 不继续发送 request),则 memory 很快就降下来,透过 pprof 查看 handle.tableDeltaMap.update 的用量都会释放

以下是抓取下来的 pprof 档

pprof.tidb-server.pb.gz (368.0 KB)

我的疑问

想询问一下老师是否有什麽建议? 另外 TiDB 如果用在这样有大量 database 与 table 的状况下,有没有什麽要特别注意的地方? 谢谢

您好:

    麻烦发送over-view 和 tidb 监控,多谢

(1)、chrome 安装这个插件https://chrome.google.com/webstore/detail/full-page-screen-capture/fdpohaocaechififmbbbbbknoalclacl

(2)、鼠标焦点置于 Dashboard 上,按 ?可显示所有快捷键,先按 d 再按 E 可将所有 Rows 的 Panels 打开,需等待一段时间待页面加载完成。

(3)、使用这个 full-page-screen-capture 插件进行截屏保存

您好:

谢谢您的回复,以下是监控, 因为档案太大 tidb 拆成两个档案上传

为了让内存上升的问题明显一点, 测试时只跑了一台 TiDB Server

感谢反馈,正在分析请稍等,

1赞

你好,

  1. 通过 go gc count,20 点和 4 点内存使用下降符合预期, image

  2. tidb 内存那使用升高,需要结合业务判断下,随着压测不断进行,缓存在 tidb 中的数据不断增加,在进行不断的压测,内存出现锯齿状攀升是符合预期的。

目前服务器还是很不错的,这边根据监控有几点需要关注下

  1. slow query 和 rollback 情况很多,可以重点检查下 rollback 为那些语句

  1. raftstore cpu 默认为 2 (监控显示200%) ,已经打满了,如果线上有需要可以调整下 [raftstore] store-pool-size = 2

  2. 目前监控中空region 0.36%(由于 region 基数很大,所以建议关注下),看下 tikv 中应该存在 region is unavailable(压测的比较厉害),测试环境还可以,生产环境如果出现在进行调整吧。

1赞

您好:

感谢您的回答 ^^, 关于几个参数指标我们会注意一下

关于内存增长的部分,如果方便的话想询问一下被 handle.tableDeltaMap.update 佔用的内存,在什么样的时机与条件下可以得到释放

以及 handle.tableDeltaMap.update 佔用 40GB 的内存是正常现象吗? 与哪些因素有关呢? 例如表的数量?

我们的业务内容是对不同的表 (总共约 10 万个) 进行随机的读取与写入,读/写比大概 1:1

每个 session 都有可能处理到任何一个表,每条连线运作三十分钟后,会断开重连

在另外一个 TiDB 内存的讨论中 TiDB 内存释放问题(二) statistics 会佔用内存的情况,不过后续没有细讲

下图为 pporf 的火焰图与 graph,谢谢

你好,

问题已收到,正在分析,稍等

Hi, 请问一下 tidb的配置文件中,[performance] 节里的 stats-lease 的配置是多少?

您好, 目前 tidb 的配置如下, 没有针对 stats-lease 做设定

token-limit = 1001
[log]
level = "info"
slow-threshold = 10000
[tikv-client]
grpc-connection-count = 32

确认了一下,感觉不像是bug。 每个表的大概有多少列? handle.tableDeltaMap 是用来记录每个表和表的列的变化情况。和表的个数以及每个表的列数正相关。 然后,默认会每隔60秒(20*stats-lease) 将内存的统计信息写到 TiKV。

所以只要一直有更新表的请求。内存就会一直占用。 想降低内存的话,可以考虑试试调低 stats-lease,频率更高地让统计信息写到 TiKV。 (如果需要的话,stats-lease 支持降低到1秒以下的)

2赞

handle.tableDeltaMap 是用来记录每个表和表的列的变化情况。和表的个数以及每个表的列数正相关。

补充一下,还和session的数量正相关。 每个session都会持有一份。所以想要降低内存,也可以考虑降低session数量。(或者扩大机器/内存规模,如果现实业务也是这种workload的话)

1赞

这个像是 bug 呀!应该有 coprocessor 读上来的 chunk,由统计信息一直持有着引用,造成内存不释放。 正常情况下哪会一直占用着 44+G 的内存呀 @SunRunAway-PingCAP

1赞

您好, 我们一个表平均大概 3~7 个 column

好吧,我错了。还真的是小块数据本身的占用,而不是 chunk 被引用的问题

1赞

您好:

这边我有个小疑惑, 当 connection 重新建立时,session 不是应该会被删除并释放出内存吗 ?

但我将 connection 时间设定成每 30 分钟重新建立, 好像没有看到这个效果

但是如果将 request 停止, 内存很快就釋放出来了

可能有连接池的影响之类的,session 并没有真正退出吧?

内存是在 session 连接里面引用着的,我把图截出来在这里

https://github.com/pingcap/tidb/blob/94011e6064fffc7df1057e2a9b2b866f429e7494/session/session.go#L1024

process info 引用着,这个东西一直都在,连接不关就不会释放。 @SunRunAway-PingCAP

1赞

感谢你们快速的回复与说明,给你们一个赞 ~~

在确认运作机制后,我们会修改一下业务代码,让 session 尽量处理同样的 table

看看这样可不可以把内存控制在比较合理的大小

1赞

您好: 感谢使用,方便的话麻烦看下私信,多谢

1赞