结合源码分析show engine innodb status输出内容(一)

show engine innodb status输出内容包括以下几块(我所有试验都是在mysql5.6.22上实现)

  • background thread
  • semphores
  • latest detected deadlock(该部分内容不一定输出,只有在系统出现过死锁情况下才输出)
  • latest foreign key error(该部分内容不一定输出,只有在系统出现过外键错误情况下才输出)
  • transactions
  • file I/O
  • logs
  • buffer pool and memory
  • insert buffer and adaptive hash index
  • row operations
接下来我将对各个模块输出内容进行解析

1.background thread(后台线程)

结合源码分析show engine innodb status输出内容(一)_第1张图片
图1 background thread 输出

在执行show engine innodb status命令之后,在background thread之前的信息是innodb status相关状态信息,per second averages calculated from the last 29 seconds意思是说接下来的信息,是对过去29秒时间内innodb状态的输出。

Innodb存储引擎室多线程的模型,因此其后台有多个不同的后台线程负责处理不同的任务。Master thread是一个非常核心的后台线程,主要负责缓冲池中的数据异步刷新到磁盘,保证数据的一致性。Master thread具有最高的线程优先级别,其内部由多个循环(loop)组成:主循环,后台循环,刷新循环,暂停循环。

参数

说明

Srv_master_thread loops

Master线程的循环次数,master线程在每次loop过程中都会sleepsleep的时间为1秒。而在每次loop的过程中会选择activeshutdownidle中一种状态执行。Master线程在不停循环,所以其值是随时间递增的。

Srv_active

Master线程选择的active状态执行。Active数量增加与数据表、数据库更新操作有关,与查询无关,例如:插入数据、更新数据、修改表等。

Srv_shutdown

这个参数的值一直为0,因为srv_shutdown只有在mysql服务关闭的时候才会增加。

Srv_idle

这个参数是在master线程空闲的时候增加,即没有任何数据库改动操作时。

Log_flush_and_write

Master线程在后台会定期刷新日志,日志刷新是由参数innodb_flush_log_at_timeout参数控制前后刷新时间差。

:Background thread部分信息为统计信息,即mysql服务启动之后该部分值会一直递增,因为它显示的是自mysqld服务启动之后master线程所有的looplog刷新操作。通过对比activeidle的值,可以获知系统整体负载情况。Active的值越大,证明服务越繁忙。

结合源码分析show engine innodb status输出内容(一)_第2张图片

图2 master thread loop


在图2中可以看到,master thread作为后台程序,一直不停的在loop,而loop过程中会选择active task 或者idle task函数执行,只有在innodb_force_recovery(对应图2的srv_force_recovery)参数设置值大于2的情况下,master thread会进入suspend状态。在srv_shutdown_state的值不等于SRV_SHUTDOWN_NONE的情况下,master thread会去执行shutdown task。输出值srv_active,srv_idle,srv_shutdown的值分别是在函数srv_master_do_active_task,srv_master_do_idle_task,srv_master_do_shutdown_task中++。而srv_master_do_active_task的执行是由当前activity_count与old_activity_count的值决定,只要这两个值不相等,则master调用active task。在执行数据库更改操作,如插入,删除,更新,表操作的情况下,当前的activity count的值会增加,系统调用active task。所以说,srv_active的值可以反映出系统的负载情况。值得注意的是background thread 部分的输出值是历史统计值,即mysqld服务启动之后会一直递增。


srv_master_do_active/idle/shutdown_task函数实现大体类似,区别主要在与函数返回情况不同。

srv_active


srv_idle


srv_shutdown



master thread还负责日志(redo log:重做日志)刷新log_flush_and_write记录了日志刷新的次数。在master thread中通过调用srv_sync_log_buffer_in_background函数实现日志的刷新。master thread并不是没次loop都刷新日志,而是每秒都会刷新日志,如图3所示,只有在前后刷新时间差大于等于srv_flush_log_at_timeout(对应系统参数innodb_flush_log_at_timeout)时才刷新日志。该函数调用了log_buffer_sync_in_background函数,在log_buffer_sync_in_background函数中主要的操作包括四步:①获取log_sys->mutex;②获取log的lsn;③释放log_sys->mutex;④调用log_write_up_to函数将log刷新到磁盘,如图4所示。


结合源码分析show engine innodb status输出内容(一)_第3张图片

图3 日志刷新函数

结合源码分析show engine innodb status输出内容(一)_第4张图片



图4 日志刷新操作

2.semphores


Semphores输出内容可以分成两段,一部分是当前的等待,这部分只是包含了在高并发环境下的全部记录(这些记录不包括sleep)。第二部分是事件统计。相比系统等待,自旋锁的开销较小,但是它是活跃的等待,会浪费CPU资源,如果有大量的自旋等待和自旋轮转,则会浪费大量的CPU资源。

参数

说明

OS WAIT ARRAY INFO

系统等待队列信息

Reservation count

线程尝试访问os wait array的次数,大于等于线程进入os wait状态的线程数(因为尝试放入os wait array可能不成功,不成功的时候reservation count也会++)

Signal count

线程被唤醒的次数,进入os wait的线程,在占用资源线程释放mutex的时候会通过signal唤醒等待线程。

Mutex spin wait

线程自旋等待次数,线程在获取mutex过程中如果没有获取到mutex,则首先进入自旋状态,这个时候mutex spin wait++

Mutex Rounds

进入spin wait的线程,通过不断循环来等待获取mutex,循环的次数称为round(理解可以参照图 4)

Mutex Os wait

线程进入系统等待的次数,现在在获取mutex过程中,如果没有在第一时间内获取,则进入自旋,自旋达到设定的时间需求后依旧没能获取到mutex,这个时候线程进入系统等待。Os wait的值增加。

Rw-sharerw-excl:对应参数含义类似。

Spin rounds per waits mutex

每次mutex自旋等待中round的次数,值=mutex rounds/mutex spin wait

Spin rounds per waits rw-share(读锁)

每次rw-sahre自旋等待中round的次数,值=rw-share rounds/rw-share spin wait

Spin rounds per waits rw-excl(写锁)

每次rw-excl自旋等待中round的次数,值=rw-excl rounds/rw-excl spin wait

注:统计信息部分给出的是自mysqld服务启动以来的累计值,进入spin wait的线程不一定会进入os wait状态,但是进入os wait状态的线程必然经历过spin wait状态。如果出现spin wait的次数与os wait次数相等或者相差不大的情况,证明绝大多数线程在spin wait状态结束后最终进入os wait状态,这种情况与innodb mutex设计初衷相违背。如果出现这样的情况,第一证明系统处于高负载,资源竞争激烈;第二,可以通过改变参数innodb_spin_wait_delayinnodb_sync_spin_loops的值来减少资源浪费。Innodb_sync_spin_loops控制的是进入spin wait状态之后循环次数,innodb_spin_wait_delay是为了延长循环时间的。统计信息给的并非实时状态,而是历史状态,如果需要观察实时状态,则必须记录两个时间点差值,这样才能充分反映当前系统状态非高并发情况下是看不到第一部分的等待信息的,所以当你看到有第一部分信息出现的时候证明你需要关注系统状态。


Innodb中的锁包括latchlock,在semphores部分指的都是latchinnodb自行实现了mutex,同时为了获取更好的并发性能,在mutex的基础上实现了读写锁。Mutex和写锁是完全互斥的,读锁在同一时间允许多个线程共同访问。


在数据库中latch lock都被称为锁,在innodblock针对的是事务,latch针对的是线程,latch又可以分为mutexrw_locklatch的目的是保证并发的线程操作临界资源的正确性。Mutex的数据结构及说明如表2所示。

表 2 mutex 数据结构

变量名称

说明

lock_word

用来进行TAS操作

os_fast_mutex

 

waiters

是否有线程等待

list

链表

thread_id

持有mutex线程ID

file_name

mutex被锁住时的文件名

line

mutex被锁住时的行

level

latch所在的level,例如Debug模式

cfile

创建mutex对象时的文件名

cline

创建mutex时所在行

magic_n

debug模式下检测内存是否越界

 innodb为了提高读的性能,自定义了read write lock,也就是读写锁。其设计原则是:

Ø 同一时刻允许多个线程同时读取内存中的变量

Ø 同一时刻只允许一个线程更改内存中的变量

Ø 同一时刻当有线程在读取变量时不允许任何线程写存在

Ø 同一时刻当有线程在更改变量时不允许任何线程读,也不允许出自己以外的线程写(线程内可以递归占有锁)。

Ø 当有rw_lock处于线程读模式下是有线程写等待,这时候如果再有其他线程读请求锁的时,这个读请求将处于等待前面写完成。

从上面5点我们可以看出,rw_lock在被占用是会处于读状态和写状态,我们称之为S-latch(读共享)X-latch(写独占),《MySQL技术内幕:innodb引擎》对S-latchX_latch的描述如表 3所示。

表 3 锁类型兼容矩阵

x-latch s-latch
x-latch conflict conflict
s-latch conflict compatible


操作系统本身有提供latch,但是Innodb基于自旋锁重写了mutex,其主要原因是:因为传统的mutex在获取不到的时候,线程会直接进入wait队列,等待被唤醒,cpu在线程进入wait队列的时候切换上下文环境。常见操作系统线程上下文切换耗时大约25um,再加上lockunlock的时间开销。Innodbspin的时间设置为20um,当前奔腾平台一个操作持有mutex或者rw-lock的时间一般在1-20um,自旋20um在绝大多数情况下能获取到mutex,相比进入os waitspin的时间20um不算多。表 4给出了mutex的获取流程。通过TAS(test_and_set)指令检测mutex是否被释放。表中参数sync_spin_rounds对应innodb参数innodb_sync_spin_rounds,主要是为了避免无限自旋而设计的。在线程自旋无法获取mutex的时候,线程进入os wait队列,并将mutex数据结构中waiters值设置为1,为的是在持有mutex线程释放mutex时,能去os wait队列中唤醒该线程。 

此外,Innodb基于自己的mutex实现基础上实现了rw-lock,读写锁包括x-latchs-latchS-latch表示对临界资源上共享锁,允许多个线程访问资源,x-latch表示对临界资源上排他锁,只运行一个线程访问该资源。信号量获取流程如图 21所示。

表 4 获取mutex流程

if (test_and_set(mutex->lock_word)==0){
get mutex;
return;}
loop:
while(mutex->lock_word!=0&& ilock_word)==0){
get mutex; return;}
reserve a cell from wait array;
mutex->waiters=1;
for(i=0;i<4;i++){
if(test_and_set(mutex->lock_word)==0){
get mutex; free cell; return;}
wait event;
goto loop;}


  结合源码分析show engine innodb status输出内容(一)_第5张图片

图5 mutex获取流程


Innodb提供了两个参数可以对mutexspin进行调整,参数如图 6所示。Innodb_sync_spin_loops设定的是线程在获取mutex过程中,如果没在第一时间获取到mutex,线程首先进入自旋,自旋的loops达到该值之后仍未获取到mutex,则释放占用的资源,进入array cell,状态os wait,等待被唤醒。Innodb_sync_wait_delay设定了在spin的过程中,依赖区间[0,innodb_spin_wait_delay]生成随机数,产生延迟。对于稳定运行系统,可以根据系统获取mutex平均等待时间,合理设置这两个参数值,例如:semphores部分内容显示,spin_waitos wait的值基本相近,那就说明该系统spin效果不明显,innodb之所以采用spin的原因就是为了减少线程进入os wait以及被唤醒的开销,如果系统结果显示,spin wait之后最终还是要去到os wait,那就证明spin设置较大值是一种浪费。

结合源码分析show engine innodb status输出内容(一)_第6张图片


图6 自旋相关参数



你可能感兴趣的:(SQL/MySQL)