首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >mysql 信号量与进程状态

mysql 信号量与进程状态

原创
作者头像
大大刺猬
发布2026-02-03 17:54:07
发布2026-02-03 17:54:07
550
举报
文章被收录于专栏:大大刺猬大大刺猬

导读

前段时间遇到某数据库进程状态为: T (disk sleeping) , 即在处于一个不可中断的IO等待中, 此时数据库无法连接(包括socket), 估计是cgroup达到限制了, 干掉同组的其它进程后,资源释放, 状态变成了T (stopped),这个状态和ctrl+z类似, 于是发送"SIGCONT"信号量之后变成了Sleep.

这就让人好奇mysql支持哪些信号量,进程状态又能在哪些状态之间变化?

能力有限, 简单分析分析

信号量

我们查询官网(https://dev.mysql.com/doc/refman/8.0/en/unix-signal-response.html)发现如下:

  1. SIGTERM 和shutdown命令效果一样
  2. SIGHUP 就是flush语句的效果
  3. SIGUSR1 就是flush error log, slow log,general query log
  4. SIGINT (Ctrl+c) 通常被忽略(除非 --gdb)

有丢丢简单啊, 本着习惯,我们看看源码sql/mysqld.cc, 发现还有SIGQUIT,SIGUSR2. 而且有各信号量更具体的信息. 再次整理得到 mysqld 信号量处理

信号量

作用

SIGTERM/SIGQUIT

正常停库

SIGHUP

REFRESH_LOG,REFRESH_TABLES,REFRESH_FAST,REFRESH_GRANT,REFRESH_THREADS,REFRESH_HOSTS

SIGUSR1

REFRESH_ERROR_LOG,REFRESH_GENERAL_LOG,REFRESH_SLOW_LOG

SIGUSR2

重启

SIGINT

忽略

mysql怎么实现重启的?

SIGUSR2信号量能重启mysqld, 这个重启就比较"神奇", 进程还能自己重启自己? 怎么实现的?

其实只是退出的时候设置下进程的退出状态码而已,启动还是由mysqld_safe/system等其它线程来实现.

代码语言:c++
复制
/**
  Exit code used by mysqld_exit, my_thread_exit function which allows
  for external programs like systemd, mysqld_safe to restart mysqld
  server. The exit code 16 is chosen so it is safe as InnoDB code
  exit directly with values like 3.
*/
constexpr const int MYSQLD_RESTART_EXIT{16};

既然mysqld_safe能根据退出状态码重启mysqld, 我们就来验证验证:

代码语言:shell
复制
ps -ef | grep mysqld
kill -SIGUSR2 35425
ps -ef | grep mysqld

还真就重启了, 我们来看下mysqld日志呢:

再看看mysqld_safe的输出:

我们再简单看看mysqld_safe脚本的逻辑:

代码语言:shell
复制
while true
do
  start_time=`date +%M%S`
  eval_log_error "$cmd"
  if [ $? -eq 16 ] ; then
    dont_restart_mysqld=false
    echo "Restarting mysqld..."
  else
    dont_restart_mysqld=true
  fi
  ....
done

确实做了退出状态码的判断的, 如果退出状态码是16,则设置dont_restart_mysqld=false, 后面如果就是'if dont_restart_mysqld; do something... break; 然后才是重启'

该说不说这变量名字dont容易看出do....

所以以后即使没有写重启脚本, 也能使用kill -SIGUSR2 mysqld_pid来实现重启mysql了, 还是比较方便.

如果对信号量感兴趣,可以看下https://www.man7.org/linux/man-pages/man7/signal.7.html, 太TM多了

其实客户端mysql也有信号量处理的,虽然就一个SIGINT(ctrl+c),但还是挺好用的,mariadb之类的就没得....

写脚本的时候可能也需要处理信号量,这里给下shell/python的信号量捕获方法:

代码语言:shell
复制
# shell
RUN_FUNC(){
	echo "Ctrl+c"
}
trap 'RUN_FUNC' SIGINT

# python
import signal
def RUN_FUNC(sig,frame):
	print("Ctrl+c")
signal.signal(signal.SIGUSR1, RUN_FUNC)

进程状态

然后我们来瞅瞅进程状态, 进程状态一共有6种:

|进程状态|描述|

|-|-|-|

|D|uninterruptible sleep, 不可中断, 就是文章开头遇到的那种|

|R|running, 就是正在运行,通常只有少部分是这个状态,除非你cpu贼多,进程也贼多|

|S|sleeping 通常多数进程都是Sleeping的,等待唤醒. show processlist看到的sleep估计也是这么设计的|\

|T|stopped by job control signal 就是文章开头的第二种状态|

|t|stopped by debugger during trace 使用gdb之类调试的时候的状态|

|Z|zombie 僵尸状态, 被僵尸感染了,变异了,被豌豆射手克制,之前cf朝歌遗迹绳子小僵尸排队找救赎天使打针-_-|

既然已经知道了这几种状态, 那就来模拟模拟吧! 主要使用cgroup(control group based traffic control filter)来限制资源, 那就先看看cgroup怎么使用吧:

我这里是centos 7.9环境,是支持crgoup的, 虽然是v1

代码语言:shell
复制
# 创建cgroup名字, 叫test_cgroup, 其实就是一个目录,然后往目录里面写东西,这就是vfs的方便之处....
mkdir /sys/fs/cgroup/memory/test_cgroup

# 限制使用20MB内存
echo "20971520" > /sys/fs/cgroup/memory/test_cgroup/memory.limit_in_bytes

# 把要限制的进程pid加进去
echo "16961" >> /sys/fs/cgroup/memory/test_cgroup/cgroup.procs

memory.limit_in_bytes是限制内存的,但超过内存之后会使用swap(如果有swap的话).

memory.memsw.limit_in_byte 是限制内存和swap使用的, 默认是9223372036854771712, 就是不限制swap大小.

所以要到达内存限制的话, 可以关闭swap (swapoff -a)

超过内存的进程会被kill

D 不可中断

有些状态是不可中断的, 比如IO, 那我们就使用cgroup来限制进程的IO

代码语言:shell
复制
# 创建IO相关的cgroup目录
mkdir /sys/fs/cgroup/blkio/test_cgroup

# 设置指定设备的IO大小
# lsblk看到的MAJ:MIN 就是这里的253:0   1就是限制的值,单位是byte. 我这里限制的是带宽,还可以限制iops
echo "253:0 1" > /sys/fs/cgroup/blkio/test_cgroup/blkio.throttle.read_bps_device
echo "253:0 1" > /sys/fs/cgroup/blkio/test_cgroup/blkio.throttle.write_bps_device

# 把要限制的进程pid加入进去
echo `pidof mysqld` >> /sys/fs/cgroup/blkio/test_cgroup/cgroup.procs

# 线程也是可以加进来的,看自己需求了.
ps -T -p `pidof mysqld` | awk '{if ($2!="SPID") print $2}' | xargs -t -i echo {} >/sys/fs/cgroup/blkio/test_cgroup/tasks 2>/dev/null

# 取消限制
echo "253:0 0" > /sys/fs/cgroup/blkio/test_cgroup/blkio.throttle.read_bps_device
echo "253:0 0" > /sys/fs/cgroup/blkio/test_cgroup/blkio.throttle.write_bps_device
echo "253:0 0" > /sys/fs/cgroup/blkio/test_cgroup/blkio.throttle.read_iops_device 

然后就是触发主进程的IO操作,我知道的主进程的IO操作就只有shutdown... 正好我们用信号量来做.

代码语言:shell
复制
head /proc/`pidof mysqld`/status
kill -15 `pidof mysqld`
head /proc/`pidof mysqld`/status

确实从S (sleeping)变成了D (disk sleep), 蒸蚌!

T (stopped)

R和S常见,就不看了, T其实就是ctrl+z

是不是很简单, 那mysqld怎么进入T状态呢?

当然是使用信号量啊, 直接

代码语言:shell
复制
kill -SIGSTOP `pidof mysqld`

但之前测试的时候,误打误撞从D进入了T. 原因不明,大概操作如下:

代码语言:shell
复制
# 配置线程级别的io限制
mkdir -p /sys/fs/cgroup/blkio/test_cgroup
echo "253:0 1" > /sys/fs/cgroup/blkio/test_cgroup/blkio.throttle.read_bps_device
echo "253:0 1" > /sys/fs/cgroup/blkio/test_cgroup/blkio.throttle.write_bps_device
ps -T -p `pidof mysqld` | awk '{if ($2!="SPID") print $2}' | xargs -t -i echo {} >/sys/fs/cgroup/blkio/test_cgroup/tasks 2>/dev/null

# 然后server执行sql(有IO就行)
mysql -h127.0.0.1 -P3314 -p123456 -e "show databases;"

# 此时应该能看到线程处于D状态
for i in /proc/`pidof mysqld`/task/*; do grep State ${i}/status;done;

# 然后等了一段时间,忘记多久了,但肯定不止10分钟.放开资源限制:
echo "253:0 0" > /sys/fs/cgroup/blkio/test_cgroup/blkio.throttle.read_bps_device
echo "253:0 0" > /sys/fs/cgroup/blkio/test_cgroup/blkio.throttle.write_bps_device

# 再观察主进程状态, 发现就变成了T (stopped)
head /proc/`pidof mysqld`/status

总结

mysql信号量处理:

  1. SIGTERM/SIGQUIT就是停库
  2. SIGUSR2就是重启,
  3. SIGHUB就是FLUSH全部,
  4. SIGUSR1就是FLUSH日志.

mysql进程通常处于Sleep/Running状态,

  1. 如果出现了较长的D则表示IO等可能有问题.
  2. 如果是T的话,就是被人暂停了(SIGSTOP),使用SIGCONT就能恢复.
  3. 如果是t的话,就是有人在使用gdb调试.

文章开头的案例并没有完整的复现出来, 但基本上可以确定是cgroup配置不合理,或者资源争抢太离谱之类的.

参考:

https://dev.mysql.com/doc/refman/8.0/en/unix-signal-response.html

https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/blkio-controller.html

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 导读
  • 信号量
    • mysql怎么实现重启的?
  • 进程状态
    • D 不可中断
    • T (stopped)
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档