问题现象
- 数据库进程内存占比较高
- 长时间占比较高
观察监控平台内存占用的变化曲线,无论当前数据库是否有业务在运行,数据库进程内存占总机器内存的比例长时间处于较高状态,且不下降。 - 执行作业期间占比较高
数据库进程在没有业务执行时,内存使用持续处于较低的状态,当有业务执行时,内存占用升高,待作业执行结束后,内存又恢复到较低的状态。 - 内存上涨不下降
数据库进程在执行业务过程中内存呈缓慢上涨趋势,且业务执行完后无下降趋势。
- 长时间占比较高
- SQL语句报内存不足错误
执行SQL语句报内存不足的错误,如下所示。
gaussdb=# select contextname, sum(totalsize)/1024/1024 sum, sum(freesize)/1024/1024, count(*) count from gs_shared_memory_detail group by contextname order by sum desc limit 10;
ERROR: memory is temporarily unavailable
DETAIL: Failed on request of size 46 bytes under queryid 281475005884780 in heaptuple.cpp:1934.
原因分析
如果出现集群内存不足或者长时间内存占用处于较高状态的情况,一般是由以下几种原因造成的。
- 内存堆积
当SQL语句执行过程中临时内存未及时释放时,会导致内存堆积。 - 并发过高导致内存占用过高
客户端向server端建立的连接数过多,导致server端创建了大量的session,占用大量内存,这种场景下会出现内存出现占用较高的情况。 - 单条SQL语句内存占用较高
对于部分SQL语句,执行过程中会使用大量的内存,导致内存出现短暂性上涨,如复杂的存储过程语句,执行时内存占用可能达到几十GB大小。 - 内存缓存过多
GaussDB Kernel引入了jemalloc开源库来管理内存管理,但是jemalloc在面对大量的内存碎片时会存在内存持续缓存不释放的问题,导致数据库进程使用的内存远远超过了实际使用的内存,具体表现为使用内存视图pv_total_memory_detail查询时,other_used_memory占用过大。如下所示。
gaussdb=# select * from pv_total_memory_detail;
nodename | memorytype | memorymbytes
----------------+-------------------------+--------------
coordinator1 | max_process_memory | 81920
coordinator1 | process_used_memory | 14567
coordinator1 | max_dynamic_memory | 34012
coordinator1 | dynamic_used_memory | 1851
coordinator1 | dynamic_peak_memory | 3639
coordinator1 | dynamic_used_shrctx | 394
coordinator1 | dynamic_peak_shrctx | 399
coordinator1 | max_backend_memory | 648
coordinator1 | backend_used_memory | 1
coordinator1 | max_shared_memory | 46747
coordinator1 | shared_used_memory | 11618
coordinator1 | max_cstore_memory | 512
coordinator1 | cstore_used_memory | 0
coordinator1 | max_sctpcomm_memory | 0
coordinator1 | sctpcomm_used_memory | 0
coordinator1 | sctpcomm_peak_memory | 0
coordinator1 | other_used_memory | 1013
coordinator1 | gpu_max_dynamic_memory | 0
coordinator1 | gpu_dynamic_used_memory | 0
coordinator1 | gpu_dynamic_peak_memory | 0
coordinator1 | pooler_conn_memory | 0
coordinator1 | pooler_freeconn_memory | 0
coordinator1 | storage_compress_memory | 0
coordinator1 | udf_reserved_memory | 0
(24 rows)
定位思路
出现内存过载的问题时,如果有内存过载的环境可以实时定位,可以使用思路一中的定位流程定位内存问题,如果现场环境已经被破坏(集群重启等导致),则按照思路二中的流程进行定位。当前GaussDB Kernel的内存管理采用内存上下文机制管理,在内存使用的统计上有着精准的统计和可视化的视图方便查询定位。
思路一:有现场环境
- 查询内存统计信息
表:信息说明
视图 | 说明 |
---|---|
pv_total_memory_detail | 全局内存信息概况。 |
pg_shared_memory_detail | 全局内存上下文内存使用详情。 |
pv_thread_memory_context | 线程级内存上下文内存使用详情。 |
pv_session_memory_context session | session级内存上下文使用详情,仅在线程池模式开启时生效。 |
- 确定内存占用分类
根据查询内存统计信息中查询出的内存统计视图结果,根据内存统计视图可以分析出如下结果。
表:分析结果
视图 | 结果 |
---|---|
process_used_memory | 数据库进程所使用的内存大小。 |
dynamic_used_memory | 已使用的动态内存。 |
dynamic_used_shrctx | 全局内存上下文已使用的动态内存。 |
shared_used_memory | 已使用的共享内存。 |
other_used_memory | 其他已使用的内存大小,一般进程释放后被缓存起来的内存会统计在内。 |
- 如果dynamic_used_memory较大,dynamic_used_shrctx较小,则可以确认是线程和session上内存占用较多,则直接查询线程和session上的内存上下文的内存占用即可确认内存堆积的具体内存上下文。
- 如果dynamic_used_memory较大,dynamic_used_shrctx和dynamic_used_memory相差不大,则可以确认是全局内存上下文使用的动态内存较大,直接查询全局的内存上下文的内存占用即可确认内存堆积的具体内存上下文。
- 如果只有shared_used_memory占用较大,则可以确认是共享内存占用较多,忽略即可。
- 如果是other_used_memory较大,一般情况下应该是出现了频繁的内存申请和释放的业务导致内存碎片缓存过多,此时需要联系华为工程师定位解决。
- 确定内存堆积原因
根据确定内存占用分类中查询出来的内存统计信息数据就可以确认数据库进程内存使用过高的原因,一般内存占用较高都是由如下2类原因导致。
表:内存较高原因
思路二:无现场环境
某些内存过载出现时会导致集群环境重启等,这种情况下没有实时的环境能够定位内存过载的原因是什么,就需要使用如下流程来定位已发生过的内存过载导致的原因。
- 查看历史内存占用曲线
在环境上查看数据库进程使用内存大小的监控数据。根据数据库在一段时间内的内存变化曲线,可以确认内存过载的时间节点,如下图所示。
从历史内存曲线中,可以看出在8月22日内存出现瞬间上涨的情况。 - 分析历史内存统计信息
根据查看历史内存占用曲线中确认的时间节点信息,使用视图gs_get_history_memory_detail可获得该时间节点的内存快照日志,快照日志以时间戳作为文件名,能够迅速查找,根据快照日志能够快速的找到内存居高的内存上下文信息,然后联系华为工程师协助即可解决。
如果没有在历史内存统计信息中找到对应时间节点的内存快照信息,此时需要关注该时间节点业务的并发量,执行语句的复杂度等,根据实际业务情况进一步分析内存上涨的原因,需联系华为工程师协助解决。
处理方法
遇到内存占用过高或者内存不足报错的场景时可以通过如下流程来分析定位原因并解决。
1.获取内存统计信息
必须要获取出现内存故障的数据库进程上的内存统计信息。
- 实时内存统计信息
查询GaussDB Kernel进程总的内存统计信息。
gaussdb=# select * from pv_total_memory_detail;
nodename | memorytype | memorymbytes
----------------+-------------------------+--------------
coordinator1 | max_process_memory | 81920
coordinator1 | process_used_memory | 14567
coordinator1 | max_dynamic_memory | 34012
coordinator1 | dynamic_used_memory | 1851
coordinator1 | dynamic_peak_memory | 3639
coordinator1 | dynamic_used_shrctx | 394
coordinator1 | dynamic_peak_shrctx | 399
coordinator1 | max_backend_memory | 648
coordinator1 | backend_used_memory | 1
coordinator1 | max_shared_memory | 46747
coordinator1 | shared_used_memory | 11618
coordinator1 | max_cstore_memory | 512
coordinator1 | cstore_used_memory | 0
coordinator1 | max_sctpcomm_memory | 0
coordinator1 | sctpcomm_used_memory | 0
coordinator1 | sctpcomm_peak_memory | 0
coordinator1 | other_used_memory | 1013
coordinator1 | gpu_max_dynamic_memory | 0
coordinator1 | gpu_dynamic_used_memory | 0
coordinator1 | gpu_dynamic_peak_memory | 0
coordinator1 | pooler_conn_memory | 0
coordinator1 | pooler_freeconn_memory | 0
coordinator1 | storage_compress_memory | 0
coordinator1 | udf_reserved_memory | 0
(24 rows