MySQL 引擎特性 InnoDB redo log漫游(4)

在修改或读一个数据文件中的数据时,一般是通过mtr来控制对对应page或者索引树的加锁,在5.7中,有以下几种锁类型(mtr_memo_type_t):

变量名描述
MTR_MEMO_PAGE_S_FIX   用于PAGE上的S锁  
MTR_MEMO_PAGE_X_FIX   用于PAGE上的X锁  
MTR_MEMO_PAGE_SX_FIX   用于PAGE上的SX锁,以上锁通过mtr_memo_push 保存到mtr中  
MTR_MEMO_BUF_FIX   PAGE上未加读写锁,仅做buf fix  
MTR_MEMO_S_LOCK   S锁,通常用于索引锁  
MTR_MEMO_X_LOCK   X锁,通常用于索引锁  
MTR_MEMO_SX_LOCK   SX锁,通常用于索引锁,以上3个锁,通过mtr_s/x/sx_lock加锁,通过mtr_memo_release释放锁  
mtr log生成

InnoDB的redo log都是通过mtr产生的,先写到mtr的cache中,然后再提交到公共buffer中,本小节以INSERT一条记录对page产生的修改为例,阐述一个mtr的典型生命周期。

入口函数:row_ins_clust_index_entry_low

开启mtr

执行如下代码块

mtr_start(&mtr); mtr.set_named_space(index->space);

mtr_start主要包括:

初始化mtr的各个状态变量

默认模式为MTR_LOG_ALL,表示记录所有的数据变更

mtr状态设置为ACTIVE状态(MTR_STATE_ACTIVE)

为锁管理对象和日志管理对象初始化内存(mtr_buf_t),初始化对象链表

mtr.set_named_space 是5.7新增的逻辑,将当前修改的表空间对象fil_space_t保存下来:如果是系统表空间,则赋值给m_impl.m_sys_space, 否则赋值给m_impl.m_user_space。

Tips: 在5.7里针对临时表做了优化,直接关闭redo记录:
mtr.set_log_mode(MTR_LOG_NO_REDO)

定位记录插入的位置

主要入口函数: btr_cur_search_to_nth_level

不管插入还是更新操作,都是先以乐观方式进行,因此先加索引S锁
mtr_s_lock(dict_index_get_lock(index),&mtr),对应mtr_t::s_lock函数
如果以悲观方式插入记录,意味着可能产生索引分裂,在5.7之前会加索引X锁,而5.7版本则会加SX锁(但某些情况下也会退化成X锁)
加X锁: mtr_x_lock(dict_index_get_lock(index), mtr),对应mtr_t::x_lock函数
加SX锁:mtr_sx_lock(dict_index_get_lock(index),mtr),对应mtr_t::sx_lock函数

对应到内部实现,实际上就是加上对应的锁对象,然后将该锁的指针和类型构建的mtr_memo_slot_t对象插入到mtr.m_impl.m_memo中。

当找到预插入page对应的block,还需要加block锁,并把对应的锁类型加入到mtr:mtr_memo_push(mtr, block, fix_type)

如果对page加的是MTR_MEMO_PAGE_X_FIX或者MTR_MEMO_PAGE_SX_FIX锁,并且当前block是clean的,则将m_impl.m_made_dirty设置成true,表示即将修改一个干净的page。

如果加锁类型为MTR_MEMO_BUF_FIX,实际上是不加锁对象的,但需要判断临时表的场景,临时表page的修改不加latch,但需要将m_impl.m_made_dirty设置为true(根据block的成员m_impl.m_made_dirty来判断),这也是5.7对InnoDB临时表场景的一种优化。

同样的,根据锁类型和锁对象构建mtr_memo_slot_t加入到m_impl.m_memo中。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/8cd87edb4eabf47a36398be5984e79ac.html