一、现象

  可能大家都遇到过这么一种现象。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。

mysql cursor存储结果释放 mysql内存不释放_MySQL

top 观察到当前 MySQL 占用 8.8G左右

 

3.2 调整线程。thread 增加到50个,然后观察内存。

mysql cursor存储结果释放 mysql内存不释放_mysql_02

 

 

mysql cursor存储结果释放 mysql内存不释放_重启_03

0.010 * 1024 = 10.24G 左右。内存从 8.8G 增加到 10.24G,等到查询结束,MySQL内存并没有释放回 8.8G

 

3.3 把 thread 调整到 10个,观察内存

 

mysql cursor存储结果释放 mysql内存不释放_mysql cursor存储结果释放_04

mysql cursor存储结果释放 mysql内存不释放_mysql_05

内存并没有增加

 

3.4 把 thread 调整到 100个,观察内存

 

mysql cursor存储结果释放 mysql内存不释放_重启_06

mysql cursor存储结果释放 mysql内存不释放_MySQL_07

0.012 * 1024 = 12.28(G) 左右

 

四、结论

从目前的实验结果来看,mysql 会把内存占用不释放,像是有一个历史高水位在那里,比如说,我历史跑过最大连接数 50 个,mysqld占用了 10.24G内存,接下来跑 10个连接数的时候,则内存不会增加。最后跑了100个连接数,内存又增加到12.28G。然后就一直保持在12.28G。