首先innodb 线程分为用户态线程和后台线程,用户态线程主要是用户操作的线程,可以通过show process list 查看,后台线程不能够查看。但是show innodb status可以查看到4个线程
I/O thread 0 state: waiting for i/o request (insert buffer thread)
I/O thread 1 state: waiting for i/o request (log thread)
I/O thread 2 state: waiting for i/o request (read thread)
I/O thread 3 state: waiting for i/o request (write thread)
IO线程是可以通过innodb_file_io_threads 参数配置,默认是4;
其次innodb 刷数据也分两种,同步IO及一步IO就是show innodb status 中出现的aio;
在innodb中后台线程刷数据时异步IO,后台线程受主线程master_thread调度;master_thread 优先级最高,其内部由几个循环构成
主 loop ,background loop,flush loop;suspend loop;master thread 运行会在这几个线程之间切换;
首先说 主loop,主loop 分为两类主要操作,每1s和每10s,但是innodb 并不是严格按照这个时段来执行的,因为中间出了切换还有sleep等操作会消耗时间;
在innodb中维护了4个队列,每个队列长度256,其中存放的是slot,当队列满时,必然会唤醒相关的IO线程执行相应的iO操作,主线程也会周期性的唤醒IO线程;然后检查自己的队列,看是否需要做刷盘操作; 这4个线程是异步IO;
每1s唤醒IO线程执行操作
1、日志缓冲刷新到磁盘,每1s 执行,同时日志缓冲会受到innodb_flush_log_at_trx_commit 参数配置的影响,但是即使事务没有提交,也会刷,在刷日志时,innodb会对时间做一个判断,首先先看有没有在队列里超过2s的 如果有 就先刷他们,如果没有超过2s的 也先刷低地址的,然后会遍历队列 把几个在同一个extent的page一起刷下去;
2、合并插入缓冲(可能);innodb会判断当前IO情况,如果当前1s内少于5次IO,它就认为当前IO负载较小,执行合并插入;
3、刷新至多100个脏数据页(可能);如果当前脏页操过了90%,就是innodb_max_dirty_pages_pct 默认值,那么它认为需要刷新脏页
4、切换到background loop;
每10s唤醒IO线程执行的操作
1、将日志缓冲刷新到磁盘;
2、合并5个插入缓冲;不管当前IO操作能力如何,总是强制执行;
3、刷新100个脏页;innodb会判断过去10s内IO操作是否小于200,如果是,那么它认为当前IO比较轻,执行刷新100个脏页;如果当前脏页比例大于70%,那么也会刷新100个脏页,否则会刷新10个;
4、删除无用的undo页;因为innodb采用了MVCC机制,有些为了一致性读而被标记为删除状态数据页需要被清理,比如有时一个查询可能需要读取前一个版本的数据信息,它被放在undo页中;但是每次最多删除20个undo页;
5、产生一个检查点;innodb 刷数据时会产生一个检查点;但是没10s也会强制产生一个检查点;为了保证性能,并不是完全刷完脏数据,而是志将最老日志序列号的页刷盘;
background loop ,当前没有或用用户或者数据库关闭时就会切换到这个循环;
它会执行以下操作;
1、删除无用的 undo页;
2、合并20个插入缓冲;
3、不断刷新100个数据页,知道符合条件(可能跳到 flush loop 中完成);
suspend_loop
如果 flush loop中也没有什么事情可以做了,innodb 会切换到 suspeng_loop,将master thread挂起,等待事件的发生;如果启用了innodb 存储引擎却没有innodb表,通用也会将 master thread 挂起;
主线程如下
void master_thread(){
2 goto loop;
3 loop:
4 for(int i = 0; i<10; i++){
5 thread_sleep(1) // sleep 1 second
6 do log buffer flush to disk
7 if ( last_one_second_ios < 5 )
8 do merge at most 5 insert buffer
9 if ( buf_get_modified_ratio_pct > innodb_max_dirty_pages_pct )
10 do buffer pool flush 100 dirty page
11 if ( no user activity )
12 goto backgroud loop
13 }
14 if ( last_ten_second_ios < 200 )
15 do buffer pool flush 100 dirty page
16 do merge at most 5 insert buffer
17 do log buffer flush to disk
18 do full purge
19 if ( buf_get_modified_ratio_pct > 70% )
20 do buffer pool flush 100 dirty page
21 else
22 buffer pool flush 10 dirty page
23 do fuzzy checkpoint
24 goto loop
25 background loop:
26 do full purge
27 do merge 20 insert buffer
28 if not idle:
29 goto loop:
30 else:
31 goto flush loop
32 flush loop:
33 do buffer pool flush 100 dirty page
34 if ( buf_get_modified_ratio_pct> innodb_max_dirty_pages_pct )
35 goto flush loop
36 goto suspend loop
37 suspend loop:
38 suspend_thread()
39 waiting event
40 goto loop;
后续会对innodb plugin 说明
上面说到 的主线程部分大多数是异步IO ,但是用户态线程大多数是同步IO;
当你发起一个事务时,innodb会判断log buffer 是否有足够的空间;如果有那么就启动一个事务,如果没有,那么就会唤醒log thread 刷日志,但是并不刷完;同时也会对脏页做类似处理;
参考 innodb 存储引擎内幕一书及自己一些学习;