InnoDB: Warning: a long semaphore wait

最近碰见案例,MySQL环境使用5.7.19~5.7.22版本 或 更低版本,经常会碰到以下信息:

InnoDB: Warning: a long semaphore wait:
813 --Thread 139957495039744 has waited at btr0cur.cc line 545 for 241.00 seconds the semaphore:
814 X-lock (wait_ex) on RW-latch at 0x7f4a60043da8 created in file dict0dict.cc line 2341
大致意思就是等待信号量。

通过以下的一些信息了解一下MySQL里信号量是指什么?

MySQL里哪里记录信号量信息

mysql> SHOW ENGINE INNODB STATUS\G;

SEMAPHORES

OS WAIT ARRAY INFO: reservation count 68581015, signal count 218437328
--Thread 140653057947392 has waited at btr0pcur.c line 437 for 0.00 seconds the semaphore:
S-lock on RW-latch at 0x7ff536c7d3c0 created in file buf0buf.c line 916
a writer (thread id 140653057947392) has reserved it in mode exclusive
Mutex spin waits 1157217380, rounds 1783981614, OS waits 10610359
RW-shared spins 103830012, rounds 1982690277, OS waits 52051891
RW-excl spins 43730722, rounds 602114981, OS waits 3495769
如果有高并发的工作负载,SEMAPHORES记录了信号量信息,它包含了两种数据:事件计数器以及可选的当前等待线程的列表。

1)OS WAIT ARRAY INFO: reservation count 68581015, signal count 218437328

这行给出了关于操作系统等待数组的信息,它是一个插槽数组,innodb在数组里为信号量保留了一些插槽,操作系统用这些信号量给线程发送信号,使线程可以继续运行,以完成它们等着做的事情,这一行还显示出innodb使用了多少次操作系统的等待:

保留统计(reservation count)显示了innodb分配插槽的频度,
信号计数(signal count)衡量的是线程通过数组得到信号的频度,
操作系统的等待相对于空转等待(spin wait)。

2)–Thread 140653057947392 has waited at btr0pcur.c line 437 for 0.00 seconds the semaphore:
这部分显示的是当前正在等待互斥量的innodb线程,在这里可以看到有两个线程正在等待,每一个都是以–Thread <数字> has waited…开始,这一段内容在正常情况下应该是空的(即查看的时候没有这部分内容),除非服务器运行着高并发的工作负载,促使innodb采取让操作系统等待的措施,

3)计数器信息
Mutex spin waits 1157217380, rounds 1783981614, OS waits 10610359 #这行显示的是跟互斥量相关的几个计数器
RW-shared spins 103830012, rounds 1982690277, OS waits 52051891 #这行显示读写的共享锁的计数器
RW-excl spins 43730722, rounds 602114981, OS waits 3495769 #这行显示读写的排他锁的计数器

innodb有着一个多阶段等待的策略,首先,它会试着对锁进行空转等待,如果经历了一个预设的空转等待周期(设置innodb_sync_spin_loops配置变量命令)之后还没有成功,那就会退到更昂贵更复杂的等待数组中。

mysql> SHOW GLOBAL VARIABLES LIKE ‘innodb_sync_spin_loops%‘;
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| innodb_sync_spin_loops | 30 |
+------------------------+-------+
image.png
在这个线程被挂起之前,线程等待一个InnoDB互斥对象被释放的次数。

4)空转等待的成本相对较低,但是它们要不停地检查一个资源能否被锁定,这种方式会消耗CPU周期,因为当处理器在等待IO时,一般都有一些空闲的CPU周期可用,即使是没有空闲的CPU周期,空等也要比其他方式更加廉价一些。
然而,当另外一个线程能做一些事情的时候,空转等待也还会把CPU独占着。
空转等待的替代方案就是让操作系统做上下文切换,这样,当一个线程在等待时,另外一个线程就可以被运行,然后,通过等待数组里的信号量发出信号,唤醒那个沉睡的线程,通过信号量来发送信号是比较有效的,但是上下文切换就很昂贵,这很快就会积少成多,每秒钟几千次的切换会引发大量的系统开销。

mysql> show engine innodb mutex;
+--------+------------------------+---------+
| Type | Name | Status |
+--------+------------------------+---------+
| InnoDB | rwlock: log0log.cc:846 | waits=3 |
+--------+------------------------+---------+
1 row in set (0.01 sec)
经上述内容可以理解到因MySQL锁机制,其他事件需要进行等待处理。

解决方式

1.扩大内核参数:

[root@ss30 bak]# cat /proc/sys/kernel/sem
250 32000 32 128
[root@ss30 bak]# echo "kernel.sem=250 32000 100 128" >> /etc/sysctl.conf
[root@ss30 bak]# sysctl -p
vm.max_map_count = 262144
kernel.sem = 250 32000 100 128

cat /proc/sys/kernel/sem

250 32000 32 128
说明:
第一列,表示每个信号集中的最大信号量数目。
第二列,表示系统范围内的最大信号量总数目。
第三列,表示每个信号发生时的最大系统操作数目。
第四列,表示系统范围内的最大信号集总数目。

MySQL关闭 adaptive hash index功能
官方说明 和 bug 结合
mysql> SHOW GLOBAL VARIABLES LIKE ‘innodb_adaptive_hash_index‘;
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| innodb_adaptive_hash_index | ON |
+----------------------------+-------+
1 row in set (0.01 sec)

mysql> SET GLOBAL innodb_adaptive_hash_index=OFF;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW GLOBAL VARIABLES LIKE ‘innodb_adaptive_hash_index‘;
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| innodb_adaptive_hash_index | OFF |
+----------------------------+-------+
1 row in set (0.01 sec)
之后再也没有出现过,类似问题。

/etc/my.cnf 添加
innodb_adaptive_hash_index=OFF

InnoDB: Warning: a long semaphore wait

上一篇:win10 下安装Oracle 11g问题:INS-13001环境不满足最低要求


下一篇:PowerDesinger mysql 反向工程时报错