一、问题来源
问1 ,为啥5.7的这个值为0
问2:为啥8.0的这个值为0
二、关于RW LOCK
其实Innodb中并发控制结构分为rw lock,mutex(TTASEventMutex)和event(带有条件变量),主要作为Innodb的并发访问控制,这部分非常复杂,还需要学习,我们通常在show engine innodb中看到的mutex或者rw lock就是他们。比如:
- rw lock其中一个作用就是作为buffer page(BPageLock)的保护,通常用于page的并发访问,比如修改和读取buffer page需要上不同的锁。
- mutex其中一个作用就是作为buffer block(BPageMutex 类型为TTASEventMutex)的保护,比如需要修改/访问 block io fix部分则需要加这个锁。
rw lock和mutex(TTASEventMutex)都会接受参数的控制:
| innodb_spin_wait_delay | 6 |
| innodb_sync_spin_loops | 30 |
逻辑大概如下:
rw_lock_s_lock_spin 函数
如果第一次获取失败
开启spin获取,则spins+1
一层循环开启:
二层循环,循环次数为srv_n_spin_wait_rounds(sync_spin_loops)
等待每次等待时长srv_spin_wait_delay(大概为空循环的次数默认为6(srv_spin_wait_delay)*50(srv_spin_wait_pause_multiplier) 300次)
如果获取了lock的使用权则结束二层循环
否则需要等满srv_n_spin_wait_rounds
如果等待满srv_n_spin_wait_rounds,则归还
CPU给操作系统,等待下次CPU的调度
再次调度的时候,rounds+1
再次尝试获取锁资源如果获取成功,则退出一层循环
如果获取锁资源如果获取失败,则OS waits+1,且进入muext等待
->sync_array_wait_event(sync_arr, cell); //等待唤醒,唤醒后继续判断,存在多个线程同时竞争的情况
唤醒后再次循环二层循环,尝试获取锁资源
一层继续循环
sync_spin_loops的内部定义为srv_n_spin_wait_rounds
如果我们将经历srv_n_spin_wait_rounds*srv_spin_wait_delay*srv_spin_wait_pause_multiplier:
默认为30*6*50=9000(代码中)
看做一轮等待。那么从逻辑来看定义如下:
- spins代表是spin获取的次数,如果第一次就获取成功了则这里是为0的
- rounds代表是经历多少轮数才获取到lock的使用权。当然最后一轮可能提前获取到使用权。
- OS waits代表是经历了多少轮的等待才获取了lock的使用权。
那么有如下结论
- spins越少越好,代表第一次获取成功了
- OS waits大量少于rounds越好,代表偶虽然第一次获取失败,但是第spin一次就获取成功了
但是myql 5.7中spins中关于x lock和s lock 对于这个计数是没有做的,8.0.23有计数。所以5.7这里可能是0。
并且注意这里是RW-LATCH INFO因此mutex是不包含的。
三、关于8.0 master线程的简析
master线程主要是每秒醒来干活,包含:
- changer buffer 合并
- flush redo
- 唤醒purge线程干活
- checkpoint(8.0剥离成了单独的checkpoint线程)
但是其中包含active和idle的方式,其判断主要是arcive计数器和上次是否相同,不相同则是active方式。那么我看了一下如下情况会进入active方式:
- 事务提交一次,增加一次active计数。
- update/delete/insert 总数超过了2的32次方就增加一次active计数。
- 出现rollback,每行记录都会触发增加一次active计数。
- innodb关闭表,增加一次active计数。
我们实际上可以从这个信息大概可以看出数据库数据的繁忙程度。但是在8.0中全局变量srv_log_writes_and_flush并没有做任何修改,因此初始化为0它一值都是0。5.6 5.7是有修改的,但是值没有什么意义就是 srv_active+ srv_idle,代表master线程触发的写盘操作。
四、page分配内存
(buf_page_get_gen->buf_buddy_alloc_low)
- 首先从freelist中获取块,不能获取则转下一个逻辑
- 从LRU中获取,受到参数innodb_lru_scan_depth的影响表示每次检查的块的数量,其中有一些条件不能获取
- 块本生是脏块
- 块本生被io fix了
如果获取成功将从hash table中去掉,并且从lru放到free list中如果循环完整个LRU链表都不能获取则转下一个逻辑
- 如果扫描了整个LRU都不能获取,那么说明块可能脏块比较多了进行单块刷盘,这个时候是直接fsync了(buf_flush_single_page_from_LRU)
其他:
8.0.23
mutex_enter(&buf_pool->flush_state_mutex); //为TTASEventMutex
os_event_reset(buf_pool->no_flush[flush_type]); //为os_event 不受spin参数影响,例子https://www.jianshu.com/p/24b212286a1b
那么研究方向应该是TTASEventMutex\rw lock\os_event 各自的实现