资讯动态

MySQL CPU 100%排查:竟是SELinux守护进程在作祟

发布时间:2026/10/20 8:28:10 来源:尧图企业网站定制
1. 现象复现一个莫名其妙的CPU 100%先说个背景。我们有一套内部系统数据库用的MySQL跑在标准Linux发行版上硬件配置不算差业务量也不算大。正常情况下MySQL的CPU占用率也就百分之十几空闲时段更低。但有一个阶段运维同事反馈某个节点的MySQL服务器CPU负载时不时冲到接近100%而且没有任何明显的业务高峰。最初的第一反应当然是查慢查询。这通常是MySQL CPU异常的常规切入点毕竟慢SQL、全表扫描、笛卡尔积连接都是CPU杀手。结果慢查询日志翻了一页又一页除了一些常规的索引缺失没有发现任何值得怀疑的、能造成CPU打满的语句。接着又查了SHOW FULL PROCESSLIST看会话状态也是一片祥和没有诡异的大量Sending data或Sorting result堆积。然后又怀疑是不是系统本身的问题。top一看MySQL进程的CPU占比其实不高反而是系统中有一个名字很陌生的进程占据了大量CPU——setrountroubleshootd准确说是setroubleshootd。光看名字一时半会很难把它和数据库性能挂钩。当时的第一反应是这个进程是从哪冒出来的服务器上默认装了一大堆系统组件很多平时根本不会注意到。一查资料才知道它是SELinux的一个守护进程专门负责在SELinux拦截操作时生成告警通知比如通过auditd日志、桌面通知或系统日志把被阻止的操作反馈给用户。问题麻烦了这个进程和MySQL在同一个系统上CPU占用异常高而MySQL本身正常。那到底是什么被SELinux反复拦截才导致这个守护进程忙个不停顺着这个线索查下去发现罪魁祸首是SELinux对某个路径访问策略的拦截导致MySQL在读取特定目录下的文件时不断触发SIGCHLD、ptrace或类似的系统调用系统审计组件把这些事件全部记录并转发给setroubleshootd处理。拦截越频繁这个守护进程的CPU消耗就越高。这里值得先解释一个关键背景SELinux和MySQL的关系经常被忽视。尤其是那些从旧系统迁移过来的环境或者使用自定义编译二进制包的场景SELinux的默认策略往往没有为MySQL的特定行为放行这时候不是MySQL本身慢而是系统安全机制在背后做太多事情。这个坑很典型CPU爆高的表象容易让人把所有注意力都放在数据库层最后才发现问题在系统安全层。接下来我把整个排查链路、验证过程和处理方法完整记录下来方便以后遇到类似情况的人少走弯路。2. 排查思路从业务层到系统层的完整链路遇到CPU异常最重要的是别急着下结论。我们需要一个系统化的排查路径从业务层逐步下沉到系统层。整个排查过程可以拆成五个步骤每一步都有明确目的和验证方法。2.1 第一步先排除业务SQL问题在确认是系统进程异常之前必须先排除业务SQL导致的CPU问题否则后续所有判断都是空中楼台。具体操作如下开启并查看慢查询日志确认slow_query_log已开启long_query_time设为了一个较小值比如1秒然后分析慢查询日志中的TOP SQL。如果业务量大可以用mysqldumpslow工具聚合统计。实时观察会话状态用SHOW FULL PROCESSLIST多次采样关注State列的分布。正常的业务高峰会出现较多的Sending data、Copying to tmp table但这些都是有业务量的不会是异常CPU爆高的直接元凶。检查InnoDB相关指标用SHOW ENGINE INNODB STATUS\G查看当前事务状态。要注意是不是有大量回滚、死锁检测、自适应哈希索引冲突等情况这些虽然不常见但确实会导致CPU起飞。这次排查中慢查询日志确实有一些索引缺失导致的语句但修正之后CPU并没有回落说明和慢SQL无关。SHOW FULL PROCESSLIST也没有发现大量堆积的会话。也就是说数据库层没有异常的迹象。提示这里有一个容易被忽略的点——如果监控工具只统计了MySQL进程本身的CPU而没统计系统整体CPU那很可能会漏掉setroubleshootd这个元凶。所以要看的不是MySQL进程的CPU而是整机CPU占用分布。2.2 第二步用top定位真正的CPU消耗大户当数据库层没有异常时必须立刻转向系统层。top命令是最直接的武器注意几个关键点按P键按CPU使用率排序而不是默认的按内存排序。观察进程的实时CPU占用而不是平均状态。留意那些名字陌生、平时没见过但长期占用CPU的进程。实际执行后发现setroubleshootd进程的CPU占用在50%~90%之间波动而MySQL进程的CPU只占20%左右。这个反差已经能说明问题了。继续深挖又注意到auditdLinux审计守护进程的CPU使用率也有上升趋势虽然没有setroubleshootd那么夸张但两个进程同时活跃说明系统审计事件频繁发生。2.3 第三步关联SELinux审计日志定位到setroubleshootd之后下一步是搞清楚它到底在处理什么事件。SELinux的审计日志是核心线索。# 查看SELinux相关审计日志 sudo ausearch -m AVC -ts recent # 也可以用journalctl查看最近的SELinux拦截记录 sudo journalctl -t setroubleshootd --since 1 hour agoausearch输出里会出现类似下面的记录typeAVC msgaudit(1691234567.890:12345): avc: denied { read } for pid2023 commmysqld namesomefile devsda1 ino123456 scontextsystem_u:system_r:mysqld_t:s0 tcontextunconfined_u:object_r:default_t:s0 tclassfile关键信息解读denied { read }这次操作被SELinux策略拒绝的权限类型是read。scontext源上下文也就是发起操作的进程的SELinux标签。这里是mysqld_t说明是MySQL进程。tcontext目标上下文也就是试图访问的文件的SELinux标签。这里是default_t说明目标文件类型不在MySQL可访问的范围。tclass目标类型类别这里是file表示是一个普通文件的读取操作。这类AVCAccess Vector Cache记录表示一个进程试图访问某个它没有被授权访问的资源。SELinux策略阻止了这次访问同时把事件记录下来。setroubleshootd的工作就是实时监听这些事件并把它们翻译成人能读懂的告警信息。如果出现大量类似的AVC记录且都指向mysqld就要意识到一个严重问题MySQL的每一次文件访问被SELinux拦截后都会触发一次审计事件而setroubleshootd需要处理这些事件。如果把这次数放大到每秒几十上百次CPU自然就上去了。2.4 第四步确认拦截源——为什么会触发大量AVC日志有了但还需搞清楚为什么MySQL会频繁访问一个SELinux标签不合适的文件路径。常见原因有以下几类原因类型典型场景排查方法数据目录放在非标准路径MySQL使用的datadir、tmpdir等位于/home或自定义挂载点ls -Z检查目录SELinux标签使用了自定义二进制包手动编译或静态链接的MySQL文件标签可能缺失restorecon重置标签错误配置了file_contexts之前手动修改过SELinux文件上下文导致规则错乱semanage fcontext -l核对数据库文件权限/属主与标签不一致迁移数据库文件后未重新打标签ls -Z对比标准标签在这个案例里查到的原因是数据目录被放置在/data/mysql而/data这个路径没有被SELinux默认策略覆盖到。因此该目录下的所有文件都属于default_t类型MySQL进程自然无法正常访问。这个原因非常典型。很多运维/开发人员习惯把数据库数据目录放到业务挂载盘上或者在初始化MySQL时自定义了目录但忘记了SELinux的标签规则。平时不出问题是因为访问被缓存或者数据量不大一旦业务并发上来每次文件访问触发一次SELinux审计累积起来就是CPU灾难。2.5 第五步判断是否可以直接修复明确拦截原因后还应该再对比一下到底是改SELinux规则还是修复文件的SELinux标签更合理。这取决于问题本质。在前面的例子中数据目录放在非标准路径文件标签错误最合理的做法是恢复文件的标准标签而不是一禁了之。如果用setenforce 0临时关闭SELinux问题确实瞬间消失但风险也很大SELinux是系统的一道安全防线直接在生产环境关闭它等于把整台机器暴露在处理失控的边缘尤其是这台机器还承载着数据库业务。所以整体结论很明确木马并不在MySQL里面而在SELinux和文件标签之间的错位。3. 修复方案针对非标准路径的SELinux标签恢复处理这个问题我的建议是不要关闭SELinux而是精确修复文件上下文。这样既保住安全能力也消除性能隐患。3.1 查看当前的SELinux状态先用基本命令确认SELinux当前状态# 查看SELinux运行模式 getenforce # 查看MySQL数据目录的SELinux标签 ls -Z /data/mysql/正常情况下MySQL数据目录里的文件应该都有mysqld_db_t这样的类型标签。如果看到一堆default_t或者unlabeled_t那就是标签不对劲了。3.2 恢复标准标签恢复标签的标准做法是使用restorecon。它会把目标路径下的文件标签恢复到SELinux策略中定义的标准标签覆盖那些刚创建或未被打标签的文件。# 恢复数据目录及子目录的标准标签 sudo restorecon -Rv /data/mysql/执行后再次用ls -Z查看应该会看到文件类型变成了mysqld_db_t。需要注意的是restorecon -R会遍历目录下所有文件如果数据库文件非常多可能需要一段时间业务高峰期间要慎重操作最好选择低峰期或维护窗口执行。如果是新建目录且从未被SELinux策略覆盖restorecon可能认不出来这个路径应该用哪个标签这时候需要手动建立映射关系。3.3 为自定义路径建立SELinux策略映射如果数据目录放在/data/mysql这类自定义路径没有任何默认的SELinux上下文规则那么需要手动告诉SELinux这个路径下的文件应该使用什么类型。# 为自定义数据目录添加SELinux文件上下文规则 sudo semanage fcontext -a -t mysqld_db_t /data/mysql(/.*)? # 然后重新打标签 sudo restorecon -Rv /data/mysql//data/mysql(/.*)?这个正则表达式的意思是匹配/data/mysql本身以及它下面所有子目录和文件。这样设置的规则是持久化的即使以后restorecon被再次执行也能正确恢复标签。需要注意的是除了数据目录MySQL还可能访问日志目录、tmpdir、socket文件目录、pid文件目录等。这些目录如果都放在自定义路径下需要逐一确认并设置正确的标签类型数据文件目录mysqld_db_t日志目录mysqld_log_t临时目录mysqld_tmp_t配置目录etc_tPID/socket目录mysqld_var_run_t用semanage fcontext -l | grep mysqld可以查看系统已有的MySQL相关上下文规则对照一下自己环境里哪些路径没被覆盖。3.4 修改端口等非文件类场景如果问题不是文件访问而是MySQL监听了自定义端口比如从默认的3306改到13306SELinux同样会拦截。这属于端口标签问题处理方式完全不同# 查看当前SELinux允许的MySQL端口列表 sudo semanage port -l | grep mysqld # 添加自定义端口 sudo semanage port -a -t mysqld_port_t -p tcp 13306不过在这次CPU爆高案例中端口不是主要矛盾重点是文件标签。3.5 修复后的验证修复完成后务必做一轮验证再次查看AVC日志确认ausearch -m AVC -ts recent不再出现mysqld相关的denied记录。观察CPU曲线在业务流量下观察5~10分钟setroubleshootd和auditd的CPU是否回落。确认MySQL功能正常跑几个常规查询、写入一些测试数据确认数据目录访问没问题。检查SELinux状态getenforce确认仍处于Enforcing模式安全机制没有被动过。修复完成后top里的setroubleshootd基本安静了CPU占用从高峰期的50%~90%降到了0%~1%MySQL服务器的整体CPU从接近100%回落到正常水平。注意如果验证时发现restorecon后标签依然不正确或者AVC日志变化不大建议检查服务器上是否有其他Agent软件或监控脚本在持续扫描文件系统。某些监控Agent的运行会触发大量文件遍历操作导致SELinux审计事件暴增。这个问题曾经在我维护的另一台服务器上出现过当时排查了MySQL的配置半天最后发现是监控Agent每隔几秒扫描一次根目录SELinux拦截日志堆积得比MySQL还多。4. 更深一层为什么setroubleshootd会成为CPU消耗点很多人会有疑问SELinux拦截事件归拦截事件为什么会体现在一个叫setroubleshootd的进程上这个进程工作时到底在做什么4.1setroubleshootd的机制setroubleshootd是SELinux的故障排除守护进程它监听auditd产生的事件把kernel发出的AVC拒绝信息进行解析、分析然后通过DBus发送给桌面提示组件或者写入系统日志。设计初衷是为了让用户能发现你的操作被SELinux阻止了从而引导用户去调整策略。它的职责决定了它在大量AVC事件到来时会很忙读取审计事件解析事件的scontext/tcontext信息用策略分析当前拒绝属于哪类问题将结果格式化并发送通知处理可能的缓冲队列如果事件数量不多这套流程的CPU消耗可以忽略。但如果系统持续产生大量拒绝事件它就会像一个反复处理邮件的客服一样忙到爆。这里有一个细节值得注意setroubleshootd处理审计事件时不止是简单的日志转发它会加载并匹配SELinux策略中的规则判断这个拒绝是常见的错误配置还是潜在的攻击行为这个过程涉及策略数据库的分析比普通的日志转发要昂贵得多。时间戳、上下文、目标类型、权限集合都需要逐一解析。大量重复事件时这种解析的消耗会被叠加放大。4.2 为什么MySQL进程看起来没事另一个容易困惑的点是SELinux明明拦的是MySQL的访问为什么MySQL自己的CPU不高因为SELinux拦截MySQL文件读取后MySQL本身只会收到一个EACCES权限不足错误。MySQL对不同场景的错误处理策略不同如果读取的是核心文件大概率会直接报错退出但如果读取的是一些非关键文件比如缓存文件、临时文件、状态文件MySQL可能会选择忽略错误继续往下跑。在这种情况下MySQL真正消耗的CPU并不高而系统层面却被不断触发审计事件拖累。这也是为什么这类问题很隐蔽从数据库监控面板看MySQL各项指标几乎正常但如果打开top观察整机才会发现问题在另一个进程上。4.3 什么场景最容易触发这类问题根据实际经验以下几类场景最容易触发类似问题MySQL数据目录不在标准路径且未配置SELinux fcontext规则。数据库文件迁移时直接cp或rsync到新目录没有保留SELinux扩展属性。使用第三方备份恢复工具文件属性和安全上下文全部丢失。底层文件系统为网络存储如NFS等SELinux对NFS文件的默认标签处理较为特殊。频繁使用chown和chmod修改文件属主权限但没有配合SELinux标签处理。如果是前几类restorecon配合semanage fcontext就能解决如果是NFS或特殊文件系统的场景还需要额外考虑挂载选项和SELinux的use_nfs_home_dirs之类的布尔值开关这里不过多展开。5. 一条更稳妥的排查路径先看SELinux再动数据库这次排查给我的最大收获是CPU类问题排查顺序和信息收集范围决定了效率。以前遇到CPU高我习惯先在MySQL这层找半天现在经验多了会把系统层的top、auditd、SELinux状态也一并纳入初始采集范围。如果你是第一次遇到类似问题可以按照下面的顺序快速定位先看整机CPU分布top里有没有非业务进程占用CPU尤其setroubleshootd、auditd、systemd-journald这类系统进程。再看MySQL会话排除慢SQL和会话堆积。对照AVC日志如果有mysqld相关的大量denied记录基本可以锁定SELinux拦截。检查文件标签和路径ls -Z对比标准路径找出标签异常点。修复标签验证效果。这套顺序的核心逻辑是先把大方向定性再动手处理。如果一上来就去优化SQL或调整MySQL参数很可能忙半天找不到症结。补充一个实用性技巧在临时排查时如果想快速确认是不是SELinux拖累CPU可以临时切到Permissive模式观察几分钟但不要直接永久关闭# 临时切换到Permissive模式仅用于快速验证 sudo setenforce 0 # 观察CPU确认是否回落 top # 验证完切回Enforcing sudo setenforce 1如果CPU明显回落说明问题确实在SELinux如果CPU没有变化再回到MySQL本身去排查不迟。这个验证方法比看日志更直观但必须记得切回Enforcing模式。6. 预防措施从此不再被SELinux偷袭经历过这次事件后我在新环境初始化数据库时会主动做几件事也算是对这次踩坑的复盘输出。6.1 初始化时就检查SELinux标签不管用包管理器安装MySQL还是自定义部署装完后的第一件事就是用ls -Z确认所有MySQL相关路径的标签。标准路径一般不会有问题但如果自定义了数据目录一定记得补上semanage fcontext规则。6.2 监控SELinux拒绝事件auditd本身会记录所有AVC事件但如果没人看等于白记。建议做一个简单的事件监控# 把AVC事件写入独立日志文件 sudo auditctl -a always,exclude -F msgtypeAVC -F exe/usr/sbin/mysqld或者更简单一点用脚本定期检查最近的小时内是否有大量新AVC记录sudo ausearch -m AVC -ts recent | awk {print $1} | uniq -c如果某类denied事件的频率异常升高就要关注了。这个监控实际上也是通过auditd的规则实现的核心目的就是及时感知SELinux拒绝事件的异常变化避免累积成性能事故。6.3 不要轻易关闭SELinux在不少旧教程里刚装完MySQL第一步就是教你setenforce 0。在个人开发机上问题不大但在生产环境这不是正确姿势。尤其是在服务器暴露在外网、承载真实业务的情况下关闭SELinux等于拆掉了一道重要防线。从我的角度看SELinux真正复杂的地方在于策略的抽象程度高参数多短时间内理解全部机制很难。但也正因为如此遇到问题才更要学会用工具解决而不是一刀切关闭。学会给文件打标签、学会用semanage管理上下文规则比背一百条SELinux配置命令都值。6.4 定期检查文件标签一致性即使初始化时妥善处理了标签后续运维操作也可能会破坏一致性。例如某些备份恢复脚本会复制文件到临时目录再拷回来中间可能丢失SELinux属性。建议在备份恢复流程中加入标签恢复步骤# 备份恢复后执行 sudo restorecon -Rv /data/mysql/这是一种比较轻量的数据完整性校验与修复手段成本低但至少可以保持标签状态可控。7. 写在最后长教训比修故障更重要这次CPU爆高的问题从发现到完全修复前后折腾了不少时间。回头看真正消耗时间的不是修复动作本身而是为什么会往MySQL上找原因这个惯性思维。MySQL CPU高直觉上就是SQL的问题、参数的问题、连接数的问题很少有人会第一时间想到SELinux的守护进程。但现实是在Linux服务器上任何进程的CPU异常都可能被系统安全组件影响。SELinux的审计机制、日志解析、告警通知设计初衷是为了安全和可观测性但在配置不当的情况下反而会成为性能瓶颈。我遇到这个问题之后再处理类似场景时会把系统层的排查动作前置尤其会关注那些平时存在感很低但在特定条件下会放大问题的系统组件。数据库运维里真正决定故障处理速度的往往不是你知道多少优化技巧而是你排错时的信息视野够不够宽。CPU问题不要只看数据库进程整机视野、系统日志、SELinux状态的全局观测才是快速定位的根本。最后再分享一个小技巧给服务器做日常巡检时除了看磁盘空间、内存占用、慢SQL不妨顺带看一下SELinux拒绝事件的统计。如果发现某个进程的denied事件异常增多先别急着忽略它很可能就是下一次CPU爆高的前兆。提前几分钟发现可能就能省下几个小时的排查时间。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑