MySQL · 源码阅读 · MySQL8.0 innodb锁相关
背景
innodb里面的mutex常见的实现是PolicyMutex<TTASEventMutex,信号量底层使用是os_event_t
代码分析
os_event_t
struct os_event {
void set();
int64_t reset();
void wait_low();
void broadcast();
private:
bool m_set;
int64_t signal_count;
os_cond_t cond_var;
EventMutex mutex;
os_cond_t cond_var;
}
- set函数 如果m_set是false,调用broadcast函数
- reset函数 设置m_set = false, 返回signal_count
- wait_low函数 m_set == false 并且signal_count == reset_sig_count 才进入wait, 保证不会死锁
- broadcast函数 m_set设置true, signal_count计数器+1,唤醒其他等待者
wait操作: 先调用reset函数,然后用返回的reset_sig_count作为参数,调用wait_low函数
signal操作: 调用set函数
PolicyMutex
先看PolicyMutex的主要结构
template <typename MutexImpl>
struct PolicyMutex {
private:
MutexImpl m_impl;
public:
void enter();
void exit();
void init();
}
init函数负责初始化,加锁是enter函数,解锁是exit函数,具体的实现都是通过m_impl来实现
TTASEventMutex
在看TTASEventMutex的主要结构
struct TTASEventMutex {
public:
void init();
void exit();
void enter();
bool try_lock();
private:
std::atomic_bool m_lock_word;
std::atomic_bool m_waiters;
os_event_t m_event;
MutexPolicy m_policy;
}
- m_lock_word 判断是否加锁成功
- m_waiters 当前锁有多少个等待者
- m_event 线程等待内部实现使用
- m_policy 统计使用
exit函数
- m_lock_word设置false
- 如果有waiter就调用signal函数唤醒其他等待者
enter函数功能就是加锁,成功返回,否则就一直等待,具体内部实现:
- 第一步会优先判断m_lock_word原子变量cas操作能否成功,成功就说明加锁成功了,否则说明有其他人持有锁
调用spin_and_try_lock函数,内部实现死循环执行下面步骤:
2.1. 先尝试max_spins次对m_lock_word变量执行cas操作,如果成功就返回
2.2. 没有成功就先尝试执行yield函数,放弃cpu占用
2.3. 调用wait函数,内部实现:2.3.1. 先调用sync_array_get_and_reserve_cell从wait_array获取一个cell,m_waiters设置为true
2.3.2. 尝试4次m_lock_word原子变量cas操作,如果成功就返回
2.3.3. 调用sync_array_wait_event等待信号量唤醒
sync_array_t
struct sync_array_t {
ulint n_reserved; //正在使用的cell个数
ulint n_cells; //数组分配大小
sync_cell_t *cells; //数组
ulint next_free_slot; //除了free list以外,下一个可以用的cell
ulint first_free_slot; //free list链表头, 复用cell里面的line字段作为next指针
}
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
struct GenericPolicy {
latch_id_t m_id;
/** Number of spins trying to acquire the latch. */
uint32_t m_spins;
/** Number of waits trying to acquire the latch */
uint32_t m_waits;
/** Number of times it was called */
uint32_t m_calls;
}
每次加锁都会更新里面相关字段,因此通过GenericPolicy可以看到锁竞争激烈程度
当前内容版权归 阿里云RDS-数据库内核组 或其关联方所有,如需对内容或内容相关联开源项目进行关注与资助,请访问 阿里云RDS-数据库内核组 .