A.INSERT
插入操作在函数btr_cur_optimistic_insert->btr_cur_ins_lock_and_undo->lock_rec_insert_check_and_lock这里进行锁的判断,我们简单的看看这个函数的流程:
1.首先先看看欲插入记录之后的数据上有没有锁,
next_rec = page_rec_get_next_const(rec);
next_rec_heap_no = page_rec_get_heap_no(next_rec);
lock = lock_rec_get_first(block, next_rec_heap_no);
如果lock为空的话,对于非聚集索引,还需要更新page上的最大事务ID。
实际上这里是比较松散的检查,大并发插入的时候,可以大大的降低创建锁开销。
那么其他事务如何发现这些新插入的记录呢(重复插入同一条记录显然应该被阻塞),这里会有个判断,其他事务去看看
新插入记录的事务是否还是活跃的,如果还是活跃的,那么就为这个事务主动增加一个锁记录(所谓的隐式锁就是么有锁。。。。),这个判断是在检查是否存在冲突键的时候进行的(row_ins_duplicate_error_in_clust->row_ins_set_shared_rec_lock->lock_clust_rec_read_check_and_lock->lock_rec_convert_impl_to_expl
row_ins_set_shared_rec_lock的目的是为了向记录上加一个LOCK_REC_NOT_GAP的LOCK_S锁,也就是非GAP的记录S锁,如果发现记录上有X锁(隐式锁转换为LOCK_REC | LOCK_X | LOCK_REC_NOT_GAP),显然是需要等待的(返回DB_LOCK_WAIT)
这里设置inherit为FALSE,然后返回DB_SUCCESS;
至于inherit的作用,稍后再议!
2.如果lock不为空,这意味着插入记录的下一个记录上存在锁,设置inherit为TRUE.
检查下一个记录上的锁是否和LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION相互冲突
if (lock_rec_other_has_conflicting(
LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION,
block, next_rec_heap_no, trx)) {
/* Note that we may get DB_SUCCESS also here! */
err = lock_rec_enqueue_waiting(LOCK_X | LOCK_GAP
| LOCK_INSERT_INTENTION,
block, next_rec_heap_no,
index, the);
如果有别的事务在下一个记录上存在显式的锁请求,并且和锁模式( LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION) 冲突,那么
这时候当前事务就需要等待。
如果别的事务持有一个GAP类型的锁以等待插入,我们认为这个锁和当前插入不冲突。
如何判定锁之间是否冲突,在上一篇博客(http://mysqllover.com/?p=425)已经介绍过,不再赘述.
当检查到存在冲突的事务,我们就将一个锁模式为LOCK_X | LOCK_GAP|LOCK_X | LOCK_GAP 加入到请求队列中(调用函数lock_rec_enqueue_waiting),这里也会负责去检查死锁。
注意在加入等待队列的时候可能会返回DB_SUCCESS,例如死锁发生,但选择另外一个事务为牺牲者。
我们上面提到变量inherit,在存在下一个记录锁时会设置为TRUE,在上层函数btr_cur_optimistic_insert,会据此进行判断:
if (!(flags & BTR_NO_LOCKING_FLAG) && inherit) {
lock_update_insert(block, *rec);
}
注意当我们执行到这部分逻辑时err为DB_SUCCESS,表示锁检查已经通过了。
BTR_NO_LOCKING_FLAG表示不做记录锁检查
对于optimistic_insert, flags值为0
对于pessimistic_insert,flags值为BTR_NO_UNDO_LOG_FLAG | BTR_NO_LOCKING_FLAG | BTR_KEEP_SYS_FLAG