在虚拟化 Ubuntu 12.04 上安装 MySQL 5.6.10 会出现大量内存占用。mysqld 进程在正常运行时间的几个小时内占用整个可用内存并强制主机交换:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
16229 mysql 20 0 26.8g 21g 8736 S 42 93.4 37:23.22 mysqld
它曾经增长到 50 GB,因此已经大大超过了数据集本身:
Current InnoDB index space = 5.25 G
Current InnoDB data space = 23.07 G
通常,我可以通过发布来释放 ~ 3 GB FLUSH TABLES
,尽管仅kill -9
mysql 进程要快得多,但要重新启动它并为 InnoDB 运行恢复。使用的表几乎完全是 InnoDB,innodb_buffer_pool_size 已设置为 5 GB(在将其设置为 16 GB 后很快耗尽了可用物理内存并换出了超过 18 GB 的内存)。
在系统进行交换时,我可以观察到“换出”计数器的数量相当多(vmstat 在突发期间显示约 1k 页/秒)并且几乎没有任何东西换回(每分钟几十页)。我首先怀疑内存泄漏,但到目前为止还没有发现任何支持这个假设的东西。
SHOW INNODB STATUS 表示缓冲池仅被部分填满:
----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 5365825536; in additional pool allocated 0
Dictionary memory allocated 2558496
Buffer pool size 320000
Free buffers 173229
Database pages 142239
Old database pages 52663
Modified db pages 344
Pending reads 1
Pending writes: LRU 0, flush list 1 single page 0
Pages made young 34, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 141851, created 387, written 41126
81.16 reads/s, 0.00 creates/s, 0.39 writes/s
Buffer pool hit rate 998 / 1000, young-making rate 0 / 1000 not 0 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 142239, unzip_LRU len: 0
I/O sum[0]:cur[464], unzip sum[0]:cur[0]
服务器总共有 80-90 个连接,其中大部分被 SHOW PROCESSLIST 报告为处于“睡眠”状态。
内存敏感选项集是
max_allowed_packet = 16M
thread_stack = 192K
thread_cache_size = 8
max_connections = 1000
innodb_file_format = Barracuda
innodb_buffer_pool_size = 5000M
innodb_log_file_size = 256M
innodb_flush_method = O_DIRECT
query_cache_limit = 1M
query_cache_size = 256M
join_buffer_size = 256k
tmp_table_size = 2M
max_heap_table_size = 64M
Tuning-primer.sh 脚本计算内存使用的合理值:
MEMORY USAGE
Max Memory Ever Allocated : 5.27 G
Configured Max Per-thread Buffers : 1.92 G
Configured Max Global Buffers : 5.15 G
Configured Max Memory Limit : 7.07 G
Physical Memory : 22.98 G
Max memory limit seem to be within acceptable norms
Binlog 已启用,并且主机连接了一个复制从属设备(尽管当时的结果并没有太大的不同,但情况并非如此)。Innodb_file_per_table 在 5.6 中默认启用,数据库总共托管约 1,300 个表。
我必须用什么方法来确定明显无限增长的可能原因?
在阅读了“MySQL 如何使用内存”之后,我怀疑临时表可能是罪魁祸首。如果由于某种原因没有正确释放它们,它们可能会很快积累。查询数据库的应用程序会发出大量嵌套的、复杂的查询,因此根据引用的文档,临时表将被大量使用。我尝试检查在mysqld达到〜20 GB时终止/重置现有(空闲)连接是否会显着减少内存使用量 - 它不会,因此这与连接状态无关,或者内存以某种方式从那里泄漏不受关闭连接的影响。
我将如何验证内存中的临时表是否占用了大量内存?STATUS 变量和 INFORMATION_SCHEMA 似乎没有此信息。
MySQL 的内存使用似乎很难调试 - 可用的计数器似乎没有占到我看到的大部分使用情况。不过,我可能会遗漏一些东西。
我还有一个基于 MyISAM 的复制从属连接到 InnoDB 主控,具有类似的(只读)负载 - 它没有显示任何内存使用过多的迹象(mysqld RSS 持续 < 1GB),所以问题似乎是特定于InnoDB 配置。
此处提交的错误(http://bugs.mysql.com/bug.php?id=68287)看起来可能相关 - 通过运行检查 table_definition_cache 设置的内容:
使用当前 GA 版本 MySQL 5.6.19 进行的测试表明该问题已经消失。
通读中间版本的发行说明,我注意到5.6.14中的这个特殊说明,我怀疑它与我们的问题有关,因为它明确引用了在我们的案例中过度使用的子查询子句:
不幸的是,Oracle 没有发布错误,所以这是我们在这个问题上可以获得的所有技术细节。