MySQL · 源码阅读 · MySQL8.0 innodb锁相关

背景

innodb里面的mutex常见的实现是PolicyMutex<TTASEventMutex,信号量底层使用是os_event_t

代码分析

os_event_t


1. struct os_event {
2. void set();
3. int64_t reset();
4. void wait_low();
5. void broadcast();
6. private:
7. bool m_set;
8. int64_t signal_count;
9. os_cond_t cond_var;
10. EventMutex mutex;
11. os_cond_t cond_var;
12. }

  1. set函数 如果m_set是false,调用broadcast函数
  2. reset函数 设置m_set = false, 返回signal_count
  3. wait_low函数 m_set == false 并且signal_count == reset_sig_count 才进入wait, 保证不会死锁
  4. broadcast函数 m_set设置true, signal_count计数器+1,唤醒其他等待者

wait操作: 先调用reset函数,然后用返回的reset_sig_count作为参数,调用wait_low函数
signal操作: 调用set函数

PolicyMutex

先看PolicyMutex的主要结构


1. template <typename MutexImpl>
2. struct PolicyMutex {
3. private:
4. MutexImpl m_impl;
5. public:
6. void enter();
7. void exit();
8. void init();
9. }

init函数负责初始化,加锁是enter函数,解锁是exit函数,具体的实现都是通过m_impl来实现

TTASEventMutex

在看TTASEventMutex的主要结构


1. struct TTASEventMutex {
2. public:
3. void init();
4. void exit();
5. void enter();
6. bool try_lock();
7. private:
8. std::atomic_bool m_lock_word;
9. std::atomic_bool m_waiters;
10. os_event_t m_event;
11. MutexPolicy m_policy;
12. }

  1. m_lock_word 判断是否加锁成功
  2. m_waiters 当前锁有多少个等待者
  3. m_event 线程等待内部实现使用
  4. m_policy 统计使用

exit函数

  1. m_lock_word设置false
  2. 如果有waiter就调用signal函数唤醒其他等待者

enter函数功能就是加锁,成功返回,否则就一直等待,具体内部实现:

  1. 第一步会优先判断m_lock_word原子变量cas操作能否成功,成功就说明加锁成功了,否则说明有其他人持有锁
  2. 调用spin_and_try_lock函数,内部实现死循环执行下面步骤:

2.1. 先尝试max_spins次对m_lock_word变量执行cas操作,如果成功就返回
2.2. 没有成功就先尝试执行yield函数,放弃cpu占用
2.3. 调用wait函数,内部实现:

```

  1. 2.3.1. 先调用sync_array_get_and_reserve_cell从wait_array获取一个cell,m_waiters设置为true
  2. 2.3.2. 尝试4次m_lock_word原子变量cas操作,如果成功就返回
  3. 2.3.3. 调用sync_array_wait_event等待信号量唤醒 ```

sync_array_t


1. struct sync_array_t {
2. ulint n_reserved;        //正在使用的cell个数
3. ulint n_cells;            //数组分配大小
4. sync_cell_t *cells;             //数组
5. ulint next_free_slot;           //除了free list以外,下一个可以用的cell
6. ulint first_free_slot;          //free list链表头, 复用cell里面的line字段作为next指针
7. }

sync_array_init 初始化sync_wait_array 二维数组,第一维大小1,第二维大小100k。
sync_array_reserve_cell 从sync_wait_array 里面获取一个free cell,极限情况全部cell被占用就返回nullptr
sync_array_free_cell 放回cell到free list
sync_array_wait_event 等待信号量唤醒

GenericPolicy


1. struct GenericPolicy {
2. latch_id_t m_id;
3. /** Number of spins trying to acquire the latch. */
4. uint32_t m_spins;

6. /** Number of waits trying to acquire the latch */
7. uint32_t m_waits;

9. /** Number of times it was called */
10. uint32_t m_calls;
11. }

每次加锁都会更新里面相关字段,因此通过GenericPolicy可以看到锁竞争激烈程度

原文:http://mysql.taobao.org/monthly/2021/02/08/