MySQL数据库SYS CPU高的可能性分析

本文探讨了MySQL数据库中SYSCPU过高的两种主要原因:在内核态spin及contextswitch过高,并提供了相应的诊断方法和解决方案。

问题背景

我们在管理繁忙的 MySQL 数据库时,可能都有碰到 SYS CPU 高的经历:系统突然 SYS CPU 高起来,甚至比 USER CPU 高很多,这时系统 QPS、TPS 急剧下降。

SYS CPU高是什么造成的呢?主要有2种可能:
1. context switch 不高,但在内核态 spin,导致 SYS CPU 高
2. context switch 高,每秒超过 200K,有时超过1M,过多 context switch 导致 SYS CPU 高

下面我们对这两种情况逐一分析。

context switch 不高,但在内核态 spin

mysql 在内核态 spin,说明需要系统资源,当这个资源紧张不足时,就会在内核态 spin。

有些资源,用户进程(或线程)通过执行系统调或因中断进入内核,一般来说申请这些资源的执行时间很短,当出现资源争用时,如果采用 sleep 再唤醒机制,代价较大,因此多采用在内核态spin的策略。

例如申请内存或发生缺页中断,没有 free 内存可用时,进程或线程就可能在内核态先执行内存回收再执行内存分配,但系统内存是共享资源,分配回收时需要锁保护,当多个进程(或线程)同时回收分配内存时。就会在内核态 spin。

当 free 内存不足时,可能出现这种情况。典型症状:

  1. MySQL running 高,但系统 qps、tps 下降
  2. 系统 free 内存不足;或系统 free 内存充足时,但启用了 numa 内存分配策略,有的节点 free 内存很少
  3. 系统 context switch 不高
  4. MySQL InnoDB 的 mutex、RWlock 查不到等待信息
  5. sar -B 显示有 pgscand 产生

分析

当系统内存不足时,MySQL 突然有大量访问,紧急需要大量内存,kswapd 在短时间内回收不了足够多的 free 内存,或 kswapd 还没有触发执行,这时 MySQL 用户线程就会在内核态执行内存回收操作,从而出现以上症状。

sar -B 输出中,pgscank 是表示内核线程 kswapd 回收内存,k意思是 kernel;pgscand是表示用户进程或线程直接回收内存,d意思是direct。

解决办法:保证系统有充足 free 内存可用,NUMA 环境要求每个节点都有足够free内存可用。

由于 Linux 系统会尽量使用 free 内存,一个运行很久的 linux 系统,free内存通常很少,存在大量 filecache 内存,但 Linux 没有直接提供控制 filecache 占用多少的参数,那怎么能够保留足够可用的 free 内存,以应对突然内存需求呢?

对此,Linux 2.3.32+ 内核中增加一个新的参数vm.extra_free_kbytes,就是控制free内存的。

关于系统free内存,有2个重要参数:vm.min_free_kbytes 和 vm.extra_free_kbytes(2.6.32+)

vm.min_free_kbytes:系统保留给内核用的内存。

这个值决定 /proc/zoneinfo 中 zone 的min值。当系统 free 内存小于这个值时,kswapd 会回收内存,直到free内存达到/proc/zoneinfo中 high 值才停止回收;

当用户进程或线程分配内存或发生缺页中断时,free 内存少于 vm.min_free_kbytes,会在用户线程上下文中直接进行回收内存(pgscand)和分配内存。

vm.extra_free_kbytes:系统保留给应用的free内存。

这个值决定了/proc/zoneinfo中Normal zone的low值。当系统free内存小于vm.min_free_kbytes + vm.extra_free_kbytes 时,kswapd会开始回收内存,直到free内存达到 /proc/zoneinfo 中high值才停止回收。

这个额外的vm.extra_free_kbytes就是给应用突发内存需求时使用的,避免急需内存时发生pgscand或kswapd回收内存不及时。

vm.extra_free_kbytes 分配多大合适呢?一般能应对流量高峰时1-2秒内存需求就可以了。free内存减少后,kswapd进程会在后台回收内存的,一般512M-2G可以满足要求。

context switch 高

有很多种情况都会导致 context switch。MySQL 中的 mutex 和 RWlock 在获取不成功后,短暂spin,还不成功,就会发生 context switch,sleep,等待唤醒。

在 MySQL中,mutex 和 RWlock导致的 context switch,一般在show global status,show engine innodb mutex,show engine innodb status,performance_schema等中会体现出来,针对不同的mutex和RWlock等待,可以采取不同的优化措施。

除了MySQL的mutex和RWlock,还发现一种情况,是MySQL外的mutex竞争导致context switch高。

典型症状:

  1. MySQL running 高,但系统 qps、tps 低
  2. 系统context switch很高,每秒超过200K
  3. 在 MySQL 内存查不到mutex和RWlock竞争信息
  4. SYS CPU 高,USER CPU 低
  5. 并发执行的SQL中出现timestamp字段,MySQL的time_zone设置为system

分析

对于使用 timestamp 的场景,MySQL 在访问 timestamp 字段时会做时区转换,当 time_zone 设置为 system 时,MySQL 访问每一行的 timestamp 字段时,都会通过 libc 的时区函数,获取 Linux 设置的时区,在这个函数中会持有mutex,当大量并发SQL需要访问 timestamp 字段时,会出现 mutex 竞争。

MySQL 访问每一行都会做这个时区转换,转换完后释放mutex,所有等待这个 mutex 的线程全部唤醒,结果又会只有一个线程会成功持有 mutex,其余又会再次sleep,这样就会导致 context switch 非常高但 qps 很低,系统吞吐量急剧下降。

解决办法:设置time_zone=’+8:00’,这样就不会访问 Linux 系统时区,直接转换,避免了mutex问题。

另外,对于spin消耗,MySQL配置变量中的innodb_spin_wait_delay 和 innodb_sync_spin_loops 可以用于微调。

<think>嗯,用户现在问的是MySQL数据库CPU使用率过如何排查。之前他们问过生产环境不能重启数据库的情况下如何监控内存分配,现在转向CPU的问题,可能他们正在面临性能问题,需要快速定位原因。作为技术支持,我需要提供一套系统化的排查方法,确保用户能在不重启数据库的情况下找到问题根源。 首先,我得回忆一下常见的CPU的原因。通常,CPU可能是因为查询效率低下,比如全表扫描、缺少索引、复杂连接操作,或者锁竞争、大量并发连接,甚至配置不当,比如缓冲区设置不合理。还有可能是硬件资源不足,比如CPU本身不够用。 接下来,我需要考虑用户可能的使用场景。用户是在生产环境,不能重启数据库,所以任何需要重启的调整都不适用。需要提供实时监控和动态调整的方法。用户可能没有root权限,或者只能通过SQL访问,所以需要分层次的解决方案,既有SQL层面的查询,也有系统命令,可能还有需要管理员权限的工具。 接下来,我需要组织回答的结构。通常,排查步骤应该从快速检查开始,逐步深入。比如先确认当前负载,然后检查慢查询,分析当前活动会话,查看锁情况,检查配置,最后系统级分析。可能还需要考虑复制延迟、存储引擎问题等。 需要确保提到的工具和命令都是可以在线执行的,不需要重启。比如使用SHOW PROCESSLIST、启用Performance Schema,使用sys schema,或者系统工具如top、perf、pt-query-digest等。还要注意权限问题,比如某些系统命令可能需要sudo,但用户可能没有权限,所以需要提供替代方案。 可能用户对某些工具不熟悉,需要给出具体的命令示例,比如如何启用Performance Schema的监控项,如何分析慢查询日志,如何使用pt-query-digest工具。同时要提醒注意事项,比如开启监控对性能的影响,特别是在负载时。 还要考虑用户可能需要紧急处理,比如如何快速终止有问题的会话,或者调整某些变量来缓解问题,比如调整线程池大小或连接数限制,但要注意这些调整可能需要权限,并且临时生效还是永久生效的问题。 另外,可能需要解释一些术语,比如缓冲池命中率、锁等待,但用户之前可能已经了解过内存监控,所以可能有一定基础,可以适当使用术语,但保持解释清晰。 最后,总结一个排查流程图或表格,帮助用户根据不同的场景选择合适的工具和方法,比如如果是慢查询导致,就检查慢日志;如果是锁竞争,就查看锁信息等。还要提醒用户长期优化的建议,比如定期分析慢查询,优化索引,调整配置参数等。 需要确保回答结构清晰,分步骤,每个步骤有具体的操作和示例命令,同时给出可能的原因和解决方案。还要注意安全性,比如在终止会话前确认是否为问题会话,避免误操作影响生产环境。</think>--- ### MySQL CPU使用率过排查指南(无需重启) --- #### 一、快速定位问题方向 **1. 确认CPU负载类型** ```bash # 查看CPU使用分布(用户态 vs 内核态) top -c -p $(pidof mysqld) ``` - **用户态CPU**:通常由SQL执行(如全表扫描、排序、复杂JOIN)导致 - **内核态CPU**:可能涉及锁竞争、I/O等待或网络问题 **2. 检查当前活动线程** ```sql -- 查看正在执行的SQL(实时快照) SHOW PROCESSLIST; -- 或使用sys Schema增强版视图 SELECT * FROM sys.processlist WHERE command != 'Sleep' ORDER BY time DESC; ``` --- #### 二、核心排查步骤 ##### 1. **抓取消耗SQL** **方法1:开启慢查询日志(动态配置)** ```sql -- 临时开启慢日志(无需重启) SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; -- 捕获超过1秒的查询 SET GLOBAL log_queries_not_using_indexes = ON; -- 确认日志路径 SHOW VARIABLES LIKE 'slow_query_log_file'; ``` **方法2:使用Performance Schema(MySQL 5.6+)** ```sql -- 启用事件监控 UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE 'events_statements%'; -- 查看CPU时间Top10的SQL SELECT DIGEST_TEXT, SUM(CPU_TIME)/1000000 AS cpu_sec, COUNT_STAR AS exec_count FROM performance_schema.events_statements_summary_by_digest ORDER BY cpu_sec DESC LIMIT 10; ``` ##### 2. **分析锁竞争** ```sql -- 查看当前锁等待 SELECT * FROM sys.innodb_lock_waits; -- 检查行锁冲突 SHOW ENGINE INNODB STATUS\G -- 关注 "TRANSACTIONS" 和 "LATEST DETECTED DEADLOCK" 部分 -- 查看表级锁 SHOW OPEN TABLES WHERE In_use > 0; ``` ##### 3. **检查配置与资源** ```sql -- 关键参数检查 SHOW VARIABLES LIKE 'innodb_thread_concurrency'; -- 并发线程限制 SHOW VARIABLES LIKE 'tmp_table_size'; -- 临时表内存阈值 SHOW STATUS LIKE 'Threads_running'; -- 当前活跃线程数 -- 缓冲池命中率(低于99%需警惕) SELECT (1 - (Variable_value / (Innodb_buffer_pool_read_requests + Innodb_buffer_pool_reads))) * 100 AS hit_rate FROM (SELECT VARIABLE_VALUE AS Innodb_buffer_pool_read_requests FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests') a, (SELECT VARIABLE_VALUE AS Innodb_buffer_pool_reads FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') b; ``` --- #### 三、系统级诊断工具 ##### 1. **perf 火焰图定位热点函数** ```bash # 安装perf sudo apt-get install linux-perf # Ubuntu sudo yum install perf # CentOS # 采集MySQL进程CPU使用(采样30秒) sudo perf record -F 99 -p $(pidof mysqld) -g -- sleep 30 sudo perf script > out.stack # 使用FlameGraph生成火焰图 git clone https://github.com/brendangregg/FlameGraph ./FlameGraph/stackcollapse-perf.pl out.stack | ./FlameGraph/flamegraph.pl > cpu.svg ``` ##### 2. **pt-query-digest 分析SQL** ```bash # 分析慢查询日志 pt-query-digest /var/lib/mysql/slow.log # 实时分析tcpdump流量(需权限) tcpdump -i eth0 -s 65535 -w - port 3306 | pt-query-digest --type tcpdump ``` --- #### 四、常见问题与应急处理 | 现象 | 快速验证方法 | 临时解决方案 | |-----------------------|----------------------------|--------------------------| | 全表扫描过多 | `EXPLAIN`查看执行计划 | 添加缺失索引 | | 排序操作消耗大 | 检查`Sort_merge_passes`状态 | 增大`sort_buffer_size` | | 大量临时表到磁盘 | 监控`Created_tmp_disk_tables`| 增大`tmp_table_size` | | 锁等待严重 | `SHOW ENGINE INNODB STATUS`| 终止阻塞事务(`KILL [ID]`)| **紧急降负操作:** ```sql -- 限制最大连接数(动态调整) SET GLOBAL max_connections = 50; -- 关闭查询缓存(如果启用) SET GLOBAL query_cache_size = 0; -- 终止消耗会话 SELECT id, time, info FROM information_schema.processlist WHERE command != 'Sleep' ORDER BY time DESC; KILL [problematic_thread_id]; ``` --- #### 五、长期优化建议 1. **索引优化** - 使用`pt-index-usage`分析索引有效性 - 定期运行`ANALYZE TABLE`更新统计信息 2. **架构调整** - 读写分离(使用MySQL Router或ProxySQL) - 热点数据拆分(分库分表) 3. **监控体系** ```bash # 推荐监控组合 Prometheus + Grafana + mysqld_exporter # 资源监控 Percona Monitoring and Management (PMM) # 专业MySQL监控 ``` --- ### 排查流程图 ``` CPU负载 → 检查用户态/内核态 → ├─用户态分析慢查询/执行计划 → 优化SQL或索引 ├─内核态 → 检查锁竞争/I/O → 调整事务隔离级别/优化磁盘 └─系统级问题 → 使用perf火焰图 → 定位代码层问题 ``` 通过以上方法,可在不重启数据库的情况下精准定位CPU问题根源。对于复杂场景,建议结合多种工具交叉验证。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值