
服务器内存一直涨,过几天就被 OOM Killer 干掉是不少用户在压测或生产上线初期会遇到的问题。这篇文章按 TDengine 实际的内存消费结构,把常见的几块内存去了哪儿讲清楚,并给出排查和调优方向。相关配置项名称和默认值均对照源码核实过。每个 vgroup 都会占一份写入缓冲区建库时的BUFFER参数(单位 MB,取值范围 3~16384,默认 256)决定了每个 vgroup 用于承接写入数据的内存缓冲池大小。这里最容易被忽视的一点是:这块内存是按 vgroup 计算的,一个 dnode 上如果承载了多个数据库、每个数据库又有多个 vgroup 副本,实际占用是BUFFER × vgroup 数量叠加起来的。如果你在同一台机器上开了很多个库、每个库又给了较大的BUFFER,内存涨得比预期快,基本就是这个原因。建议:排查内存占用时,先用SHOW VGROUPS之类的命令数一下这台机器上实际承载了多少 vgroup 副本,再乘以各个库的BUFFER设置,看看理论占用和实际观察到的内存增长是否吻合。CACHESIZE只在开启缓存最新值功能时才有意义建库参数里的CACHESIZE(对应底层的 cacheLastSize,默认 1MB,范围 1~65536MB)是给缓存最后一行/最后一个值这个功能用的一个基于 RocksDB 的内存块缓存(block cache)。如果你的业务大量查询设备的最新状态(LAST/LAST_ROW),这块缓存能明显提升查询速度,但代价是内存占用。需要注意的是,这个 RocksDB 缓存除了CACHESIZE控制的块缓存,还有几个默认值容易被忽略的相关参数:写缓冲区(默认 8MB × 2 个)、后台线程数(默认 2)、以及最大打开文件数默认是不限制。在文件数很多的场景下,不限制打开文件数是一个常见的内存放大来源(每个打开的文件都会带来额外的索引/过滤块内存开销),这是排查内存异常增长时容易被忽略的一环。建议:如果没有明确需要缓存最新值的查询场景,不必特意调大CACHESIZE;如果用到了这个功能且观察到内存异常增长,可以关注一下这部分 RocksDB 相关参数,而不是只盯着BUFFER。查询本身也会占内存,而且默认是不限制单次查询除了写入侧的内存,查询执行过程本身也会占用内存(排序、聚合中间结果等)。TDengine 有一个查询内存池机制,涉及几个关键配置:查询内存池的开关(是否让查询统一走内存池管理);单次查询的内存上限——默认值是 0,也就是不限制;一个最小保留内存的水位线,用来防止查询内存池把机器内存全部吃满,挤占操作系统和其他进程。这意味着如果没有主动配置单次查询的内存上限,一条写得不好的复杂查询(比如大范围聚合、超级表全表扫描后排序)理论上是可以把内存占用推得很高的。在多租户或者和其他服务混部的机器上,这一点尤其值得关注。建议:生产环境如果是多个业务共享同一套 TDengine,或者机器本身内存不算充裕,建议主动给单次查询设置一个合理的内存上限,而不是依赖默认的不限制。同时给操作系统预留出合理的内存水位,避免查询内存池把机器逼到临界值。小结排查 TDengine 内存问题时,建议按这个顺序看:先算清楚写入缓冲区(BUFFER× vgroup 数)是不是本身就设得偏大;再看是否开启了缓存最新值功能以及相关 RocksDB 参数是否有不合理的默认值(尤其是文件数不限制这一点);最后检查查询侧有没有对单次查询内存做限制——这三块加起来,基本能覆盖大部分内存异常增长的场景。