从 TDengine 3.3.5.0 版本起,增加了一重磅的用于日常运行维护的功能,即服务器端内存管控。此功能主要解决在日常运行过程中突然上来一大批查询瞬间把服务器内存打满导致 OOM,影响服务的稳定性。
1. 现状
TDengine 产品在服务端查询时内存方面主要存在以下几点问题:
查询占用内存大导致 OOM;
内存占用无法计量分析,无法识别问题功能和模块;
缺少单个查询内存使用过大出错机制;
已经执行完成或出错的 TASK 内存未及时释放;
现有内存分配没有检查返回值;
2. 期望
期望通过本次内存管控功能达成以下目的:
服务端查询分配内存上限可以通过配置控制。
能够按照TASK、QUERY、全局统计查询内存分配、释放、占用情况;
在实时可用内存数不足以支持某些查询完成时返回查询内存耗尽错误;(可控)
超过单个查询内存使用上限的查询直接返回查询内存到达使用上限错误(可控);
通过及时释放已经执行完成或出错的 TASK 内存来降低并发内存占用;
3. 定义
内存池:本次功能实现是通过一个通用会话内存池与查询的结合来实现,受限于时间问题,暂时只能先实现最简单的内存管理策略——直接从系统分配与释放,后续再增加复用与其他策略。
分配的内存大小(MS):在通过各种内存分配接口从系统分配内存时指定的要分配的内存大小,其值由应用具体指定。
实际分配的内存大小(AMS):通过各种内存分配接口从系统分配的实际内存大小,因为内存管理器的实现原因,其值有可能会大于应用指定的分配大小。
实际使用的内存大小(UMS):实际使用的内存大小是应用真正从系统获得的物理内存大小,因为物理内存的分配是在实际使用时才会分配,因此 UMS 会小于或等于 AMS,并且根据使用情况的不同其差值可能会存在显著差异。
系统可用内存大小(SAMS):在本文中指的是某一时刻系统中可以使用的物理内存大小(不含 SWAP)。
4. 行为说明
4.1 配置项
配置项 |
类型 |
含义 |
适用范围 |
值域范围 |
默认值 |
动态更新 |
queryUseMemoryPool |
bool |
查询是否使用内存池管理内存,默认打开使用本功能,可根据需要关闭 |
服务端 |
false:关闭 true:打开 |
true(打开) |
不支持 |
memPoolFullFunc |
bool |
是否启用全功能内存池,当启用时会记录统计内存使用信息,但同时会显著降低内存接口性能,因此可以只在需要调试问题时开启(不对外暴露) |
服务端 |
false:关闭 true:打开 |
false(关闭) |
不支持 |
minReservedMemorySize |
INT32 |
最小预留的系统可用内存数量,单位:MB |
服务端 |
[1024-1000000000] |
无(根据可以内存自行预留) |
不支持 |
singleQueryMaxMemorySize |
INT32 |
单个查询在单个节点(dnode)上可以使用的内存上限,单位:MB |
服务端 |
[0-1000000000] |
0(无上限) |
不支持 |
queryNoFetchTimeoutSec |
INT32 |
查询中当应用长时间不 FETCH 数据时的超时时间,从最后一次响应起当超过该时间时自动清除查询任务,单位:秒。 |
服务端 |
[60, 1000000000] |
3600*5 (即查询结果 5 小时不取不再保留) |
支持 |
重要说明:
queryUseMemoryPool 是整个内存管控功能的开关,当关闭时上表中其他配置都不生效。
4.2 内存管控说明
单个查询内存限制模式
用户可以通过配置项 singleQueryMaxMemorySize 来指定单个查询在单个节点上可以使用的内存上限(AMS),当某个查询在某个节点上执行时内存消耗超过该内存上限时,直接返回查询内存到达上限错误(0x80000739 错误码);若未配置该配置项,该功能不启动,即单个查询内存使用无上限。
因为实际分配的内存大小(AMS) 与实际使用的内存大小(UMS)之间可能存在差异,因此可能出现实际使用内存未达上限而因为分配内存达到上限的场景。
在全功能内存池模式下,采用非精确统计模式以便获取更好的内存使用性能。
内存预留上限模式
当用户配置了 minReservedMemorySize 时,服务端将根据该配置预留内存;当用户未配置 minReservedMemorySize 时,将自动预留系统物理内存总量的 20% 且不小于 1G 大小的内存(SRMS)。除预留内存外,剩余的可用内存都可以被查询使用。在这种模式下,查询内存池会始终动态保留该预留,也就意味着查询可用内存数将跟随系统中可用内存的升降而自动更新,但是无法保证预留操作更新的实时性。
说明:
使用内存预留上限模式可以简化用户配置,同时可以保证物理内存更好的使用效率,但是因为无法保证内存预留和释放的实时性,因此如果遇到系统中任意应用急速分配物理内存场景仍有可能导致内存耗尽问题(OOM)。因此在这种模式下可以根据安全性的需要设置 singleQueryMaxMemorySize 大小,其值越大出现 OOM 的概率越低。
模式对比
对比项 |
内存预留上限模式 |
不使用内存管控模式 |
|
上限值 |
浮动值 |
无 |
|
内存利用率 |
中 |
高 |
|
安全性(不 OOM) |
中 |
低 |
|
可能造成 OOM 的场景 |
|
|
|
规避 OOM 手段 |
|
|
说明:
单个查询内存限制模式可以同其他两种模式混合使用。
默认配置下,默认模式为内存预留上限模式;
当 taosd 启动时,系统可用内存大小低于 5G 或预留模式下预留后低于 4G 时自动不使用查询内存管控功能,即采用不使用内存管控模式;
4.3 查询淘汰策略
查询淘汰是指当内存使用达到或即将达到上限时,通过自动驱使部分查询失败来释放内存,最终达到内存不超过上限的目的,在不同的模式下使用不同的策略来触发淘汰:
内存预留上限模式
如果任一时刻新分配内存的请求将导致系统可用内存数量低于预留大小时,将自动淘汰当前尝试分配内存的查询;
如果任一时刻系统可用内存大小(SAMS)小于预留大小时,将自动淘汰部分当前进行中的查询,直至系统可用内存大小大于预留大小;
5. 运维
默认自动引入查询内存管控功能并使用内存预留上限模式,如果与客户实际使用需求或场景不相符可以更改配置或彻底关闭此功能;预留内存越大,OOM 概率越低。
建议在部署时配置合适大小 SWAP 空间。
容器环境下因为每个 taosd 获取的内存信息都是物理机的全局信息,因此会造成信息不准确,需谨慎使用或关闭内存管控功能。
6. 使用场景
适用于所有查询,具体可根据客户场景来确定使用方式:
有明确查询内存限制 - 手动配置
查询内存上限或内存预留上限;无明确查询内存限制 - 使用内存预留上限模式,根据需要和安全性要求配置最小预留的系统可用内存数量;
不需要内存管控 - 手动配置关闭;
系统可用内存较小 - 自动或手动配置关闭;
7. 约束和限制
使用限制
当 taosd 启动时,系统可用内存大小低于 5G 或预留模式下预留后低于 4G 时自动不使用查询内存管控功能;
因为系统限制或实现原因无法获取系统的内存相关信息时自动不使用查询内存管控功能;
平台限制
暂只支持 Linux 平台;
8. 常见错误和排查
错误码 | 错误信息 |
说明 |
错误排查与处理 |
0x80000739 | “Query memory upper limit is reached” |
单个查询内存到达使用上限 |
检查单个查询内存上限配置 singleQueryMaxMemorySize 是否合理 |
0x8000073A | "Query memory exhausted" |
所有查询内存耗尽 |
|