GaussDB 的内存管理采用动态内存与静态内存相结合的方式,由参数 max_process_memory 控制数据库可用的最大内存。
-
静态内存区域主要用作数据库的共享缓冲区,用于缓存数据页面,由 shared_buffers 参数控制。
-
动态内存区域,则由数据库根据需要进行动态分配,主要包括元数据的缓存、执行计划的缓存、用户建连以及内部线程的内存消耗等。
1. 内存相关参数
1.1 max_process_memory
max_process_memory :数据库最大可用内存
1.2 shared_buffers
shared_buffers:数据库行存缓存大小,建议设为 max_process_memory 的 40% 以内,用于缓存热点数据,减少磁盘 I/O。
1.3 cstore_buffers
cstore_buffers :设置列存表所使用的共享缓冲区大小。
1.4 work_mem
设置单个数据库会话中执行排序、HASH Join、Merge Join 等操作时使用的最大内存量。其作用对象是查询执行过程中的临时数据操作,建议为16 MB-64 MB,避免排序/哈希操作使用磁盘。
实际生产环境中, work_mem 值需要经过多次测试才能设定比较合理合适的值。需要注意的是 work_mem 是每单个连接用户使用的内存,也就是实际需要的内存为 max_connections * work_mem,必须保证 max_connections * work_mem 的值不要超过实际可用的内存。
1.5 maintenance_work_mem
设置如 VACUUM、CREATE INDEX、ALTER TABLE ADD FOREIGN KEY 的内存上限。这些操作通常需要大块内存以提高效率,尤其在处理大数据量时。在导入大量数据时,临时增大此参数可显著加速索引创建和外键约束的添加。
当 autovacuum 运行时,内存分配上限为 autovacuum_max_workers × maintenance_work_mem,可能引发高内存占用。此时可通过单独设置 autovacuum_work_mem 限制自动清理的内存使用。
1.6 temp_buffers
设置每个数据库会话使用的临时缓冲区的大小。
1.7 max_connections
设置最大连接数,建议为5000-10000。
1.8 effective_cache_size
虽然带有 “size” 后缀,但它并不是真正的内存分配参数,而是一个给优化器看的评估假设值。
-
即使将该参数设置为 64 GB,数据库启动或运行时也不会真的去申请这部分内存。它只存在于优化器的计算公式中。
-
设置过小(如默认的 128 MB):优化器会认为系统可用的缓存非常少。如果进行索引扫描(Index Scan),每次随机 I/O 都有可能需要从磁盘读取,代价极高。因此,优化器会更倾向于使用全表顺序扫描(Seq Scan)。
-
设置合理且较大:优化器会假设大量的数据页和索引页已经常驻在系统缓存中。此时,随机 I/O 的代价被评估得非常低,优化器会强烈倾向于使用索引扫描(Index Scan)和嵌套循环连接(Nested Loop),从而大幅提升查询性能。
该参数的建议值为可用空闲内存(包括系统剩余物理内存 + 系统的 Page Cache 缓存)的 25% ~ 50%,可以让优化器更倾向使用索引扫描而不是顺序扫描。
在实际生产环境中,由于 GaussDB 的 shared_buffers 已经缓存了大量数据,且操作系统的 Page Cache 也会缓存数据文件,通用的推荐计算公式为:
effective_cache_size =(服务器总物理内存 – max_process_memory)+ shared_buffers
1.9 max_stack_depth(执行堆栈最大深度)
-
作用:控制数据库执行复杂函数或嵌套查询时的堆栈深度安全上限,默认值为 2 MB。
-
隐患:当业务中存在大量深层嵌套、递归函数或极复杂的存储过程时,默认值可能过小,直接导致函数执行失败并报错。
-
默认值:2 MB
-
推荐设置值: 4 MB 或 8 MB (绝大多数大型生产环境的标配),但必须确保该值小于操作系统限制的 ulimit -s(stack size),通常需要留出至少 512 KB 的安全余量。
1.10 max_saved_plans(执行计划缓存数量上限)
-
作用:控制服务器缓存的执行计划最大数量,包含函数编译结果以及带 Hint 的 SELECT 查询产生的计划。
-
隐患:缓存过多会引发严重的内存占用。由于 GaussDB 的计划缓存是以进程或会话为单位隔离的,过大的设置会大幅消耗本地工作内存。
-
默认值:通常为 100 或 256。
-
推荐设置值:
-
标准 OLTP 场景:256 ~ 512 足够。
-
超复杂业务(如 ERP、核心系统、含有数千个带 Hint 复杂 SQL 的场景):可以放宽到 1000 ~ 2000。
-
避坑指南:切勿盲目设为 -1 或万级以上。GaussDB 的 Plan Cache(计划缓存)是每个 Session 独立或通过线程局部管理的。如果并发连接数是 1000,每个会话缓存 2000 个复杂的执行计划,会导致本地内存严重膨胀。
1.11 max_catcache_tuple(系统表元组缓存数量上限)
-
作用:控制服务器在内存中缓存系统表元组(Catalog Cache)的最大数量。
-
隐患:对于表、字段、分区极多的复杂高并发环境,缓存过多同样会导致系统表元组大量占用内存。将其维持在合理区间,可以在减少内存占用与提升查询编译性能之间取得最佳平衡。
-
推荐设置值:
-
中小型系统(表数量 < 5000):保持默认 65536 (即 64 K)。
-
大型/超大型复杂系统(多租户、分库分表、表和分区总数达数万甚至数十万):推荐调整为 131072 (128 K) ~ 262144 (256 K)。
-
-
调优避坑指南:
-
如果系统有海量的分区表,优化器在编译 SQL 时需要频繁访问系统表(如 pg_class, pg_attribute)。如果该值太小,导致系统表缓存频繁换出,会引起 SQL 编译变慢,CPU 飙高。
-
反之,如果将其调整到100 万以上,当遇到大量并发查询时,Catalog Cache 占用的物理内存可能会吃掉好几个 GB。因此,对于内存敏感的设备,建议通过实际压测,监控元数据内存占比来给出一个合理的折中值。
-
1.12 内存水位防线
在 GaussDB Kernel 中,资源管理的核心 GUC 参数 enable_control_group 、 enable_memory_limit 和 use_workload_manager 在默认状态下均为 on 。数据库内核通过以下公式动态计算当前的可支配内存 (即留给会话和动态执行作业的动态内存):
可支配内存=max_process_memory−shared_buffers−cstore_buffers−元数据内存
-
触发条件:当可支配内存少于 2 GB 时。
-
内核强制行为:即使您在配置文件中将 enable_memory_limit 设为 on,GaussDB Kernel 也会强制将其关闭(设置为 off)。
-
潜在风险:一旦 enable_memory_limit 被强制关闭,数据库将失去对会话、动态执行作业的严格内存限额保护,在高并发或大查询场景下极易触发操作系统的 OOM(内存溢出)机制,导致数据库进程被意外杀死。
-
在规划和调整 shared_buffers 与 cstore_buffers 时,必须留足安全余量。确保总上限 max_process_memory 减去这两个共享缓冲区及元数据预估值后,剩余空间远大于 2 GB,以防动态限额保护失效。
2. 内存导致的性能问题排查
内存导致的性能问题通常分为以下几个方面:
2.1 共享缓存区不足
共享缓存区不足,导致 SQL 的 buffer 命中率低。
为了查看相应的性能指标,可以借助 GaussDB 的管控平台或者 WDR 报告。通常情况下,TP 数据库的 buffer 命中率应该在 99%以上。如果数据库的 buffer 命中率较低,建议排查数据库的 shared_buffers 参数设置是否合理。
2.2 SQL 存在数据落盘操作
SQL 的 hash join 或者 sort 算子存在数据落盘操作,work_mem 参数控制可下盘算子可用的物理内存空间。如果 work_mem 所限定的物理内存不够,算子运算的数据将被写入临时表空间,会带来 5-10 倍的性能下降。为了优化性能,可以查看 SQL 的执行计划,如果算子存在落盘的情况,可适当调整 work_mem 参数值。
2.3 数据库动态内存不足
数据库动态内存不足,导致业务执行报错(ERROR:memory is temporarily unavailable )或者性能不足。
–内存消耗高的 SQL 语句
select unique_sql_id,substr(query,1,50) as query ,n_calls,round(total_elapse_time/n_calls/1000,2) avg_time,round(total_elapse_time/1000,2) as total_time,hash_mem_used,sort_mem_used from dbe_perf.statement t where n_calls>10 and avg_time>3 order by (hash_mem_used+sort_mem_used) desc;
借助 SQL 的执行计划分析,检查是否有不合理的 join 顺序,或者是否存在非必要的排序操作,从而避免消耗大量内存。如果需要排查由非业务 SQL 语句导致的异常的内存消耗问题,比如内存堆积、内存泄露等。
|
max_process_memory |
实例可用最大内存阈值,由 guc 参数 max_process_memory 控制。 |
|
process_used_memory |
实例当前已使用的内存值,从 OS top 命令 RES 列取得。 |
|
max_dynamic_memory |
实例可使用的最大动态内存 |
|
dynamic_used_memory |
实例当前已使用的动态内存 |
|
dynamic_peak_memory |
实例从启动后到目前为止的动态内存峰值 |
|
dynamic_used_shrctx |
当前已使用的共享内存上下文内存量,也记录在 dynamic_used_memory 内 |
|
dynamic_peak_shrctx |
共享内存上下文峰值。如减去 sctpcomm_peak_memory 后超过 1G, |
|
max_shared_memory |
最大共享内存,主要为 shared_bubffers。 |
|
shared_used_memory |
实例当前已使用的共享内存,从 OS top 命令 SHR 列取得 |
|
max_cstore_memory |
列存 buffer 可使用的最大内存,由 guc 参数 cstore_buffers 控制。 |
|
cstore_used_memory |
实例当前已使用的列存 buffer. |
|
max_sctpcomm_memory |
通信模块可使用的最大内存。 |
|
sctpcomm_used_memory |
通信模块已使用的最大内存 |
|
sctpcomm_peak_memory |
通信模块峰值内存 |
|
ther_used_memory |
其他内存占用,为该 gaussdb 进程 OS RES – dynamic_used_memory- shared_used_memory- cstore_used_memory 后的值。 |
–查看节点内存消耗情况
select * from pg_total_memory_detail order by memorymbytes desc; –查看此视图需要开启内存保护特性 disable_memory_protect 默认为 off
nodename | memorytype | memorymbytes
———-+————————-+————–
cn_5001 | max_process_memory |10240
cn_5001 | max_dynamic_memory |7041
cn_5001 | max_shared_memory |1452
cn_5001 | process_used_memory |1449
cn_5001 | max_backend_memory |1314
cn_5001 | shared_used_memory |705
cn_5001 | dynamic_peak_memory |627
cn_5001 | dynamic_used_memory |594
cn_5001 | dynamic_used_shrctx |199
cn_5001 | dynamic_peak_shrctx |199
cn_5001 | other_used_memory |81
cn_5001 | max_cstore_memory |32
cn_5001 | backend_used_memory |1
–ERROR: dn_6004_6005_6006: unsupported view for memory protection feature is disabled.
select nodename,memorytype ,memorymbytes from dbe_perf.GLOBAL_MEMORY_NODE_DETAIL order by 3 desc;
–ERROR: dn_6007_6008_6009: unsupported view for memory protection feature is disabled.
SELECT * FROM pgxc_total_memory_detail ORDER BY totalsize desc LIMIT 100;
–连接异常节点,查看当前节点动态内存上下文消耗情况
select contextname, pg_size_pretty(sum(totalsize)) as totalsize,pg_size_pretty(sum(freesize)) as freesize, count(*) cnt
from pv_session_memory_detail group by contextname order by sum(totalsize) desc limit 10;
contextname | totalsize | freesize | cnt
———————————+———–+————+—–
DefaultTopMemoryContext | 48 MB | 5752 kB | 113
LocalSysCacheShareMemoryContext | 43 MB | 3977 kB | 109
ThreadTopMemoryContext | 38 MB | 656 kB | 109
CBBTopMemoryContext | 26 MB | 1488 kB | 113
LocalSysCacheTopMemoryContext | 19 MB | 2718 kB | 109
gs_signal | 16 MB | 6126 kB |1
StorageTopMemoryContext | 9386 kB | 706 kB | 113
Undo Recycler | 8200 kB | 8048 bytes |1
CommunicationTopMemoryContext | 6629 kB | 1216 kB | 113
ExecutorTopMemoryContext | 5413 kB | 1035 kB | 113
–totalsize 当前内存上下文的内存总数,单位字节。指的是分配给当前内存上下文的内存总数,为freesize+usedsize之和。
–freesize 当前内存上下文中已释放的内存总数,单位字节。是指当前你内存上下文中预留的内存,只有这个内存上下文可以使用,其他线程及其他内存上下文都不能使用,
–实际还是当前内存上下文中占用的。
–结合pg_stat_activity视图可以找到哪个语句使用的memcontext最多
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, query
from pv_session_memory_detail a , pg_stat_activity b
where split_part(a.sessid,'.',2) = b.pid order by totalsize desc limit 100;
–紧急恢复
EXECUTE DIRECT ON(cn_5001) 'SELECT pg_terminate_backend(139780156290816)';
–查看不同会话状态的内存使用,尤其关注是否存在空闲连接过多导致内存占用高
select b.state,pg_size_pretty(sum(totalsize)) as totalsize, pg_size_pretty(sum(freesize)) as freesize, pg_size_pretty(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 100;
state | totalsize | freesize | usedsize
——–+———–+———-+———-
idle | 36 MB | 4787 kB | 32 MB
active | 24 MB | 2958 kB | 21 MB
| 15 MB | 1975 kB | 13 MB
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 asdynamic_peak_memory,
c.memorymbytes as max_dynamic_memory,d.memorymbytes asprocess_used_memory,
e.memorymbytes as other_used_memory,f.memorymbytes asmax_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;
–查询每个会话使用的内存
SELECT sessid, pg_size_pretty(sum(totalsize)) as totalsize, count(1) as cnt FROM
pv_session_memory_context GROUP BY sessid ORDER BY 2 DESC LIMIT 10;
3. 内存占用高的场景分析
3.1 空闲连接过多导致内存占用
如果 idle 状态的 totalsize 占用很多内存。解决措施:清理 idle 状态的空闲连接。CLEAN CONNECTION TO ALL FOR DATABASE xxxx;
clean connection 只能清理 pg_pooler_status 中 in_used 是 f 状态的空闲连接,不能清理 in_used 状态为 t 的连接,in_used 为 t 一般是执行了 pbe 语句导致 cn 和 dn 的空闲连接。如果上述方法不能释放,只能尝试清理 cn 和客户端的连接。清理 cn 和 dn 之间的连接,可以尝试在 cn 上找到是 idle 状态的空闲连接,此操作会断掉 cn 和客户端的连接,谨慎执行。
select 'execute direct on ('||coorname||') ''select pg_terminate_backend('||pid||')'';' from pgxc_stat_activity where usename not in ('Ruby', 'omm') and state='idle';
3.2 内存参数不合理
内存参数不合理,语句使用内存(max_dynamic_memory)被挤占。语句使用内存 max_dynamic_memory = max_process_memory – shared_buffers – cstore_buffers – udf_memory_limit ,排查是否是(shared_buffers,cstore_buffers,udf_memory_limit)设置过大,导致语句内存被挤占导致内存不足。
也有可能是 Stream 线程池缓存了很多线程,每个线程占用一些内存导致整体占用内存较大,查询是否是 stream 线程池占用很多内存,stream 线程池一般是在 dn 上,需要连接 dn 查询以下语句。
select b.application_name, pg_size_pretty(sum(totalsize)) as totalsize, pg_size_pretty(sum(freesize)) as freesize, pg_size_pretty(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.application_name order by totalsize desc limit 100; select b.application_name, 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.application_name order by totalsize desc limit 100;
3.3 语句占用内存过多
如果是 active 状态的语句内存占用多,说明是正在执行语句占用的内存多导致的。
–查询内存占用多的语句
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 and usename not in ('Ruby', 'omm') group by state,sessid,query_id,query order by totalsize desc limit 100;
找到语句后,根据 query_id 去对应的 cn 上进行查杀这个异常 sql
–查询哪些用户占用了大量内存
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 100;
以上就是 GaussDB 内存参数,以及内存方面的诊断思路。
转载:实战派K8S&DB



