内存泄露处理方案

1 监控指导

1.1 如何判断内存池空闲还是紧张

v$mem_pool可以查看所有内存池的使用信息。当前系统的内存池总大小可以通过以下语句查询,单位是M:
select
name, --内存池名称
is_shared, --是否是共享的
is_overflow, --是否用到了备份池
org_size/1024.0/1024.0, --内存池初始大小
TOTAL_size/1024.0/1024.0, --内存池总大小(包括扩展的)
RESERVED_SIZE/1024.0/1024.0, --当前已分配大小(包括扩展的)
DATA_SIZE/1024.0/1024.0, --实际有效字节
EXTEND_SIZE, --每次扩展多少
TARGET_SIZE, --目标大小
N_EXTEND_NORMAL , --TARGET范围内累计扩展次数
N_EXTEND_EXCLUSIVE --超过TARGET累计扩展次数
from v$mem_pool
order by TOTAL_size desc;
注意:
1、N_EXTEND_EXCLUSIVE如果长期大于0,说明长期从池外扩展,可能存在内存泄露。需要重点关注。
2、用到备份池的话,需要保持高度关注,此时系统非常危险。
3、内存池创建的线程号creator可以与session的thrd_id关联,查看对应的某个会话的内存使用情况。
4、如果RESERVED_SIZE比org_size小,说明内存池非常空闲,需要把对于的初始内存放小,否则浪费。
5、如果TOTAL_size比TARGET_SIZE大,说明内存池不够,经常向池外申请,需要把对于的参数调大。尽量保持每个池自持。

1.2 如何判断BUFFER空闲还是紧张

select
name, --缓冲区名称
n_pages, --页数
free, --空闲页数目
N_DISCARD64 --淘汰的页数
from v$bufferpool
注意重点:
1、如果free很多说明该缓冲区很空闲,可以适当的调整降低buffer缓冲区参数值。
2、如果free项为0,或者N_DISCARD64非零,表示该缓冲区经常淘汰。这种情况,就说明对应的缓冲区参数太小,导致频繁淘汰,需要调整对应的缓冲区的参数。

1.3 达梦内存总量

达梦数据库使用的内存大致等于BUFFER + MPOOL,对应的 SQL 语句为:
select
(select sum(n_pages * page_size)/1024/1024 from v$bufferpool)||‘MB’ as BUFFER_SIZE,
( select sum(total_size)/1024/1024 from v$mem_pool)||‘MB’ as mem_pool,
(select sum(n_pages * page_size)/1024/1024 from v$bufferpool)+(select sum(total_size)/1024/1024 from v$mem_pool)||‘MB’ as TOTAL_SIZE
From dual;

1.4 使用内存最多的sql

V$SQL_STAT 需要ENABLE_MONITOR=1才开始监控。其中5~58列中的监控项,可以通过SP_SET_SQL_STAT_THRESHOLD()设置监控阀值,超过阀值才开始监控。
例如:监控sql执行在1秒以上的SQL语句使用的内存
SELECT “SESSID”, MAX_MEM_USED||‘KB’,SQL_TXT FROM V$SQL_STAT order by MAX_MEM_USED DESC;
通过此SQL语句确定使用内存较大的SQL,进行针对性的优化,如消除HASH,SORT,DISTINCT 等操作。该查询只能查询当前活动 STMT 上的语句消耗情况,历史情况需要查询V$SQL_STAT_HISTORY ,该视图上保留 1W 行SQLSTAT 历史信息.

1.5 单个会话内存使用总量

SELECT
A.CREATOR ,
B.SQL_TEXT ,
SUM(A.TOTAL_SIZE)/1024.0/1024.0 TOTAL_M, --当前总量(包括扩展)
SUM(A.DATA_SIZE) /1024.0/1024.0 DATA_SIZE_M --实际使用量
FROM
V$MEM_POOL A,
V$SESSIONS B
WHERE
A.CREATOR = B.THRD_ID
GROUP BY
A.CREATOR,
B.SQL_TEXT
ORDER BY
TOTAL_M DESC;

1.6 内存增长监控

–打开内存泄露检查参数MEMORY_LEAK_CHECK
alter system set ‘MEMORY_LEAK_CHECK’=1 ;
–执行查询
select * from V$MEM_REGINFO ORDER BY REFNUM DESC
如果看到REFNUM值特别的大,一直不会变小,说明该内存存在堆积,需要具体的分析。其中fname指定了该内存池的内存来自哪个文件,lineno指定是在该文件哪一行。

1.7 通过v$sysstat视图监控内存的使用情况

select name ,stat_val/1024.0/1024.0 from v$sysstat where CLASSID=11 ;
其中:
memory pool size in bytes: 内存池总的大小
memory used bytes: 内存池使用的内存大小
memory used bytes from os: 内存池从操作系统分配的大小

2 模拟场景演练&分析

2.1内存泄露检查

内存泄漏(Memory Leak)是指程序中已动态分配的内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
达梦数据库中提供MEMORY_LEAK_CHECK参数进行内存泄漏检查,如果打开MEMORY_LEAK_CHECL开关,系统会在VKaTeX parse error: Double subscript at position 478: … 1000 MEMORY_N_̲POOLS = 4 MEMOR…mem_pool where n_extend_exclusive >0
在这里插入图片描述
从上面可以分析出:共享池有4个,每一个原始大小为250M,目标大小为250M,每个池池外扩展了将近1G,扩展的内存都是来源于操作系统。主要是dict和sql cache managerment进行扩展,导致共享池扩展。SQL CACHE MANAGERMENT扩展了4.2G和共享池的扩展差不多。
此时我们通过V M E M R E G I N F O 视图,查看操作系统分配情况: s e l e c t s u m ( R E F N U M ) , s u m ( R E S E R V E D S I Z E ) / 1024 / 1024 f r o m V MEM_REGINFO视图,查看操作系统分配情况: select sum(REFNUM),sum(RESERVED_SIZE)/1024/1024 from V MEMREGINFO视图,查看操作系统分配情况:selectsum(REFNUM),sum(RESERVEDSIZE)/1024/1024fromVMEM_REGINFO where POOL like ‘NULL%’;
在这里插入图片描述
可以看出pool=NULL,是从操作系统分配,分配了17866次,内存大小为4.185G,和上面分析的差不多。当程序执行完,再次查询确认已经释放:
在这里插入图片描述
可以清晰的看到达梦数据库在运行过程中分配的内存,已经释放给操作系统。查看buffer和mpool的各自使用总量几乎为初始值
在这里插入图片描述
注意:如果REFNUM值很大,当程序运行结束也没下降,意味这当前由这行代码申请的内存一直没有被释放。此时需要提高警惕,排查系统是否出现内存泄漏。

2.2内存占用过高(oom)

上述的程序在服务器不重启的情况下,再次运行tpcc测试,此时数据库因为内存分配不足,导致系统处于等待状态,最终导致被操作系统oom。资源使用情况如下图所示:
在这里插入图片描述
达梦数据库使用的内存:
在这里插入图片描述
从top看达梦数据库的进程中virt和res中的内存要比实际使用的要高,怀疑是glibc库的malloc/free有自己的内存管理机制,并不会将这部分内存马上归还给OS。为了验证这个问题,又做了一个实验。
从上面我们看出SQL CACHE MANAGERMENT的扩展有4.2G都是来源于共享池。而共享池的扩展又来源于操作系统,这样就导致该进程的VIRT和RES比较高。即使程序free了分配的内存,glibc也没有及时将这部分内存归还给操作系统,导致下次在跑tpcc的时候又在分配内存,导致内存的增长,最终被操作系统oom。因此根据这个思路既然是SQL CACHE MANAGERMENT的计划池发生了扩展,那么将CACHE_POOL_SIZE设置大一点,尽量该内存池自持。因此将CACHE_POOL_SIZE设置1000,多次运行tpcc测试,内存的占用保持不变,未发生oom。监控内存使用情况如下:
在这里插入图片描述
几乎和我们通过视图查询出来的差不多。
下面对比下从v$sysstat查看出来的内存池的使用情况:
在这里插入图片描述
从这里看出内存池使用了1731M+buffer(2g),和top中看到的res值差不多。
总结:尽量保持每个内存池自持,减少从池外分配。

2.3 关注备份池的使用

1、操作系统物理内存:7812M
2、数据库的主要参数设置:
MEMORY_POOL = 500
MEMORY_TARGET = 0
BUFFER = 1000
RECYCLE = 100
SORT_BUF_SIZE = 2048
DICT_BUF_SIZE = 1000
SESS_POOL_SIZE = 1024
VM_POOL_SIZE = 64
3、数据库的前台命令行打印:
out of memory, fail to allocate memory from OS
out of memory, fail to allocate memory from OS
out of memory, fail to allocate memory from OS
out of memory, fail to allocate memory from mem pool: BACKUP POOL
out of memory, fail to allocate memory from OS
out of memory, fail to allocate memory from mem pool: BACKUP POOL
out of memory, fail to allocate memory from OS
out of memory, fail to allocate memory from OS
out of memory, fail to allocate memory from OS
out of memory, fail to allocate memory from mem pool: BACKUP POOL
out of memory, fail to allocate memory from OS
out of memory, fail to allocate memory from mem pool: BACKUP POOL
Killed
当从操作系统分配失败,转而向备份池申请,最终导致被操作系统oom了。当看到从备份池分配时,需要重点关注。存在很大的可能会被操作系统kill。内存池在申请内存时的原则是:超过扩展的大小的内存先从共享池,共享池不够,共享池在从操作系统分配。操作系统也分配失败时,才从备份池申请。因此当发现从备份池申请时需要高度重视。

2.4 内存增长过快分析

1、问题描述:外面项目反馈,分区表中对大字段列进行max(length(file_id))会导致CPU暴增。
2、重现的SQL:
–1、创建表
CREATE TABLE USER_MAIL_INDEX_0
(
ACCT_ID BIGINT DEFAULT ‘0’ NOT NULL,
file_id text,
MAIL_ID BIGINT DEFAULT ‘0’ NOT NULL,
THREAD_ID BIGINT DEFAULT ‘0’ NOT NULL,
CONSTRAINT PK_MAIL_INDEX_0 NOT CLUSTER PRIMARY KEY(ACCT_ID, MAIL_ID))
PARTITION BY HASH(ACCT_ID)
PARTITIONS 100 STORAGE(USING LONG ROW, ON MAIN, CLUSTERBTR) ;
–2、插入数据
begin
for i in 1…1000000 loop
insert into USER_MAIL_INDEX_0 values(i,‘dadfdsgfdgfdd’,i-1,9);
end loop;
end;
commit;
–3、执行查询
select max(length(file_id)) from user_mail_index_0;
3、参数设置:
MEMORY_POOL = 100
MEMORY_TARGET = 500
BUFFER = 1000
RECYCLE = 64
4、现象:内存增长的特别快,在一些内存配置比较小的机器上(8G),会被操作系统OOM
数据库刚启动后的内存情况:
在这里插入图片描述
执行查询sql:30秒的内存情况,8G的内存都被吃完:
在这里插入图片描述
5、分析
1、通过查询监控内存的使用,确认是哪块内存在增长
select
(select sum(n_pages * page_size)/1024/1024 from v$bufferpool)||‘MB’ as BUFFER_SIZE,
( select sum(total_size)/1024/1024 from v$mem_pool)||‘MB’ as mem_pool,
(select sum(n_pages * page_size)/1024/1024 from v$bufferpool)+(select sum(total_size)/1024/1024 from v m e m p o o l ) ∣ ∣ ′ M B ′ a s T O T A L S I Z E F r o m d u a l ; 在启动前查询下上述语句,记录初始值。然后在内存增长的时候,在查询该语句。发现是 v mem_pool)||'MB' as TOTAL_SIZE From dual; 在启动前查询下上述语句,记录初始值。然后在内存增长的时候,在查询该语句。发现是v mempool)MBasTOTALSIZEFromdual;在启动前查询下上述语句,记录初始值。然后在内存增长的时候,在查询该语句。发现是vmem_pool在增长。那么我们怎么知道是哪个内存池在内存一直在堆积不释放呢?
2、打开MEMORY_LEAK_CHECK
alter system set ‘MEMORY_LEAK_CHECK’=1 ;
select * from v$dm_ini where para_name like '%MEMORY_LEAK_CHECK%;
3、查询 V$MEM_REGINFO视图,关注REFNUM字段,如果这个字段很大,就说明存在内存堆积的情况,需要将对应的结果反馈给研发,以帮助研发进一步分析。
select * from V$MEM_REGINFO ORDER BY REFNUM DESC;
–结果如下图所示:
在这里插入图片描述
从该图上可以看到是VM的内存池在增长,根据前面第一章的基本原理,我们已知VM上的内存是从session池来的,SESSION池如果不够,扩展的内存是从共享池(SHARE_POOL),而如果share_pool的内存不够,是从操作系统分配的。既然找到是VM池的增长导致的,那么我们就需要把上图的VIRTUAL MACHINE池的LINENO、refnum、fname提供给研发。PS:另外设置MEMORY_TARGET,可以在内存比较大的时候,从os里面分配出来的内存可以在sql运行完以后在收缩到MEMORY_TARGET设置的大小。

3 抓取使用频繁的SQL语句

3.1 将生产环境sql日志文件拷贝到本地虚拟机/测试环境使用分析工具分析(本地虚拟机需安装达梦数据库)

SQL日志文件路径寻查看方法:在dm.ini同级目录下有sqllog.ini配置文件,打开sqllog.ini配置文件即可查看sql日志存放路径,例如:
在这里插入图片描述
切换到/data/XIJIUDB/sqlserver_uncase/sqllog目录即可查看SQL日志文件
在这里插入图片描述
jar包工具(不管是ZYJ还是通用机,也不管是dm7还是dm8环境都可以用Dmlog_DM7_5.1.jar包,比起其他版本的jar包,这个包相对最好用):
在这里插入图片描述
1)使用该工具前提:
1.需要有jdk环境,能够运行该jar包
2.库的页大小为32(小于32会出问题报错)
3.该工具只能分析sp_set_para_value(1,‘svr_log’,1)打开的dmsql_DMSERVER日志
4.选择的日志路径下只能要分析的日志不能有其他文件,可以有多个日志
5.分析完成记得关闭sp_set_para_value(1,‘svr_log’,0)(可选项)
6.分析结果会在运行jar包路径下
2)使用方法:
1.上传DMLOG8.2_20210929.zip包并用zip工具解压
2.配置dmlog.properties文件
3.java -jar Dmlog_DM_8.2.jar
在这里插入图片描述
3.执行完成会在执行的当前目录生成“RESULT_当前日期”的文件
在这里插入图片描述
4.把执行生成的目录拷贝到本地windows服务器分析
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
从以上的两个execl表格可以明显看出执行次数,还可以对执行次数进行排序即可查询出执行次数频率最多的语句。

4 拷贝生产环境的参数到测试环境

要想测试环境参数优化与生产环境保持一致的前提条件是生产环境资源与测试环境资源一样。拷贝生产环境的dm.ini文件到测试环境相应的dm.ini的路径即可,并重启数据库生效。建议在测试环境初始化实例时路径与生产环境保持一致,这样可以把生产环境的dm.ini参数文件直接拷贝到测试环境的dm.ini对应的路径(拷贝之前先备份测试环境dm.ini文件)。如果测试环境初始化路径与生产环境路径不一致,需要修改测试环境相应的路径,否则数据库启动会失败。需要修改的路径:CTL_PATH、CTL_BAK_PATH、SYSTEM_PATH、CONFIG_PATH、TEMP_PATH、BAK_PATH、DFS_PATH(参考测试环境备份dm.ini文件对应的路径)

达梦社区地址:https://eco.dameng.com

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值