美文网首页
Innodb-Insert

Innodb-Insert

作者: 多血 | 来源:发表于2020-11-17 00:21 被阅读0次
lock_rec_lock(bool, unsigned long, buf_block_t const*, unsigned long, dict_index_t*, que_thr_t*) lock0lock.cc:2063
lock_clust_rec_read_check_and_lock(unsigned long, buf_block_t const*, unsigned char const*, dict_index_t*, unsigned long const*, lock_mode, unsigned long, que_thr_t*) lock0lock.cc:6422
row_ins_set_shared_rec_lock(unsigned long, buf_block_t const*, unsigned char const*, dict_index_t*, unsigned long const*, que_thr_t*) row0ins.cc:1491
row_ins_duplicate_error_in_clust(unsigned long, btr_cur_t*, dtuple_t const*, que_thr_t*, mtr_t*) row0ins.cc:2335
row_ins_clust_index_entry_low(unsigned long, unsigned long, dict_index_t*, unsigned long, dtuple_t*, unsigned long, que_thr_t*, bool) row0ins.cc:2562
row_ins_clust_index_entry(dict_index_t*, dtuple_t*, que_thr_t*, unsigned long, bool) row0ins.cc:3299
row_ins_index_entry(dict_index_t*, dtuple_t*, que_thr_t*) row0ins.cc:3437
row_ins_index_entry_step(ins_node_t*, que_thr_t*) row0ins.cc:3587
row_ins(ins_node_t*, que_thr_t*) row0ins.cc:3725
row_ins_step(que_thr_t*) row0ins.cc:3861
row_insert_for_mysql_using_ins_graph(unsigned char const*, row_prebuilt_t*) row0mysql.cc:1746
row_insert_for_mysql(unsigned char const*, row_prebuilt_t*) row0mysql.cc:1866
ha_innobase::write_row(unsigned char*) ha_innodb.cc:7612
handler::ha_write_row(unsigned char*) handler.cc:8093
write_record(THD*, TABLE*, COPY_INFO*, COPY_INFO*) sql_insert.cc:1889
Sql_cmd_insert::mysql_insert(THD*, TABLE_LIST*) sql_insert.cc:776
Sql_cmd_insert::execute(THD*) sql_insert.cc:3134
mysql_execute_command(THD*, bool) sql_parse.cc:3606
mysql_parse(THD*, Parser_state*) sql_parse.cc:5584
dispatch_command(THD*, COM_DATA const*, enum_server_command) sql_parse.cc:1491
do_command(THD*) sql_parse.cc:1032
::handle_connection(void *) connection_handler_per_thread.cc:313
::pfs_spawn_thread(void *) pfs.cc:2197
_pthread_start 0x00007fff72bdb109
thread_start 0x00007fff72bd6b8b
/***************************************************************//**
Tries to insert an entry into a clustered index, ignoring foreign key
constraints. If a record with the same unique key is found, the other
record is necessarily marked deleted by a committed transaction, or a
unique key violation error occurs. The delete marked record is then
updated to an existing record, and we must write an undo log record on
the delete marked record.
@retval DB_SUCCESS on success
@retval DB_LOCK_WAIT on lock wait when !(flags & BTR_NO_LOCKING_FLAG)
@retval DB_FAIL if retry with BTR_MODIFY_TREE is needed
@return error code */
dberr_t
row_ins_clust_index_entry_low(
/*==========================*/
    ulint       flags,  /*!< in: undo logging and locking flags */
    ulint       mode,   /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE,
                depending on whether we wish optimistic or
                pessimistic descent down the index tree */
    dict_index_t*   index,  /*!< in: clustered index */
    ulint       n_uniq, /*!< in: 0 or index->n_uniq */
    dtuple_t*   entry,  /*!< in/out: index entry to insert */
    ulint       n_ext,  /*!< in: number of externally stored columns */
    que_thr_t*  thr,    /*!< in: query thread */
    bool        dup_chk_only)
                /*!< in: if true, just do duplicate check
                and return. don't execute actual insert. */
{
    btr_pcur_t  pcur;
    btr_cur_t*  cursor;
    dberr_t     err     = DB_SUCCESS;
    big_rec_t*  big_rec     = NULL;
    mtr_t       mtr;
    mem_heap_t* offsets_heap    = NULL;
    ulint           offsets_[REC_OFFS_NORMAL_SIZE];
    ulint*          offsets         = offsets_;
    rec_offs_init(offsets_);

    /* Note that we use PAGE_CUR_LE as the search mode, because then
    the function will return in both low_match and up_match of the
    cursor sensible values */
    //调用btr_cur_search_to_nth_level 查询索引树,将cursor移动到记录相应的位置
    btr_pcur_open(index, entry, PAGE_CUR_LE, mode, &pcur, &mtr); 
    cursor = btr_pcur_get_btr_cur(&pcur);
    cursor->thr = thr;

    /* Allowing duplicates in clustered index is currently enabled
    only for intrinsic table and caller understand the limited
    operation that can be done in this case. */
    if (!index->allow_duplicates // 是否允许重复
        && n_uniq //唯一索引的字段数量
        && (cursor->up_match >= n_uniq || cursor->low_match >= n_uniq)) { //是否存在唯一索引冲突,up_match与low_match可以先简单理解为记录前后两条记录与它相等的字段数量
           //满足了这几个条件需要进入row_ins_duplicate_error_in_clust
        if (flags
            == (BTR_CREATE_FLAG | BTR_NO_LOCKING_FLAG
            | BTR_NO_UNDO_LOG_FLAG | BTR_KEEP_SYS_FLAG)) {
        } else {
            /* Note that the following may return also
            DB_LOCK_WAIT */

            err = row_ins_duplicate_error_in_clust(
                flags, cursor, entry, thr, &mtr);
        }
    }
}
聚簇索引
进入row_ins_duplicate_error_in_clust后分几种场景:
1)唯一键冲突,冲突的记录是未提交事务insert的。
2)唯一键冲突,冲突的记录是未提交的事务delete。
3)唯一键冲突,冲突的记录是已提交的事务。
4)唯一键冲突,冲突的记录是已提交,但是delete-mark的未被purge。
5)唯一键冲突,冲突的记录是自身事务delete-mark的记录。
1)
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
----------------判断冲突的记录事务是活跃的,调用下面函数给记录加锁,冲突记录的事务持有
----------------lock_rec_convert_impl_to_expl_for_trx
--------------------!trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY) && !lock_rec_has_expl(LOCK_X |LOCK_REC_NOT_GAP, block, heap_no, trx))
--------------------判断事务状态不为TRX_STATE_COMMITTED_IN_MEMORY并且没有显式锁
--------------------lock_rec_add_to_queue
----------给冲突的记录加锁,本事务持有
-----------lock_rec_lock
---------------lock_rec_lock_fast or lock_rec_lock_slow
2)
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
----------------判断冲突的记录事务是活跃的,调用下面函数给记录加锁,冲突记录的事务持有
----------------lock_rec_convert_impl_to_expl_for_trx
--------------------!trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY) && !lock_rec_has_expl(LOCK_X |LOCK_REC_NOT_GAP, block, heap_no, trx))
--------------------因为delete有锁,不需要加锁
----------给冲突的记录加锁,本事务持有
-----------lock_rec_lock
---------------lock_rec_lock_fast or lock_rec_lock_slow
3)
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
----------------判断冲突的记录事务是提交的,无需记录加锁
----------给自身记录加锁
-----------lock_rec_lock
---------------lock_rec_lock_fast or lock_rec_lock_slow
----判断是否是DB_DUPLICATE_KEY
----row_ins_dupl_error_with_rec
报错duplicate key但是仍然持有锁
死锁案例:https://mp.weixin.qq.com/s/RleocRPvK67aTJqbDXeICw,同样的场景可复现于聚簇索引。
4)
5)
row_ins_clust_index_entry
----row_ins_clust_index_entry_low
--------row_ins_set_shared_rec_lock
------------lock_clust_rec_read_check_and_lock
----------------lock_rec_convert_impl_to_expl
--------------------lock_rec_convert_impl_to_expl_for_trx
--------------------因为delete有锁,所以不需要加锁
---------row_ins_duplicate_error_in_clust
------------row_ins_set_shared_rec_lock
----------------lock_clust_rec_read_check_and_lock
--------------------lock_rec_lock
------------------------lock_rec_lock_slow
----------------------------lock_rec_has_expl
----------------------------因为事务自身已经持有了锁,不需要加锁
--------row_ins_clust_index_entry_by_modify
------------btr_cur_optimistic_update
----------------btr_cur_update_in_place
--------------------btr_cur_upd_lock_and_undo
------------------------lock_clust_rec_modify_check_and_lock
案例:https://zhuanlan.zhihu.com/p/52098868

相关文章

网友评论

      本文标题:Innodb-Insert

      本文链接:https://www.haomeiwen.com/subject/tcptiktx.html