[MySQL 生产环境内存报警]Select之后Free -m的used总是会一直不停的增加直到100%

生产环境内存使用率常超85%阀值,以往靠重启解决。此次在测试环境分析,查看内存使用情况后,运行无索引消耗资源的SQL,发现内存使用量增长。尝试调小innodb_buffer_pool_size值,执行SQL后内存仍增加,后续还给出了memory使用率判断方法。

现象是:生产环境的内存使用率经常超过85%阀值,nagios不停报警,以往的处理办法就是restart,一切memery使用率回到65%。还得研究找到问题所在,所以在测试环境进行问题测试分析。

 

 

1 先查看问题当前的内存使用情况: 
[novamysqladminlt@test-by7db2 ~]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      24792       7316          0        824      11019
-/+ buffers/cache:      12948      19159
Swap:        32767         42      32725
[novamysqladminlt@test-by7db2 ~]$ 

 

2  去运行一个无索引的消耗资源的sql如下
mysql> select  * from tb01 where GROUP_ID>100 limit 10000000, 1000;

 

 

3 去运行一个无索引的消耗资源的sql如下
mysql> select  * from tb01 where GROUP_ID>100 limit 10000000, 1000;




4 然后去检查内存使用情况: 
 

[mysql@test-by7db2 5.5.25a]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      25358       6750          0        824      10976
-/+ buffers/cache:      13557      18550
Swap:        32767         44      32723
[mysql@test-by7db2 5.5.25a]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      25399       6709          0        824      10976
-/+ buffers/cache:      13598      18510
Swap:        32767         44      32723
[mysql@test-by7db2 5.5.25a]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      26807       5301          0        824      10943
-/+ buffers/cache:      15039      17069
Swap:        32767         44      32723
[mysql@test-by7db2 5.5.25a]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      26841       5267          0        824      10939
-/+ buffers/cache:      15077      17031
Swap:        32767         44      32723
[mysql@test-by7db2 5.5.25a]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      26862       5246          0        824      10931
-/+ buffers/cache:      15106      17002
Swap:        32767         44      32723
[mysql@test-by7db2 5.5.25a]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      26894       5213          0        824      10931
-/+ buffers/cache:      15139      16969
Swap:        32767         44      32723
[mysql@test-by7db2 5.5.25a]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      26895       5213          0        824      10931
-/+ buffers/cache:      15139      16969
Swap:        32767         44      32723
[mysql@test-by7db2 5.5.25a]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      26895       5213          0        824      10931
-/+ buffers/cache:      15139      16969
Swap:        32767         44      32723
[mysql@test-by7db2 5.5.25a]$ 
[mysql@test-by7db2 5.5.25a]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      26895       5212          0        824      10932
-/+ buffers/cache:      15139      16969
Swap:        32767         44      32723
[mysql@test-by7db2 5.5.25a]$ 
[mysql@test-by7db2 5.5.25a]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      26896       5211          0        824      10932
-/+ buffers/cache:      15140      16968
Swap:        32767         44      32723
[mysql@test-by7db2 5.5.25a]$ 
[mysql@test-by7db2 5.5.25a]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      26896       5211          0        824      10932
-/+ buffers/cache:      15139      16968
Swap:        32767         44      32723
[mysql@test-by7db2 5.5.25a]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      26897       5211          0        824      10932
-/+ buffers/cache:      15139      16968
Swap:        32767         44      32723
[mysql@test-by7db2 5.5.25a]$ 
[mysql@test-by7db2 5.5.25a]$ 
[mysql@test-by7db2 5.5.25a]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      26897       5211          0        824      10933
-/+ buffers/cache:      15139      16968
Swap:        32767         44      32723
[mysql@test-by7db2 5.5.25a]$

 

 

 

 


OK,查询跑完了,耗时如下:
......
1000 rows in set (1 min 4.15 sec)



4 检查现在运行完SQL的内存使用情况:

 

 

 

 

 

 

 

[mysql@test-by7db2 5.5.25a]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      26913       5195          0        824      10946
-/+ buffers/cache:      15143      16965
Swap:        32767         44      32723
[mysql@test-by7db2 5.5.25a]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      26912       5196          0        824      10946
-/+ buffers/cache:      15141      16967
Swap:        32767         44      32723
[mysql@test-by7db2 5.5.25a]$ 
[mysql@test-by7db2 5.5.25a]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      26911       5196          0        824      10946
-/+ buffers/cache:      15140      16967
Swap:        32767         44      32723
[mysql@test-by7db2 5.5.25a]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      26911       5196          0        824      10946
-/+ buffers/cache:      15140      16968
Swap:        32767         44      32723

 

不停的在使用着的内存不停的在增长,used值直线上升。最后等查询结束了,会稳定在26911M上面,不再动弹,但是比原来的25358要高很多。


为了验证这个,再继续执行一个慢sql:
mysql> select  * from tb01 where GROUP_ID>100 limit 12000000, 1000;

 

 

 

 

used继续不停的增长,加大。

 

 

 

 

 

5,也许是innodb_buffer_pool_size值太大了,尝试把innodb_buffer_pool_size值调小看看: 

现在把innodb_buffer_pool_size值从25000M调整到12000M,试试看效果。 
innodb_buffer_pool_size = 12000M

[mysql@test-by7db2 5.5.25a]$ service mysql status
 SUCCESS! MySQL running (8913)
[mysql@test-by7db2 5.5.25a]$ service mysql restart
Shutting down MySQL........... SUCCESS! 
rm: cannot remove `/var/lock/subsys/mysql': Permission denied
Starting MySQL........... SUCCESS! 
[mysql@test-by7db2 5.5.25a]$ 
[mysql@test-by7db2 5.5.25a]$ 
[mysql@test-by7db2 5.5.25a]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      12752      19356          0        840       9681
-/+ buffers/cache:       2230      29878
Swap:        32767         26      32741
[mysql@test-by7db2 5.5.25a]$ 

去客户端执行SQL:
mysql> select  * from identity.tb01 where GROUP_ID>100 limit 12000000, 1000;

 

再来看used使用情况:

[novamysqladminlt@eanltemydb5-c000utl ~]$ ssh test-by7db2.abn-sjl.ea.com
Last login: Mon Nov 25 02:55:46 2013 from eanltemydb5-c000utl.abn-sjl.ea.com
[novamysqladminlt@test-by7db2 ~]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      14197      17911          0        840       9681
-/+ buffers/cache:       3675      28433
Swap:        32767         26      32741
[novamysqladminlt@test-by7db2 ~]$ 

 

used值在增加,再来执行新的SQL语句:

mysql> select  * from identity.tb01 where GROUP_ID>100 limit 13000000, 1000;


[novamysqladminlt@test-by7db2 ~]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      15682      16426          0        840       9681
-/+ buffers/cache:       5160      26948
Swap:        32767         26      32741

看出每执行一条不同的SQL语句,used值都在增加。

mysql> select  * from identity.tb01 where GROUP_ID>100 limit 13000000, 1000;


[novamysqladminlt@test-by7db2 ~]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      15682      16426          0        840       9681
-/+ buffers/cache:       5160      26948
Swap:        32767         26      32741

看出执行相同的select语句,内存使用率used没有增加。  

 

mysql> select  * from identity.tb01 where GROUP_ID>100 limit 14000000, 1000;
[novamysqladminlt@test-by7db2 ~]$ free -m
             total       used       free     shared    buffers     cached
Mem:         32108      15931      16177          0        840       9681
-/+ buffers/cache:       5408      26699
Swap:        32767         26      32741


使用新的sql语句,used使用的内存从15682增加到15931。

 

从这里可以看出,不管innodb_buffer_pool_size值设置多大多小,used值总是会增加的,看来分析的思路有误,得换个方向。

 

 

6 memory使用率判断:

 

mysql> SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_data'; 
+-------------------------------+--------+
| Variable_name                 | Value  |
+-------------------------------+--------+
| Innodb_buffer_pool_pages_data | 1322836|
+-------------------------------+--------+
1 row in set (0.00 sec)


mysql> SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_total';
+--------------------------------+---------+
| Variable_name                  | Value   |
+--------------------------------+---------+
| Innodb_buffer_pool_pages_total | 1344000 |
+--------------------------------+---------+
1 row in set (0.00 sec)


mysql> SHOW GLOBAL STATUS LIKE 'Innodb_page_size';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| Innodb_page_size | 16384 |
+------------------+-------+
1 row in set (0.00 sec)	


Innodb_buffer_pool_pages_data' X 'Innodb_page_size' X 1.05 / (1024*1024*1024)	


Innodb_buffer_pool_pages_data' X 'Innodb_page_size' X 1.05 / (1024*1024*1024)


'Innodb_buffer_pool_pages_data' X 100 / 'Innodb_buffer_pool_pages_total'
当结果 > 95% 则增加 innodb_buffer_pool_size, 建议使用 ram total 75%
当结果 < 95% 则减少 innodb_buffer_pool_size, 
建议 'Innodb_buffer_pool_pages_data' X 'Innodb_page_size' X 1.05 / (1024*1024*1024)

 

这计算 'Innodb_buffer_pool_pages_data' X 100 / 'Innodb_buffer_pool_pages_total' 值是  

 

mysql> select 1322836*100/1344000;
+---------------------+
| 1322836*100/1344000 |
+---------------------+
|             98.4253 |
+---------------------+
1 row in set (0.00 sec)

 

 

 

 

 

 

 

 

 

[...持续分析中...]

<think>我们正在分析MySQL 8.0在innodb_buffer_pool_size设置为机器内存65%且业务负载很低的情况下,仍持续占用100%内存不释放的原因。 根据引用内容,我们知道innodb_buffer_pool_size是InnoDB缓冲池的大小,用于缓存数据和索引。但是,MySQL内存占用不仅仅包括缓冲池。 引用[2]提供了一个计算MySQL内存占用的公式: SELECT ((@@key_buffer_size+@@innodb_buffer_pool_size+@@innodb_log_buffer_size)/1024/1024)+((@@read_rnd_buffer_size+@@read_buffer_size+@@myisam_sort_buffer_size+@@sort_buffer_size+@@join_buffer_size)/1024/1024*@@max_connections); 这个公式将内存分为两部分: 1. 全局缓冲区:key_buffer_size, innodb_buffer_pool_size, innodb_log_buffer_size 2. 每个连接(会话)的缓冲区:read_rnd_buffer_size, read_buffer_size, myisam_sort_buffer_size, sort_buffer_size, join_buffer_size等,乘以最大连接数(max_connections) 因此,即使innodb_buffer_pool_size只占65%,但其他内存部分(尤其是连接相关的缓冲区)可能占用大量内存。 另外,引用[3]提到,在专用数据库服务器上,innodb_buffer_pool_size可以设置为机器物理内存的80%,但要注意不要设置得太大,以免导致操作系统分页。 现在,我们分析可能的原因: 1. **缓冲池外的内存占用**:除了innodb_buffer_pool_size,MySQL还有其他内存区域,包括: - 全局内存:如key_buffer(用于MyISAM,即使不使用MyISAM表也可能被分配),innodb_log_buffer,查询缓存(在MySQL8.0中已移除)等。 - 每个连接的内存:如果max_connections设置得很大,那么即使空闲连接也会占用每个连接配置的缓冲区。例如,如果max_connections=1000,每个连接的缓冲区总大小(read_buffer, sort_buffer等)为1MB,则最大可能占用1GB。 2. **操作系统报告的内存占用**:操作系统报告的内存占用可能包括: - MySQL分配的所有内存(包括缓冲池和连接缓冲区) - 操作系统的文件系统缓存(这部分通常被计算在已用内存中,但实际是可用内存,因为操作系统会将空闲内存用于缓存,并在应用程序需要时释放) 3. **InnoDB缓冲池的占用**:InnoDB缓冲池在启动时就会分配指定大小的内存,并且不会释放给操作系统(除非重启或者动态调整并触发释放)。即使业务负载很低,缓冲池仍然会保持分配状态。 4. **其他组件占用**:如引用中提到的,服务器上还运行了Redis、Kettle等其他服务,这些服务也会占用内存。 5. **内存泄漏**:虽然MySQL本身没有内存泄漏,但某些特定情况下(如某些插件或存储引擎)可能会导致内存泄漏。 6. **动态内存分配**:MySQL有一些动态内存分配,例如临时表、存储过程、触发器等的执行可能会临时申请内存,但正常情况下在操作完成后会释放。 7. **监控指标的理解**:监控显示的100%内存占用可能是: - 未扣除缓冲池中未被使用的部分(实际上缓冲池不会释放给操作系统,但内部可能有未被使用的页面) - 操作系统的缓存占用(如文件系统缓存)也被算在MySQL头上? 根据以上分析,我们逐步排查: 步骤1:检查MySQL的总内存配置 使用引用[2]的SQL计算MySQL理论最大内存占用: ```sql SELECT ((@@key_buffer_size + @@innodb_buffer_pool_size + @@innodb_log_buffer_size) + (@@read_rnd_buffer_size + @@read_buffer_size + @@myisam_sort_buffer_size + @@sort_buffer_size + @@join_buffer_size) * @@max_connections ) / (1024*1024) AS total_memory_mb; ``` 如果这个值接近或超过系统内存,那么在连接数时内存占用就会很。 步骤2:检查当前实际内存分配 使用MySQL的性能模式(performance_schema)来查看内存使用情况: ```sql SELECT * FROM performance_schema.memory_summary_global_by_event_name ORDER BY COUNT_ALLOC DESC, SUM_NUMBER_OF_BYTES_ALLOC DESC LIMIT 10; ``` 或者查看sys schema中的内存视图(MySQL 5.7+): ```sql SELECT * FROM sys.memory_global_total; SELECT * FROM sys.memory_global_by_current_bytes WHERE current_alloc > 0; ``` 步骤3:检查操作系统级别的内存使用情况 使用命令(如top, htop, free)查看系统内存使用情况,特别是查看MySQL进程的实际内存占用(RES)以及缓存(cache)和缓冲(buffer)的占用。 步骤4:检查连接数 如果空闲连接很多,每个连接都会占用一些内存。可以检查: ```sql SHOW STATUS LIKE 'Threads_connected'; -- 当前连接数 SHOW VARIABLES LIKE 'max_connections'; -- 最大连接数 ``` 步骤5:检查InnoDB缓冲池的使用情况 虽然缓冲池被分配了很大,但实际使用情况如何? ```sql SHOW ENGINE INNODB STATUS\G ``` 在输出的"BUFFER POOL AND MEMORY"部分,可以看到缓冲池的使用情况,包括: Total memory allocated: 分配的总内存(应该等于innodb_buffer_pool_size) Database pages: 实际存储数据的页数量 Free buffers: 空闲页的数量 如果Free buffers很多,说明缓冲池并没有完全利用,但内存已经被分配。 步骤6:检查其他可能占用内存的因素 如临时表、内存表等: ```sql SHOW GLOBAL STATUS LIKE 'Created_tmp_%tables'; SHOW GLOBAL STATUS LIKE 'Handler_read_rnd%'; ``` 原因总结: 1. **缓冲池预先分配**:InnoDB缓冲池在启动时一次性分配整个缓冲池的内存,并且不会释放给操作系统。即使业务量下降,缓冲池也不会缩小(除非重启或动态调整缓冲池大小并触发收缩,但动态调整通常不会释放物理内存给操作系统,而是保留在MySQL进程内)。 2. **连接缓冲区**:如果max_connections设置过,即使空闲连接也会占用内存(每个连接有独立的缓冲区)。如果应用程序有连接泄漏,导致连接数持续很,那么内存占用也会很。 3. **其他全局内存**:如key_buffer_size(默认8M)、innodb_log_buffer_size(默认16M)等,虽然不大,但也要考虑。 4. **操作系统的文件缓存**:操作系统可能会将MySQL的数据文件缓存到内存中,这部分内存会被算到系统已用内存中,但实际上它属于可回收内存。如果监控工具没有区分应用程序内存和系统缓存,可能会显示内存占用很。 5. **其他进程**:如Redis、Kettle等,它们也会占用内存。 解决方案建议: 1. 检查MySQL的实际内存配置总和(使用步骤1的SQL),如果过,调整相关参数: - 降低max_connections(但确保不影响业务) - 降低每个连接的缓冲区大小(如read_buffer_size, sort_buffer_size等,但设置过低可能影响查询性能) 2. 监控实际连接数,确保没有连接泄漏(使用SHOW PROCESSLIST或监控Threads_connected)。 3. 使用性能模式或sys schema定位具体的内存分配模块。 4. 考虑增加机器内存(如果其他服务也需要内存)。 5. 优化应用程序,减少连接数,使用连接池并控制连接池大小。 6. 如果确认是缓冲池过大,可以尝试动态调整innodb_buffer_pool_size(但注意调整过程可能会阻塞,且只能调整到比当前小,但通常不会释放给操作系统,MySQL会保留以备后续使用)。 注意:在Linux系统中,不要被free命令中的used内存吓到,因为Linux会自动利用空闲内存作为文件系统缓存(cache和buffer),这部分内存在应用程序需要时会被释放。因此,监控应该关注应用程序的RES(常驻内存)和交换(swap)使用情况。 如果MySQL的RES占用接近innodb_buffer_pool_size+其他全局内存+(每个连接内存*当前连接数),则属于正常。如果远超过,则考虑内存泄漏。 最后,建议使用专业的监控工具(如Prometheus+mysqld_exporter)来监控MySQL内存使用情况。 相关问题: 1. 如何动态调整innodb_buffer_pool_size? 2. 如何监控MySQL内存使用情况? 3. 如何优化MySQL的连接数? 4. 如何检查操作系统中各进程的内存使用情况? 5. 为什么Linux系统显示的内存占用很但实际可用内存还充裕? </think>### MySQL内存占用100%原因分析 在`innodb_buffer_pool_size=65%内存`且业务负载低的情况下,MySQL仍持续占用100%内存不释放,主要涉及以下机制: --- #### 一、核心原因:InnoDB缓冲池的预分配机制 1. **固定内存池特性** - InnoDB缓冲池启动时**一次性分配**指定大小的连续内存空间(16GB)[^3] - 设计上**不会主动释放**给操作系统,即使业务空闲[^1] - 公式验证: $$ \text{MySQL内存} = \text{全局缓冲} + (\text{会话缓冲} \times \text{最大连接数}) $$ 引用[2]的计算方法可能显示未超标,但未计入OS文件缓存 2. **操作系统级文件缓存** - Linux自动将剩余内存用于**磁盘缓存**(Page Cache) - MySQL数据文件(`ibd`)会被OS缓存,这部分显示为MySQL占用 ```bash # 查看真实内存分配(示例) sudo pmap -x <mysql_pid> | grep "total kB" ``` --- #### 二、其他关键因素 1. **会话级内存堆积** - 即使QPS低,**持久连接**占用缓冲区内存: ```sql SHOW STATUS LIKE 'Threads_connected'; -- 查看活跃连接数 ``` - 每个连接默认占用: - `join_buffer_size`(默认256KB) - `sort_buffer_size`(默认256KB) - `read_buffer_size`(默认128KB) 2. **内存碎片问题** - 长期运行后出现**内存碎片化** - 可通过优化器统计信息观察: ```sql SHOW ENGINE INNODB STATUS; -- 关注BUFFER POOL FRAGMENTATION ``` 3. **其他组件内存占用** | 组件 | 默认内存 | 是否可释放 | |------|----------|------------| | 键缓存(key_buffer) | 8MB | 否 | | 日志缓冲(innodb_log_buffer) | 16MB | 否 | | 临时表内存 | 动态分配 | 部分可释放 | --- #### 三、诊断步骤 1. **确认真实内存分配** ```sql /* 计算MySQL理论内存上限[^2] */ SELECT (@@innodb_buffer_pool_size + @@key_buffer_size + @@innodb_log_buffer_size + (@@read_buffer_size + @@read_rnd_buffer_size + @@sort_buffer_size + @@join_buffer_size) * @@max_connections ) / 1024 / 1024 AS "Total MB"; ``` 2. **分析操作系统内存** ```bash free -h # 关注buff/cache列 ps aux --sort=-%mem | head -10 ``` 3. **检查InnoDB状态** ```sql SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages%'; --Free页占比>30%说明缓冲池未充分利用 ``` --- #### 四、解决方案 1. **动态调整缓冲池(需MySQL 8.0+)** ```sql SET GLOBAL innodb_buffer_pool_size=12*1024*1024*1024; -- 12GB ``` - 每次调整最小单位为`chunk_size`(默认128MB) - 调整后观察`Innodb_buffer_pool_resize_status` 2. **优化会话配置** ```ini [mysqld] max_connections=150 # 合理限制连接数 tmp_table_size=32M # 减小临时表内存 ``` 3. **操作系统级优化** ```bash # 清理Page Cache(测试环境) sync; echo 3 > /proc/sys/vm/drop_caches ``` > **关键建议**:在内存监控中区分`RSS`(常驻内存)和`Page Cache`,推荐使用`smem -t -k -P mysqld` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值