MySQL 内存问题排查思路

简介:MySQL 内存持续增长的问题虽然不会经常发生,但是如果偶尔发生了,也会让我们不知所措。

一 、如果碰到MySQL 内存持续升高问题的主要情况有4种:

  1. MySQL 没有合理设置 innodb_buffer_pool_size

  2. 服务器上还有一些其他进程可以分配内内存。

  3. MySQL 内存泄漏。

  4. 内存缓慢增长属于正常现象,但存在因为一些慢sql频繁执行并因MySQL的内存分配使用了系统glibc,而glibc本身的内存分配算法存在缺陷,导致内存释放不完全。

二、MySQL 内存持续排查思路:

  1 基于服务器的检查:

    1.1  通过检查 MySQL 错误日志和 Linux 日志文件(即 /var/log/messages 或 /var/log/syslog)来识别崩溃。您可能会看到一个条目说 OOM Killer 杀死了 MySQL。每当 MySQL 被 OOM 杀死时,“dmesg”也会显示有关它周围情况的详细信息。

    1.2 检查可用内存:

free -g

cat /proc/meminfo

  1.3  使用命令 top 或 htop 检查哪些应用程序正在使用 RAM(参见常驻内存与虚拟内存)

  1.4 检查MySQL配置:检查/etc/my.cnf或一般的/etc/my*(包括/etc/mysql/*等文件)。MySQL 可能使用不同的 my.cnf(  run ps ax| grep mysql ) 运行。

  1.5 运行  vmstat 5 5 以 查看系统是否通过虚拟内存进行读/写以及是否正在交换。

  1.6 对于非生产环境,我们可以使用其他工具(如Valgrind、gdb等)来检查MySQL的使用情况。

2  现在我们可以通过MySQL运行机制以便查找潜在的内存泄漏因素。MySQL 在很多地方分配内存,尤其是:

   2.1表缓存

   2.2启用 Performance_schema功能

show engine performance_schema status 并查看最后一行。

   2.3 InnoDB(运行  show engine innodb status 并检查缓冲池部分,为 buffer_pool 和相关缓存分配的内存)

   2.4 在内存中的临时表(找到运行内存中的所有表:select * from information_schema.tables where engine='MEMORY')

  2.5 Prepared 语句,当它没有被解除分配时(通过运行 show global status like 检查通过解除分配命令准备的命令的数量  Com_prepare_sql;

show global status like 'Com_dealloc_sql'

3 从 5.7 开始,我们可以基于 performance_schema 查询内存使用情况。使用方法:

3.1 开启收集内存的统计信息

UPDATE setup_instruments SET ENABLED = 'YES' WHERE NAME LIKE 'memory/%';

3.2 运行sql :

select event_name, current_alloc, high_alloc from sys.memory_global_by_current_bytes where current_count > 0;

3.3 通常情况下,第二部的结果集会展示具体的代码模块使用了比较多的内存。它通常是不言自明的,我们可以搜索mysql的bugs 或者可以去检查 MySQL 源代码。

举个例子, https://bugs.mysql.com/bug.php?id=86821 ,这篇文章展示了 mysql为触发器分配了过多的内存。

mysql> select event_name, current_alloc, high_alloc from memory_global_by_current_bytes where current_count > 0;
+--------------------------------------------------------------------------------+---------------+-------------+
| event_name                                                                     | current_alloc | high_alloc  |
+--------------------------------------------------------------------------------+---------------+-------------+
| memory/innodb/buf_buf_pool                                                     | 7.29 GiB      | 7.29 GiB    |
| memory/sql/sp_head::main_mem_root                                              | 3.21 GiB      | 3.62 GiB    |
...

虽然 buf_buf_pool 占用了7G ,但是系统依然为存储过程对象分配3G内存,显然分配的内存太大了。根据文档描述 sp_head 代表这个存储程序的一个实例,它可能是任何类型(存储过程、函数、触发器、事件)。在上述情况下,这个mysql有潜在的内存泄漏。

注意:

其实官方并不承认 存储过程对象导致内存使用量持续增加是个bug。官方给的建议是调整参数  table_open_cache_instances

另外我们可以通过如下语句 查看具体是哪个模块在消耗大量内存。

mysql> select  substring_index(
    ->     substring_index(event_name, '/', 2),
    ->     '/',
    ->     -1
    ->   )  as event_type,
    ->   round(sum(CURRENT_NUMBER_OF_BYTES_USED)/1024/1024, 2) as MB_CURRENTLY_USED
    -> from performance_schema.memory_summary_global_by_event_name
    -> group by event_type
    -> having MB_CURRENTLY_USED>0;
+--------------------+-------------------+
| event_type         | MB_CURRENTLY_USED |
+--------------------+-------------------+
| innodb             |              0.61 |
| memory             |              0.21 |
| performance_schema |            106.26 |
| sql                |              0.79 |
+--------------------+-------------------+
4 rows in set (0.00 sec)

三 、jemalloc 在 mysql 当中的使用:

yum install jemalloc
在mysqld_safe中,最前面添加如下信息:
export LD_PRELOAD="/lib64/libjemalloc.so.1"
重启启动mysql实例

使用pt工具确认是否是用你了jemalloc
pt-mysql-summary  -S /tmp/mysql-3320.sock --user root --password 123456|grep -A 5 "Memory management" 
显示如下信息,则代表使用了jemalloc

<think>嗯,用户问的是如何排查MySQL服务器的内存泄漏问题。这个问题看起来有点复杂,但让我慢慢理清楚思路。 首先,我需要回忆一下内存泄漏可能的表现。MySQL内存泄漏通常表现为内存使用量持续增长,即使在没有负载的情况下,最终可能导致服务器崩溃或者被OOM Killer终止。那用户可能已经观察到内存使用不断上升,需要找出原因。 接下来,我应该考虑从哪里开始排查。通常,第一步是确认是否真的是内存泄漏,而不是正常的内存使用。比如,MySQL的缓冲池(InnoDB Buffer Pool)等组件会预先分配大量内存,这是正常现象。所以,需要区分正常使用和泄漏。 然后,检查操作系统级别的内存监控。使用top、htop或者free -m命令查看内存使用情况,特别是RES(常驻内存)和VIRT(虚拟内存)的值。如果RES持续增长,可能存在泄漏。另外,可以通过/proc/meminfo来查看更详细的内存信息,比如Slab内存是否异常增加,这可能和内核内存分配有关。 接下来,查看MySQL的内部内存使用情况。MySQL提供了一些性能模式(Performance Schema)和系统变量来监控内存分配。比如,启用Performance Schema中的内存统计功能,可以查看各个组件的内存使用情况。需要检查是否启用了performance_schema=ON,并且设置instruments和consumers来收集内存数据。 然后,用户可能需要执行一些SQL查询来获取内存分配的具体情况。例如,使用SELECT * FROM sys.memory_global_by_current_bytes WHERE current_alloc > 100*1024*1024; 这样的查询,找出占用内存较多的组件。这有助于定位是哪个部分的内存没有释放。 另外,检查MySQL的错误日志也是重要的步骤。可能有相关的警告或错误信息,提示内存分配失败或者泄漏的线索。需要关注是否有重复的错误消息,比如无法分配内存等。 接下来,考虑第三方工具,如Valgrind,但这对生产环境可能不适用,因为会影响性能。不过,如果是测试环境,可以用Valgrind启动MySQL,检测内存泄漏。 同时,分析代码或版本问题。如果MySQL的某个特定版本存在已知的内存泄漏问题,升级到修复版本可能解决问题。需要查看官方文档或社区报告,确认是否存在已知问题。 然后,检查插件和自定义代码。用户可能安装了第三方插件、存储引擎或UDF,这些可能存在内存管理问题。禁用这些组件,观察内存是否稳定,可以帮助排查问题。 还要检查连接和会话的内存使用。比如,每个连接可能会分配一些内存,如果连接数过多或存在连接泄漏,会导致内存增长。需要监控线程和连接数,使用SHOW PROCESSLIST查看是否有异常连接。 此外,临时表和排序操作也可能占用大量内存,尤其是在处理复杂查询时。检查是否有大量的磁盘临时表创建,或者需要调整sort_buffer_size、join_buffer_size等参数。 最后,如果上述步骤无法确定问题,可能需要联系官方支持或社区寻求帮助,提供相关的日志和诊断信息。 在回答时,需要将这些步骤结构化,逐步引导用户排查,从确认问题存在,到系统监控,再到MySQL内部诊断,最后到级工具和社区支持。同时,提醒用户谨慎调整参数,并在生产环境中做好备份和测试。 还要注意用户的实际情况,可能对某些步骤不熟悉,需要给出具体的命令和查询示例,比如如何启用Performance Schema,如何执行特定的SQL语句。同时,强调区分正常内存使用和泄漏的重要性,避免用户误判。 总之,回答需要条理清晰,逐步推进,结合多种排查手段,帮助用户系统地定位和解决MySQL内存泄漏问题。</think>排查MySQL服务器内存泄漏需要结合系统监控、MySQL内部诊断及工具分析,以下是分步骤排查指南: --- ### **一、确认内存泄漏现象** 1. **监控系统内存使用** ```bash # 实时查看内存变化(观察RES列) top -c -p $(pgrep mysqld) # 或使用周期性采集 free -m -s 5 ``` - **关键指标**:若`RES`(物理内存占用)持续增长且不释放,可能存在泄漏。 2. **检查MySQL内存配置** ```sql SHOW VARIABLES LIKE '%buffer%'; SHOW VARIABLES LIKE '%cache%'; ``` - 确认`innodb_buffer_pool_size`等配置是否合理,避免误将正常缓存分配视为泄漏。 --- ### **二、定位MySQL内部内存分配** 1. **启用Performance Schema** ```ini # 在my.cnf中启用并重启MySQL [mysqld] performance_schema=ON ``` ```sql -- 开启内存监控 UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME LIKE 'memory/%'; UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE 'memory%'; ``` 2. **查询内存分配详情** ```sql -- 查看内存占用排名(单位:MB) SELECT SUBSTRING_INDEX(event_name, '/', 2) AS category, ROUND(SUM(current_alloc)/1024/1024, 2) AS current_alloc_mb FROM sys.memory_global_by_current_bytes GROUP BY category ORDER BY current_alloc_mb DESC; ``` - **重点关注**:异常增长的组件(如`Temporary`、`Query Cache`)。 --- ### **三、检查常见泄漏场景** 1. **连接泄漏** ```sql SHOW STATUS LIKE 'Threads_connected'; SHOW PROCESSLIST; ``` - 若连接数持续增长,检查应用是否未正确关闭连接。 2. **临时表与排序** ```sql SHOW STATUS LIKE 'Created_tmp%'; SHOW STATUS LIKE 'Sort%'; ``` - 大量磁盘临时表(`Created_tmp_disk_tables`)可能因`tmp_table_size`过小导致内存溢出。 3. **插件与存储引擎** ```sql SHOW PLUGINS; SELECT * FROM information_schema.ENGINES; ``` - 禁用第三方插件(如审计插件)观察内存是否稳定。 --- ### **四、级诊断工具** 1. **Valgrind检测(仅测试环境)** ```bash valgrind --leak-check=full mysqld --console ``` - 输出内存泄漏堆栈信息(需调试符号支持)。 2. **分析核心转储** ```bash # 生成核心转储文件 gcore $(pgrep mysqld) # 使用gdb分析 gdb /usr/sbin/mysqld core.dump ``` - 结合`malloc_zone`或`jemalloc`工具检查堆内存。 --- ### **五、修复与优化** 1. **升级MySQL版本** - 确认是否遇到已知内存泄漏Bug(参考[MySQL Release Notes](https://dev.mysql.com/doc/relnotes/mysql/))。 2. **调整配置参数** ```ini # 限制全局内存(示例) [mysqld] table_open_cache=2000 thread_cache_size=100 max_connections=500 ``` --- ### **六、持续监控** ```bash # 使用Prometheus+Grafana监控 mysql> SELECT * FROM sys.memory_global_total; ``` --- ### **总结** 1. 优先区分正常缓存与泄漏(如`InnoDB Buffer Pool`初始化后应稳定)。 2. 从系统层到MySQL内部逐层缩小范围。 3. 生产环境慎用Valgrind,建议通过Performance Schema和错误日志分析。 若问题仍未解决,可提供以下信息寻求进一步帮助: - MySQL版本、错误日志片段、`SHOW ENGINE INNODB STATUS`输出、内存监控图表。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值