Out of Memory,简称 OOM ,代表内存耗尽的一种异常状态。OOM 的表现形式千差万别,可能是服务异常终止,亦或是系统性能急剧下降。这一现象背后的根本问题在于内存的不足。造成 OOM 的原因有很多,其中包括数据量庞大、频繁的数据写入和查询操作,以及可能存在的内存泄漏问题。了解这些原因,能够帮助我们更好地规划、优化和维护系统,从而提高其稳定性和性能。
本文将针对在使用 DolphinDB 时遇到 OOM 这一问题,对造成 OOM 的原因进行定位分析和归纳总结,并给出相应解决方案以及规避建议。
DolphinDB 的内存分配机制基于 TCMalloc 实现,TCMalloc 是一个由 Google 开发的用户空间内存分配器,用于替代标准的 C 库中的 malloc 和 free 函数。TCMalloc 的设计目标是提高多线程环境下的内存分配性能,并降低内存碎片。
TCMalloc 在 DolphinDB 中主要负责管理动态内存,而静态内存的释放则由用户手动管理。这种方式可能导致多样化的内存需求,从而增加了内存消耗和内存压力。在某些情况下,这可能最终导致内存不足(OOM)的问题。
DolphinDB 内存不足的常见错误,错误示例包括:
导致 DolphinDB OOM 的主要原因可分为以下三类:
如果内存不足错误只是偶尔出现,或者在短时间内出现,这代表着系统正常运行中的短时异常,无需特别处理这种短暂的内存问题。然而,如果错误在多个连接上多次发生,并且持续数秒或更长时间,就需要采取一些诊断和解决方案来解决内存问题。
诊断内存使用模式:
使用监控工具来诊断 DolphinDB 内存使用模式。查看内存使用的波动情况,分析内存占用的高峰时刻。这有助于确定是什么导致了内存不足的问题。
检查 DolphinDB 中组件运行任务:
来自 DolphinDB 内部组件的内存压力也可能导致内存不足错误。比如数据查询计算功能、流计算引擎、写入任务、后台计算任务组件消耗内存,需要通过内存管理函数跟踪,了解这些组件在 DolphinDB 中如何分配内存。 想要解决内存不足问题,必须确定哪些组件消耗的内存最大。 例如,如果发现某个 session 的内存管理显示较大的内存分配,则需要了解当前 session 消耗这么多内存的原因。 用户可能会发现某些查询没有走分区扫描,可以通过优化 SQL 语句来优化查询或者建立合适的 sortKey,减少内存的消耗。所以需要检查 DolphinDB 中正在执行的查询和任务,以确定是否有复杂或者资源密集型的操作。
检查通过外部加载到 DolphinDB 中的插件内存使用情况:
内存压力可能由于 DolphinDB 内部引擎导致,也可能由于 DolphinDB 进程中运行的某些插件导致,如 kafka 、mysql、odbc 以及 hdfs 插件或者自定义插件等等。插件若设计不佳,容易消耗大量内存。 例如,假设任意插件将来自外部源的 20 亿行数据缓存到 DolphinDB 内存中,DolphinDB 进程中消耗的内存会很高,从而导致内存不足错误。
插件的内存使用目前仅仅记录在日志中,你可以分析 DolphinDB 的日志,来查看是否有异常情况或者报错信息。
更新或优化配置:
有三大类外部限制导致 DolphinDB 内存不足:
OS 内存压力是指在同一操作系统中,一个或多个应用程序共同耗尽了可用的物理内存。为了响应新的应用程序对资源的请求,OS 会尝试释放一些内存。然而,当其他应用程序消耗完 OS 的内存,DolphinDB 可用内存将不足,由此产生 OOM 的报错。
针对此种情况,你可以考虑更新 DolphinDB 的版本,或者调整配置参数以优化系统性能。
若要诊断来自 DolphinDB 引擎内部组件的内存压力,请使用以下方法:
getSessionMemoryStat()
,获取当前节点所有连接会话的内存占用状态,返回一张表包含以下字段:
缓存类型对应解释见下表:
注意:2.00.11 和1.30.23 版本启用了维度表自动回收机制。
通过参数 warningMemSize 设置的定期内存检测机制,当内存使用超过该阈值时,会尝试释放部分维度表内存。
缓存类型 | 含义 | 影响大小因素 | 释放函数 |
---|---|---|---|
__DimensionalTable__ | 维度表缓存,单位为 Bytes。 | 维度表大小以及数据量 | clearCachedDatabase |
__SharedTable__ | 共享表缓存,单位为 Bytes。 | 共享表大小以及数据量 | undef("sharedTable", SHARED) |
__OLAPTablet__ | OLAP 引擎数据库表的缓存,单位为 Bytes。 | OLAP 数据缓存大小,一般都是MB级别 | clearAllCache() |
__OLAPCacheEngine__ | OLAP 引擎 cache engine 的内存占用,单位为 Bytes。 | OLAP CacheEngine 参数有关 | flushOLAPCache() |
__OLAPCachedSymbolBase__ | OLAP 引擎 SYMBOL 类型字典编码的缓存,单位为 Bytes。 | OLAP,Symbol 数据类型大小,一般为 MB 级别 | 无需释放 |
__DFSMetadata__ | 分布式存储的元数据缓存,单位为 Bytes。 | 分布式库数量以及大小,一般为 MB 级别 | 无需释放 |
__TSDBCacheEngine__ | TSDB 引擎 cache engine 的内存占用,单位为 Bytes。 | TSDB CacheEngine 参数有关 | flushTSDBCache() |
__TSDBLevelFileIndex__ | TSDB 引擎 level file 索引的缓存,单位为 Bytes。 | TSDB 常驻索引大小TSDBLevelFileIndexCacheSize 有关,默认为5% * maxMemSize | invalidateLevelIndexCache() |
__TSDBCachedSymbolBase__ | TSDB 引擎 SYMBOL 类型字典编码的缓存,单位为 Bytes。 | TSDB,Symbol 数据类型大小,一般为MB级别 | 无需释放 |
__StreamingPubQueue__ | 流数据发布队列里未处理的消息数。 | maxPubQueueDepthPerSite 参数有关 | 无需释放 |
__StreamingSubQueue__ | 流数据订阅队列里未处理的消息数。 | 流数据队列内存大小,和引擎数量以及订阅数据有关 | 管理订阅以及引擎 |
定位数据内存占用过大的具体对象,然后使用上表对应函数第四列对应函数进行释放,另外假设为 session 内变量占用内存过高,可以联系团队 DBA 使用函数?closeSessions?关闭相应 session, 操作步骤如下:
closeSessions(getSessionMemoryStat().sessionId[11]);
getRecentJobs()
?和?getConsoleJobs()
?查看是否还有超过预期运行时长的后台或交互任务。通过?cancelJob?以及?cancelConsoleJob?关闭对应 job。
若要诊断由 DolphinDB 插件引起的内存压力,请使用以下方法:
[xx plugin],Out of memory
,如果有日志中内容,可以判断是 DolphinDB 插件引发的 OOM。如果为官方发布的插件,可以通过点击官网下载页面的社群入口,将问题反馈给工程师。如果是自定义开发插件,需要定位下哪个函数导致的内存使用过高,修改插件源代码,重新编译再加载到 DolphinDB 中。Java heap space
?该异常通常出现在 DolphinDB GUI 客户端,通常情况下,直接原因为 GUI 客户端占用内存过高,而这是由于运行以下语句造成:select * from xx
需要修改 SQL 语句为
t = select * from xx
select top 1000 * from xx
这样可以降低 GUI 客户端内存占用,从而避免上述问题。
面对 DolphinDB 进程之外的外部限制以及操作系统上的内存不足情况,请使用以下方法:
输入命令
dmesg -T|grep memory
如上图,若出现了“Out of memory: Kill process”,说明 DolphinDB 使用的内存超过了操作系统所剩余的空闲内存,导致操作系统杀死了 DolphinDB 进程。解决这种问题的办法是:通过参数 maxMemSize(单节点模式修改 dolphindb.cfg,集群模式修改cluster.cfg)设定节点的最大内存使用量。需要合理设置该参数,设置太小会严重限制集群的性能;设置太大可能触发操作系统杀掉进程。若机器内存为16GB,并且只部署1个节点,建议将该参数设置为12GB(服务器内存的80%-90%之间)左右。
license().maxMemoryPerNode
//8
如上所示,结果为8,则当前 lic 限制最大使用内存为 8 GB (如果 lic 类型为社区版), DolphinDB 使用内存最大上限为8GB,如果需要申请使用更多内存,请联系 DolphinDB 技术支持工程师。
getConfig(`maxMemSize)
//16
如上所示为系统配置内存上限,结果为16,则当前 配置文件限制最大使用内存为 16 GB, DolphinDB 使用内存最大上限为16 GB,如果需要申请使用更多内存,通过参数 maxMemSize(单节点模式修改dolphindb.cfg,集群模式修改cluster.cfg)设定节点的最大内存使用量。
配置文件最大内存不可超过 DolphinDB 许可证限制的内存以及不能超过服务器内存的90%
getMemLimitOfQueryResult()
//8
如上所示为查询内存上限配置,结果为8,则当前 配置文件限制最大单次查询结果占用的内存上限为 8 GB,如果需要增大单次查询的上限,通过函数?setMemLimitOfQueryResult?进行修改。
ulimit -a
参数 | 描述 |
---|---|
max memory size | 一个任务的常驻物理内存的最大值 |
virtual memory | 限制进程的最大地址空间 |
如上图所示,操作系统会限制 DolphinDB 进程的资源上限,图上代表为不限制进程最大使用内存,如果需要修改,修改方式见?ulimit 修改方式。
top
如上图所示,有其他进程占用 操作系统资源,需要将其关闭,以减少其内存使用量。
在企业生产环境下,DolphinDB 往往作为流数据中心以及历史数据仓库,为业务人员提供数据查询和计算。当用户较多时,不当的使用容易频繁造成 OOM 影响业务使用,甚至宕机。为尽量减少此类问题的发生,现给出以下5类规避 DolphinDB OOM 的建议。
v = 1..10000000
,或者将含有大量数据的查询结果赋值给一个变量?t = select * from t where date = 2010.01.01
,v 和 t 将会在用户的 session 占用大量的内存。如果不及时释放,执行其他任务时,就有可能因为内存不足而抛出异常。用户的私有变量在其创建的 session 里面保存。session 关闭的时候,会回收这些内存。可通过?undef
?函数将其赋值为 NULL,或者关闭?session
?来及时释放变量的内存。tableAppender
?接口自动切分大数据分批次写入此类接口由于将大数据切分为多块小数据写入,没办法保证一批大数据的写入的原子性。
分布式数据库 DolphinDB 的设计十分复杂,发生 OOM 的情况各有不同。若发生节点 OOM,请按本文所述一步步排查: