DWS内存管控
【摘要】 1、内存简介 DWS集群有丰富的内存监控和管控工具,实际环境中经常出现内存使用率较高甚至出现"memory is temporarily unavailable"报错提示信息,本文对DWS提供的内存参数和视图,以及常见内存问题进行分析介绍,希望对大家的内存定位问题有帮助。2、内存管控相关参数和视图2.1 内存相关guc参数max_process_memory 设置单节点最...
1、内存简介
DWS集群有丰富的内存监控和管控工具,实际环境中经常出现内存使用率较高甚至出现"memory is temporarily unavailable"报错提示信息,本文对DWS提供的内存参数和视图,以及常见内存问题进行分析介绍,希望对大家的内存定位问题有帮助。
2、内存管控相关参数和视图
2.1 内存相关guc参数
- max_process_memory
设置单节点最大可用内存,当实际物理内存较大,且集群出现"memory is temporarily unavailable"报错信息时,可适当调大该参数。
- max_process_memory_auto_adjust
开启max_process_memory参数的自动调整功能,开启情况下,CM会在主备切换情况下,动态调整对应DN节点上的max_process_memory参数值,默认为on。
- shared_buffers
设置单节点使用的共享内存大小。
- cstore_buffers
设置列存和OBS、HDFS外表列存格式(orc、parquet、carbondata)所使用的共享缓冲区的大小。
- work_mem
设置内部排序操作和Hash表在开始写入临时磁盘文件之前使用的内存大小,开启内存自适应后(enable_dynamic_workload为on),收集统计信息后不再需要使用work_mem进行算子内存使用调优,由系统根据当前负载情况,为每个语句生成计划,并估算每个算子的内存使用量和整个语句的内存使用量。系统根据负载情况和整个语句内存使用量进行队列调度,所以多并发场景会出现语句排队的情况。
- query_mem
设置执行作业所使用的内存。如果设置的query_mem值大于0(最小为32MB, 否则不生效),在生成执行计划时,优化器会将作业的估算内存调整为该值。
- query_max_mem
设置执行作业所能够使用的最大内存。如果设置的query_max_mem值大于0(最小为32MB, 否则不生效),在生成执行计划时,优化器会根据该值来设置算子的可用内存。当作业执行时所使用内存超过该值时,将报错退出。
- enable_memory_limit
启用逻辑内存管理模块。
- enable_global_memctl
是否开启全局内存管理功能,资源管理相关参数。
- memory_spread_strategy
调整自定义资源池DN内存扩展的策略,资源管理相关参数。
- dynamic_memory_quote
自适应负载场景下,设置内存控制的比重,即可以使用系统最大可用内存的比例,资源管理相关参数。
- disable_memory_protect
禁止内存保护功能,该参数只适用于在系统内存不足时进行系统诊断和调试,正常运行时请保持该参数配置为off。
- enable_wlm_internal_memory_limit
设置是否开启负载管理对语句估算内存的内置限制功能。
- enable_strict_memory_expansion
开启对语句内存扩展的严格限制
- allow_zero_estimate_memory
是否允许语句估算内存为0。
- wlm_memory_feekback_adjust
是否开启动态负载管理中的内存负反馈功能。
2.2 内存相关视图
- pv_total_memory_detail
统计当前节点内存使用信息,多节点的视图为pgxc_total_memory_detail。
max_process_memory |
实例最大可用内存阈值,guc参数max_process_memory,支持reload设置 |
process_used_memory |
实例当前已使用的内存,/proc/self/statm中第二个结果,即进程的res |
max_dynamic_memory |
实例可使用的最大动态内存,其值为max_process_memory - cstore_buffers - udf_reserved_memory - max_shared_memory |
dynamic_used_memory |
实例当前已使用的动态内存,一般情况下,主要来自共享内存(pg_shared_memory_detail)和普通内存(pv_session_memory_detail) |
dynamic_peak_memory |
dynamic_used_memory内存的峰值 |
dynamic_used_shrctx |
共享内存使用的内存,包含在dynamic_used_memory中 |
dynamic_peak_shrctx |
dynamic_used_shrctx共享内存的峰值 |
max_shared_memory |
共享内存的大小(详见CreateSharedMemorySegments) |
shared_used_memory |
最大共享内存,主要为shared_bubffers,/proc/self/statm中第三个结果,即进程的共享内存使用 |
max_cstore_memory |
guc参数cstore_buffers |
cstore_used_memory |
CUCache数据和元数据的使用内存 |
max_sctpcomm_memory |
guc参数comm_usable_memory |
sctpcomm_used_memory |
通信使用的内存 |
sctpcomm_peak_memory |
通信内存峰值 |
max_topsql_memory |
guc参数session_history_memory |
topsql_used_memory |
TopSQL使用的内存 |
topsql_peak_memory |
TopSQL内存使用峰值 |
other_used_memory |
(process_used_memory - shared_used_memory - text(/proc/self/statm 第四个结果,代码段)) - dynamic_used_memory - cstore_used_memory - gpu_dynamic_used_memory - mmap_used_memory,一般为系统中不受memory context管理的第三方组件所占用的内存。如果过大,也有可能是内存泄漏。 |
gpu_max_dynamic_memory |
GPU内存最大值,GPU加速插件中接口获取 |
gpu_dynamic_used_memory |
GPU使用的内存,GPU加速插件中接口获取 |
gpu_dynamic_peak_memory |
GPU内存使用峰值,GPU加速插件中接口获取 |
pooler_conn_memory |
pooler连接占用内存大小 |
pooler_freeconn_memory |
pooler空闲连接占用的内存大小 |
storage_compress_memory |
列存压缩和解压缩占用的内存大小 |
udf_reserved_memory |
为UDF worker进程预留的内存大小 |
mmap_used_memory |
调用mmap申请的内存 |
SCHEDULER_USED_MEMORY |
异步IO使用内存 |
SCHEDULER_PEAK_MEMORY |
异步IO使用内存峰值 |
DISK_CACHE_USED_MEMORY |
diskCache使用内存 |
DISK_CACHE_PEAK_MEMORY |
diskCache使用内存峰值 |
查询所有节点内存使用情况
with
a as (select * from pgxc_total_memory_detail where memorytype = 'dynamic_used_memory'),
b as (select * from pgxc_total_memory_detail where memorytype = 'dynamic_peak_memory'),
c as (select * from pgxc_total_memory_detail where memorytype = 'max_dynamic_memory'),
d as (select * from pgxc_total_memory_detail where memorytype = 'process_used_memory'),
e as (select * from pgxc_total_memory_detail where memorytype = 'other_used_memory'),
f as (select * from pgxc_total_memory_detail where memorytype = 'max_process_memory')
select
a.nodename,
a.memorymbytes as dynamic_used_memory,
b.memorymbytes as dynamic_peak_memory,
c.memorymbytes as max_dynamic_memory,
d.memorymbytes as process_used_memory,
e.memorymbytes as other_used_memory,
f.memorymbytes as max_process_memory
from
a, b, c, d, e, f
where
a.nodename = b.nodename
and b.nodename = c.nodename
and c.nodename = d.nodename
and d.nodename = e.nodename
and e.nodename = f.nodename
order by
a.nodename;
- pv_session_memory_detail
查看内存上下文级别的内存占用详细信息
名称 |
类型 |
描述 |
sessid |
text |
线程启动时间+线程标识(字符串信息为timestamp.threadid)。 |
sesstype |
text |
线程名称。 |
contextname |
text |
内存上下文名称。 |
level |
smallint |
当前上下文在整体内存上下文中的层级。 |
parent |
text |
父内存上下文名称。 |
totalsize |
bigint |
当前内存上下文的内存总数,单位Byte。 |
freesize |
bigint |
当前内存上下文中已释放的内存总数,单位Byte。指的是分配给当前内存上下文的内存总数,为freesize+usedsize之和。 |
usedsize |
bigint |
当前内存上下文中已使用的内存总数,单位Byte。是指当前你内存上下文中预留的内存,只有这个内存上下文可以使用,其他线程及其他内存上下文都不能使用,实际还是当前内存上下文中占用的。 |
# 在当前dn节点查看内存占用较高的context
select sessid, contextname, level,parent, pg_size_pretty(totalsize) as total ,pg_size_pretty(freesize) as freesize, pg_size_pretty(usedsize) as usedsize, datname,query_id, substr(query, 1,60) as query from pv_session_memory_detail a , pg_stat_activity b where split_part(a.sessid,'.',2) = b.pid order by totalsize desc limit 10;
- pg_shared_memory_detail
查询所有已产生的共享内存上下文的使用信息。
名称 |
类型 |
描述 |
contextname |
text |
内存上下文的名字 |
level |
smallint |
当前上下文在整体内存上下文中的层级 |
parent |
text |
上级内存上下文 |
totalsize |
bigint |
共享内存总大小,单位Byte |
freesize |
bigint |
共享内存剩余大小,单位Byte |
usedsize |
bigint |
共享内存使用大小,单位Byte |
# 在dn节点上查看共享内存使用较高的context
select * from pg_shared_memory_detail order by totalsize desc limit 30;
- pv_session_memory
pv_session_memory统计Session级别的内存使用情况,包含执行作业在数据节点上Postgres线程和Stream线程分配的所有内存。
名称 |
类型 |
描述 |
sessid |
text |
线程启动时间+线程标识。 |
init_mem |
integer |
当前正在执行作业进入执行器前已分配的内存,单位MB。 |
used_mem |
integer |
当前正在执行作业已分配的内存,单位MB。 |
peak_mem |
integer |
当前正在执行作业已分配的内存峰值,单位MB。 |
2.3 内存相关函数
- pv_session_chunk_dump(tid, contextname)
将tid对应的线程下memory context当前生成的所有chunk信息输出到文件,需要搭配pv_session_chunk_detail函数使用。
名称 |
类型 |
描述 |
tid |
integer |
线程号 |
contextname |
cstring |
memory context的名称 |
- pv_session_chunk_detail(tid, contextname)
用于打印tid对应的线程下memory context生成的所有chunk信息,需要先执行pv_session_chunk_dump函数。
名称 |
类型 |
描述 |
tid |
integer |
线程号 |
contextname |
cstring |
memory context的名称 |
输出字段信息:
名称 |
类型 |
描述 |
parent |
text |
memory context的父结点名字,没有父结点为null |
level |
integer |
memory context在树中的层级 |
chunk_size |
bigint |
chunk大小 |
requested_size |
bigint |
chunk已使用大小 |
file_name |
text |
chunk申请位置对应文件名 |
line_number |
integer |
chunk申请位置对应行号 |
- pg_shared_chunk_dump(contextname)
将共享memory context当前生成的所有chunk信息输出到文件,需要搭配pg_shared_chunk_detail函数使用。
名称 |
类型 |
描述 |
tid |
integer |
线程号 |
contextname |
cstring |
memory context的名称 |
- pg_shared_chunk_detail(tid, contextname)
用于打印共享memory context生成的所有chunk信息,需要先执行pg_shared_chunk_dump函数。
名称 |
类型 |
描述 |
contextname |
cstring |
memory context的名称 |
输出字段信息:
名称 |
类型 |
描述 |
parent |
text |
memory context的父结点名字,没有父结点为null |
level |
integer |
memory context在树中的层级 |
chunk_size |
bigint |
chunk大小 |
requested_size |
bigint |
chunk已使用大小 |
file_name |
text |
chunk申请位置对应文件名 |
line_number |
integer |
chunk申请位置对应行号 |
效果演示:
select * from pg_shared_memory_detail order by totalsize desc;
select * from pg_shared_chunk_dump('RunTime Statistics');
select file_name, line_number, sum(requested_number) rn, sum(total_size) ts from pg_shared_chunk_detail('RunTime Statistics') group by 1,2 order by 3 desc;
3、内存资源管控
内存资源管控仅使用于复杂作业,即动态资源管控场景下语句估算内存大于等于32MB的内存。
- 实例级别的内存管理
实例级别的内存管理通过max_process_memory实现,用户通过调整该参数,进而影响max_dynamic_memory,控制集群整体可以内存,值得注意的是,在ccn排队逻辑中,仲裁作业是排队还是运行的逻辑是比较作业的估算内存(语句单dn内存估算,cn内存不管控)与集群剩余可用内存,其中集群可用内存就是该集群下所有dn节点中最小的max_dynamic_memory。
- 资源池级别内存管控
资源池级别的内存管控通过创建资源池指定的mem_percent参数实现,该参数和资源池慢车道排队强相关。资源池作业级别资源管控,主要通过mem_limit参数实现,该参数主要限制资源池复杂语句的最大估算内存,防止语句估算内存过高引起异常排队,与此对应的是数据库级别语句内存管控参数query_mem,该参数直接指定语句估算内存,注意区别。
4、内存问题常用定位语句
- 监控当前实例内存总体实时使用情况
SELECT * FROM pg_total_memory_detail;
- 监控当前实例共享内存实时使用情况
select * from pg_shared_memory_detail order by totalsize desc limit 30;
- 监控同一pid作业总共使用的session内存
select split_part(sessid,'.',2) pid,pg_size_pretty(sum(totalsize)) total_size,count(*) context_count from pv_session_memory_detail group by pid order by sum(totalsize) desc limit 50;
- 会话级内存,包括memoryContext名字,query信息,threadid,内存使用大小(totalsize)等信息
select sessid,contextname,level,parent,pg_size_pretty(totalsize) as total,pg_size_pretty(freesize) as freesize,pg_size_pretty(usedsize) as usedsize,datname,usename,application_name,client_addr,now() - query_start as dur,query_id,substr(query,1,100) from pv_session_memory_detail a, pg_stat_activity b where split_part(a.sessid,'.',2) = b.pid order by totalsize desc limit 50;
- 监控当前节点内存使用最高的语句
select b.state as state, a.sessid as sessid, b.query_id as query_id, substr(b.query,1,100) as query, sum(totalsize) as totalsize, sum(freesize) as freesize, sum(usedsize) as usedsize from pv_session_memory_detail a , pg_stat_activity b where split_part(a.sessid,'.',2) = b.pid group by state,sessid,query_id,query order by totalsize desc limit 50;
- 监控当前节点不同状态语句的内存使用信息
select b.state, sum(totalsize) as totalsize, sum(freesize) as freesize, sum(usedsize) as usedsize from pv_session_memory_detail a , pg_stat_activity b where split_part(a.sessid,'.',2) = b.pid group by b.state order by totalsize desc limit 20;
- 监控当前节点不同用户语句的内存使用信息
select b.usename, sum(totalsize) as totalsize, sum(freesize) as freesize, sum(usedsize) as usedsize from pv_session_memory_detail a , pg_stat_activity b where split_part(a.sessid,'.',2) = b.pid group by b.usename order by totalsize desc limit 30;
- 监控作业并发信息
select * from gs_respool_monitor;
- 监控所有节点运行时间最长的作业
select coorname,usename,client_addr,sysdate-query_start as dur, state, enqueue, waiting,pid,query_id, substr(query,1,50) from pgxc_stat_activity where usename != 'omm' and usename != 'Ruby' and state = 'active' order by dur desc limit 100;
- 所有节点线程运行状态统计
select node_name,wait_status,wait_event,count(*) as cnt from pgxc_thread_wait_status where wait_status <>'wait cmd' and wait_status <> 'synchronize quit' and wait_status <>'none' and wait_status<>'wait stream task' and wait_status <> 'wait node' group by 1,2,3 order by 4 desc limit 100;
5、案例
出现内存不可用的报错常见的有以下场景:
①语句中相关的表未及时做analyze,统计信息差距过大,导致执行计划和内存估算出现步骤,该情况可以及时对相关表进行analzye矫正内存估算情况;
②SQL不下推。如内存问题在CN上出现,很大程度上是因为某个SQL的不下推导致。通过explain确认语句不下推后,将语句进行修改使其下推;
③中间结果集倾斜。GaussDB DWS在进行表关联时,优先使用hash join,需要对一张表创建哈希表,当哈希表大小超过work_mem限制时,会进行下盘避免内存占用过多,但如果原始表里或中间结果集中在join列上存在大量重复数据,是无法下盘的,必须全部载入内存中进行计算,会造成内存大量占用。如pv_session_memory_detail中占用内存最大的内存上下文为hash context,则大概率是此问题。这时应对客户的表和SQL进行分析,对重复值进行处理后,再进行关联。
④max_process_memory设置过小或者shared_buffer/cstore_buffer设置过大。
⑤作业并发较大,需降低并发,值得注意的是快车道是不管控内存和cpu资源的,所以当快车道作业并发量较大时,也会占用较高内存
案例1:
某局点出现内存不可用报错,需对其进行分析:
步骤1:作业内存不可用已经发生,可通过日志查看确定内存使用较高的context
步骤2:通过日志可以看出Exprcontext内存使用较高,可知作业数据倾斜率较大导致的内存使用较高,可对作业的相关表进行重分布,使数据均衡
6、参考文献
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)