一、现象
可能大家都遇到过这么一种现象。MySQL占用着内存不释放,然后还时不时的一点点增加。
二、问题排查
结合官方文档 https://dev.mysql.com/doc/refman/5.7/en/memory-use.html 和 percona文档 https://www.percona.com/blog/2018/06/28/what-to-do-when-mysql-runs-out-of-memory-troubleshooting-guide/
得出以下MySQL占用内存的计算方法
--查看每个线程占用多少内存,然后乘以正在运行的线程(也就是排查sleep的)。
SELECT ( ( @@read_buffer_size
+ @@read_rnd_buffer_size
+ @@sort_buffer_size
+ @@join_buffer_size
+ @@binlog_cache_size
+ @@thread_stack
+ @@max_allowed_packet
+ @@net_buffer_length )
) / (1024*1024) AS MEMORY_MB;
--查看MySQL全局占用多少内存
select (@@innodb_buffer_pool_size
+@@innodb_log_buffer_size
+@@key_buffer_size) / 1024 /1024 AS MEMORY_MB;
--查看performance_schema占用多少内存
SELECT SUBSTRING_INDEX(event_name,'/',2) AS
code_area, sys.format_bytes(SUM(current_alloc))
AS current_alloc
FROM sys.x$memory_global_by_current_bytes
GROUP BY SUBSTRING_INDEX(event_name,'/',2)
ORDER BY SUM(current_alloc) DESC;
--查看 memory 存储引擎占用多少内存
select sum(max_data_length)/1024/1024 as MEMORY_MB from tables where engine='memory';
再把上面的结果相加,就是MySQL当前占用的内存大小。但问题是算出来的结果比实际的小了很多。但如果拿历史最大连接数去算的话,结果是差不多的。所以,就在怀疑,MySQL是否有一个内存的“高水位“。因此带着疑问做了以下实验
三、实验
操作系统:centos 7.4
MySQL:percona 5.7.23
内存:32G
innodb_buffer_pool_size:8G
3.1 首先重启MySQL,释放掉内存,重启后,innodb_buffer_pool 中有 free_page,这时候,使用 sysbench 生成 8G 数据。全表扫描 sbtest1,把innodb_buffer_pool 占满,观察当前MySQL占用的内存
--全表扫描sbtest1
select count(*) from (select * from sbtest1) a;
--查看innodb_buffer_pool情况
select sum(POOL_SIZE),sum(free_buffers),sum(DATABASE_PAGES) from INNODB_BUFFER_POOL_STATS;
+----------------+-------------------+---------------------+
| sum(POOL_SIZE) | sum(free_buffers) | sum(DATABASE_PAGES) |
+----------------+-------------------+---------------------+
| 524224 | 6537 | 517687 |
+----------------+-------------------+---------------------+
因为我设置 innodb_buffer_pool_instance = 8,innodb_lru_scan_depth = 1024。所以 free_page 最大会剩余 8192 个 buffer。
top 观察到当前 MySQL 占用 8.8G左右
3.2 调整线程。thread 增加到50个,然后观察内存。
0.010 * 1024 = 10.24G 左右。内存从 8.8G 增加到 10.24G,等到查询结束,MySQL内存并没有释放回 8.8G
3.3 把 thread 调整到 10个,观察内存
内存并没有增加
3.4 把 thread 调整到 100个,观察内存
0.012 * 1024 = 12.28(G) 左右
四、结论
从目前的实验结果来看,mysql 会把内存占用不释放,像是有一个历史高水位在那里,比如说,我历史跑过最大连接数 50 个,mysqld占用了 10.24G内存,接下来跑 10个连接数的时候,则内存不会增加。最后跑了100个连接数,内存又增加到12.28G。然后就一直保持在12.28G。