本文對一個(gè)案例的有趣的地方進(jìn)行分析狰挡,然后最后再綜述下replace into的流程裙椭。
基礎(chǔ)環(huán)境
進(jìn)行一個(gè)Replace操作驴娃,唯一索引沖突削饵,SQL執(zhí)行完后事務(wù)持有的鎖岩瘦。
Insert常規(guī)的加鎖參看這個(gè)文章:http://www.reibang.com/p/d945753a4f15
這里面有幾個(gè)有趣的地方:
- 主鍵上為什么會(huì)在4、5上加GAP鎖窿撬。
- 為什么唯一索引會(huì)在(7,7)上加Next Key鎖启昧。
- 為什么唯一索引會(huì)在(6,4)上有Gap鎖。
先看看這個(gè)SQL執(zhí)行期間都做了什么:
1.首先因?yàn)橹麈I上沒有沖突劈伴,主鍵上插入成功密末。
2.然后插入二級(jí)索引,既唯一索引b宰啦。先對索引(6,6)加X|LOCK_ORDINARY鎖,然后因?yàn)?6,6)這條記錄產(chǎn)生了DB_DUPLICATE_KEY錯(cuò)誤苏遥。
3.Server層收到DB_DUPLICATE_KEY錯(cuò)誤饼拍,需要把第一步插入的聚簇索引回滾掉赡模,這個(gè)時(shí)候需要給(4,6)這個(gè)記錄加上一個(gè)LOCK_REC_NOT_GAP|LOCK_REC|LOCK_X(1024+32+3=1059)的鎖。
4.加鎖完成后师抄,對(4,6)記錄進(jìn)行樂觀刪除btr_cur_optimistic_delete漓柑,刪除導(dǎo)致(4,6)上的鎖發(fā)生了繼承,變?yōu)長OCK_GAP|LOCK_REC|LOCK_X(512+32+3=547)的鎖叨吮,落在(5,5)記錄上辆布。
5.接下來再根據(jù)沖突的唯一索引去定位(row_search_mvcc),對記錄(6,6)加LOCK_REC_NOT_GAP|LOCK_REC|LOCK_X(1024+32+3=1059)的鎖茶鉴,因?yàn)橹耙呀?jīng)持有了X|LOCK_ORDINARY锋玲,所以不需加新鎖。
6.根據(jù)唯一索引定位的主鍵記錄涵叮,進(jìn)行必要的加鎖惭蹂,對主鍵上的記錄(6,6)加LOCK_REC_NOT_GAP|LOCK_REC|LOCK_X(1024+32+3=1059)的鎖。
7.因?yàn)槲ㄒ凰饕齜是表的最后一個(gè)唯一索引(如果發(fā)生uk沖突的索引是最后一個(gè)唯一索引割粮、沒有外鍵引用盾碗、且不存在delete trigger時(shí),使用UPDATE ROW的方式來解決沖突http://mysql.taobao.org/monthly/2015/03/01/)舀瓢。首先對(6,6)這條記錄設(shè)置delete mark廷雅。
8.然后插入(4,6)這條記錄,進(jìn)行意向鎖(LOCK_X|LOCK_GAP|LOCK_INSERT_INTENTION=2563)兼容監(jiān)測,監(jiān)測通過。
9.執(zhí)行(4,6)記錄的插入
10.插入(4,6),導(dǎo)致本事務(wù)在(5,5)上持有的GAP鎖分裂。因此在(4,6)記錄上加個(gè)LOCK_GAP|LOCK_REC|LOCK_X(512+32+3=547)鎖航缀。
11.接下來對唯一索引b上的(6,6)進(jìn)行加鎖LOCK_REC_NOT_GAP商架,因?yàn)楸臼聞?wù)已經(jīng)在該記錄上持有了LOCK_ORDINARY,所以不需要重新加鎖谬盐。
12.對唯一索引b上的(6,6)進(jìn)行delete mark甸私。
13.進(jìn)行唯一索引(6,4)記錄的插入,因?yàn)槲ㄒ凰饕?6,6)的記錄已經(jīng)被設(shè)置為delete mark飞傀,所以會(huì)給(7,7)加上X|LOCK_ORDINARY(3+0=3)的鎖
14.執(zhí)行唯一索引(6,4)記錄的插入
15.唯一索引(6,4)記錄的插入皇型,導(dǎo)致(7,7)上的LOCK_ORDINARY的鎖發(fā)生了分裂,(6,4)記錄上多了LOCK_GAP|LOCK_REC|LOCK_X(512+32+3=547)的鎖砸烦。
調(diào)用棧
1.首先因?yàn)橹麈I上沒有沖突弃鸦,主鍵上插入成功。
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:1538
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 0x00007fff6d3d3109
thread_start 0x00007fff6d3ceb8b
2.然后插入二級(jí)索引幢痘,既唯一索引b
先對索引(6,6)加X|LOCK_ORDINARY鎖,然后因?yàn)?6,6)這條記錄產(chǎn)生了DB_DUPLICATE_KEY錯(cuò)誤唬格。
row_ins_scan_sec_index_for_duplicate(unsigned long, dict_index_t*, dtuple_t*, que_thr_t*, bool, mtr_t*, mem_block_info_t*) row0ins.cc:2152
row_ins_sec_index_entry_low(unsigned long, unsigned long, dict_index_t*, mem_block_info_t*, mem_block_info_t*, dtuple_t*, unsigned long long, que_thr_t*, bool) row0ins.cc:3040
row_ins_sec_index_entry(dict_index_t*, dtuple_t*, que_thr_t*, bool) row0ins.cc:3388
row_ins_index_entry(dict_index_t*, dtuple_t*, que_thr_t*) row0ins.cc:3439
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:1538
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 0x00007fff6d3d3109
thread_start 0x00007fff6d3ceb8b
3.Server層收到DB_DUPLICATE_KEY錯(cuò)誤,需要把第一步插入的聚簇索引回滾掉颜说,這個(gè)時(shí)候需要給(4,6)這個(gè)記錄加上一個(gè)LOCK_REC_NOT_GAP|LOCK_REC|LOCK_X(1024+32+3=1059)的鎖购岗。
參看row_convert_impl_to_expl_if_needed的注釋:
/* In case of partial rollback implicit lock on the
record is released in the middle of transaction, which
can break the serializability of IODKU and REPLACE
statements. Normal rollback is not affected by this
becasue we release the locks after the rollback. So
to prevent any other transaction modifying the record
in between the partial rollback we convert the implicit
lock on the record to explict. When the record is actually
deleted this lock be inherited by the next record. */
row_convert_impl_to_expl_if_needed(btr_cur_t*, undo_node_t*) row0undo.cc:392
row_undo_ins_remove_clust_rec(undo_node_t*) row0uins.cc:147
row_undo_ins(undo_node_t*, que_thr_t*) row0uins.cc:519
row_undo(undo_node_t*, que_thr_t*) row0undo.cc:335
row_undo_step(que_thr_t*) row0undo.cc:420
que_thr_step(que_thr_t*) que0que.cc:1055
que_run_threads_low(que_thr_t*) que0que.cc:1119
que_run_threads(que_thr_t*) que0que.cc:1159
trx_rollback_to_savepoint_low(trx_t*, trx_savept_t*) trx0roll.cc:126
trx_rollback_to_savepoint(trx_t*, trx_savept_t*) trx0roll.cc:167
row_mysql_handle_errors(dberr_t*, trx_t*, que_thr_t*, trx_savept_t*) row0mysql.cc:775
row_insert_for_mysql_using_ins_graph(unsigned char const*, row_prebuilt_t*) row0mysql.cc:1759
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:1538
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 0x00007fff6d3d3109
thread_start 0x00007fff6d3ceb8b
4.加鎖完成后,對(4,6)記錄進(jìn)行樂觀刪除btr_cur_optimistic_delete门粪,刪除導(dǎo)致(4,6)上的鎖發(fā)生了繼承喊积,變?yōu)長OCK_GAP|LOCK_REC|LOCK_X(512+32+3=547)的鎖,落在(5,5)記錄上玄妈。
lock_rec_add_to_queue(unsigned long, buf_block_t const*, unsigned long, dict_index_t*, trx_t*, bool) lock0lock.cc:1849
lock_rec_inherit_to_gap(buf_block_t const*, buf_block_t const*, unsigned long, unsigned long) lock0lock.cc:2695
lock_update_delete(buf_block_t const*, unsigned char const*) lock0lock.cc:3603
btr_cur_optimistic_delete_func(btr_cur_t*, unsigned long, mtr_t*) btr0cur.cc:5136
row_undo_ins_remove_clust_rec(undo_node_t*) row0uins.cc:149
row_undo_ins(undo_node_t*, que_thr_t*) row0uins.cc:519
row_undo(undo_node_t*, que_thr_t*) row0undo.cc:335
row_undo_step(que_thr_t*) row0undo.cc:420
que_thr_step(que_thr_t*) que0que.cc:1055
que_run_threads_low(que_thr_t*) que0que.cc:1119
que_run_threads(que_thr_t*) que0que.cc:1159
trx_rollback_to_savepoint_low(trx_t*, trx_savept_t*) trx0roll.cc:126
trx_rollback_to_savepoint(trx_t*, trx_savept_t*) trx0roll.cc:167
row_mysql_handle_errors(dberr_t*, trx_t*, que_thr_t*, trx_savept_t*) row0mysql.cc:775
row_insert_for_mysql_using_ins_graph(unsigned char const*, row_prebuilt_t*) row0mysql.cc:1759
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:1538
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 0x00007fff6d3d3109
thread_start 0x00007fff6d3ceb8b
5.接下來再根據(jù)沖突的唯一索引去定位(row_search_mvcc)乾吻,對記錄(6,6)加LOCK_REC_NOT_GAP|LOCK_REC|LOCK_X(1024+32+3=1059)的鎖,因?yàn)橹耙呀?jīng)持有了X|LOCK_ORDINARY拟蜻,所以不需加新鎖绎签。
lock_rec_lock_slow(unsigned long, unsigned long, buf_block_t const*, unsigned long, dict_index_t*, que_thr_t*) lock0lock.cc:1985
lock_rec_lock(bool, unsigned long, buf_block_t const*, unsigned long, dict_index_t*, que_thr_t*) lock0lock.cc:2069
lock_sec_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:6349
sel_set_rec_lock(btr_pcur_t*, unsigned char const*, dict_index_t*, unsigned long const*, unsigned long, unsigned long, que_thr_t*, mtr_t*) row0sel.cc:1275
row_search_mvcc(unsigned char*, page_cur_mode_t, row_prebuilt_t*, unsigned long, unsigned long) row0sel.cc:5529
ha_innobase::index_read(unsigned char*, unsigned char const*, unsigned int, ha_rkey_function) ha_innodb.cc:8753
handler::index_read_map(unsigned char*, unsigned char const*, unsigned long, ha_rkey_function) handler.h:2818
handler::index_read_idx_map(unsigned char*, unsigned int, unsigned char const*, unsigned long, ha_rkey_function) handler.cc:7621
handler::ha_index_read_idx_map(unsigned char*, unsigned int, unsigned char const*, unsigned long, ha_rkey_function) handler.cc:3098
write_record(THD*, TABLE*, COPY_INFO*, COPY_INFO*) sql_insert.cc:1642
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 0x00007fff6d3d3109
thread_start 0x00007fff6d3ceb8b
6.根據(jù)唯一索引定位的主鍵記錄,進(jìn)行必要的加鎖酝锅,對主鍵上的記錄(6,6)加LOCK_REC_NOT_GAP|LOCK_REC|LOCK_X(1024+32+3=1059)的鎖诡必。
lock_rec_add_to_queue(unsigned long, buf_block_t const*, unsigned long, dict_index_t*, trx_t*, bool) lock0lock.cc:1817
lock_rec_lock_slow(unsigned long, unsigned long, buf_block_t const*, unsigned long, dict_index_t*, que_thr_t*) lock0lock.cc:2008
lock_rec_lock(bool, unsigned long, buf_block_t const*, unsigned long, dict_index_t*, que_thr_t*) lock0lock.cc:2069
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_sel_get_clust_rec_for_mysql(row_prebuilt_t*, dict_index_t*, unsigned char const*, que_thr_t*, unsigned char const**, unsigned long**, mem_block_info_t**, dtuple_t const**, mtr_t*) row0sel.cc:3652
row_search_mvcc(unsigned char*, page_cur_mode_t, row_prebuilt_t*, unsigned long, unsigned long) row0sel.cc:5777
ha_innobase::index_read(unsigned char*, unsigned char const*, unsigned int, ha_rkey_function) ha_innodb.cc:8753
handler::index_read_map(unsigned char*, unsigned char const*, unsigned long, ha_rkey_function) handler.h:2818
handler::index_read_idx_map(unsigned char*, unsigned int, unsigned char const*, unsigned long, ha_rkey_function) handler.cc:7621
handler::ha_index_read_idx_map(unsigned char*, unsigned int, unsigned char const*, unsigned long, ha_rkey_function) handler.cc:3098
write_record(THD*, TABLE*, COPY_INFO*, COPY_INFO*) sql_insert.cc:1642
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 0x00007fff6d3d3109
thread_start 0x00007fff6d3ceb8b
7.因?yàn)槲ㄒ凰饕齜是表的最后一個(gè)唯一索引(如果發(fā)生uk沖突的索引是最后一個(gè)唯一索引、沒有外鍵引用搔扁、且不存在delete trigger時(shí)爸舒,使用UPDATE ROW的方式來解決沖突http://mysql.taobao.org/monthly/2015/03/01/)。首先對(6,6)這條記錄設(shè)置delete mark阁谆。
rec_set_deleted_flag_new(unsigned char*, page_zip_des_t*, unsigned long) rem0rec.ic:765
btr_rec_set_deleted_flag(unsigned char*, page_zip_des_t*, unsigned long) btr0cur.ic:236
btr_cur_del_mark_set_clust_rec(unsigned long, buf_block_t*, unsigned char*, dict_index_t*, unsigned long const*, que_thr_t*, dtuple_t const*, mtr_t*) btr0cur.cc:4844
row_upd_clust_rec_by_insert(unsigned long, upd_node_t*, dict_index_t*, que_thr_t*, unsigned long, mtr_t*) row0upd.cc:2546
row_upd_clust_step(upd_node_t*, que_thr_t*) row0upd.cc:2979
row_upd(upd_node_t*, que_thr_t*) row0upd.cc:3054
row_upd_step(que_thr_t*) row0upd.cc:3200
row_update_for_mysql_using_upd_graph(unsigned char const*, row_prebuilt_t*) row0mysql.cc:2582
row_update_for_mysql(unsigned char const*, row_prebuilt_t*) row0mysql.cc:2672
ha_innobase::update_row(unsigned char const*, unsigned char*) ha_innodb.cc:8257
handler::ha_update_row(unsigned char const*, unsigned char*) handler.cc:8134
write_record(THD*, TABLE*, COPY_INFO*, COPY_INFO*) sql_insert.cc:1831
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 0x00007fff6d3d3109
thread_start 0x00007fff6d3ceb8b
8.然后插入(4,6)這條記錄,進(jìn)行意向鎖(LOCK_X|LOCK_GAP|LOCK_INSERT_INTENTION=2563)兼容監(jiān)測,監(jiān)測通過碳抄。
lock_rec_insert_check_and_lock(unsigned long, unsigned char const*, buf_block_t*, dict_index_t*, que_thr_t*, mtr_t*, unsigned long*) lock0lock.cc:5991
btr_cur_ins_lock_and_undo(unsigned long, btr_cur_t*, dtuple_t*, que_thr_t*, mtr_t*, unsigned long*) btr0cur.cc:2977
btr_cur_optimistic_insert(unsigned long, btr_cur_t*, unsigned long**, mem_block_info_t**, dtuple_t*, unsigned char**, big_rec_t**, unsigned long, que_thr_t*, mtr_t*) btr0cur.cc:3217
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:2612
row_ins_clust_index_entry(dict_index_t*, dtuple_t*, que_thr_t*, unsigned long, bool) row0ins.cc:3299
row_upd_clust_rec_by_insert(unsigned long, upd_node_t*, dict_index_t*, que_thr_t*, unsigned long, mtr_t*) row0upd.cc:2590
row_upd_clust_step(upd_node_t*, que_thr_t*) row0upd.cc:2979
row_upd(upd_node_t*, que_thr_t*) row0upd.cc:3054
row_upd_step(que_thr_t*) row0upd.cc:3200
row_update_for_mysql_using_upd_graph(unsigned char const*, row_prebuilt_t*) row0mysql.cc:2582
row_update_for_mysql(unsigned char const*, row_prebuilt_t*) row0mysql.cc:2672
ha_innobase::update_row(unsigned char const*, unsigned char*) ha_innodb.cc:8257
handler::ha_update_row(unsigned char const*, unsigned char*) handler.cc:8134
write_record(THD*, TABLE*, COPY_INFO*, COPY_INFO*) sql_insert.cc:1831
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 0x00007fff6d3d3109
thread_start 0x00007fff6d3ceb8b
9.執(zhí)行(4,6)記錄的插入
page_cur_insert_rec_low(unsigned char*, dict_index_t*, unsigned char const*, unsigned long*, mtr_t*) page0cur.cc:1354
page_cur_tuple_insert(page_cur_t*, dtuple_t const*, dict_index_t*, unsigned long**, mem_block_info_t**, unsigned long, mtr_t*, bool) page0cur.ic:287
btr_cur_optimistic_insert(unsigned long, btr_cur_t*, unsigned long**, mem_block_info_t**, dtuple_t*, unsigned char**, big_rec_t**, unsigned long, que_thr_t*, mtr_t*) btr0cur.cc:3224
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:2612
row_ins_clust_index_entry(dict_index_t*, dtuple_t*, que_thr_t*, unsigned long, bool) row0ins.cc:3299
row_upd_clust_rec_by_insert(unsigned long, upd_node_t*, dict_index_t*, que_thr_t*, unsigned long, mtr_t*) row0upd.cc:2590
row_upd_clust_step(upd_node_t*, que_thr_t*) row0upd.cc:2979
row_upd(upd_node_t*, que_thr_t*) row0upd.cc:3054
row_upd_step(que_thr_t*) row0upd.cc:3200
row_update_for_mysql_using_upd_graph(unsigned char const*, row_prebuilt_t*) row0mysql.cc:2582
row_update_for_mysql(unsigned char const*, row_prebuilt_t*) row0mysql.cc:2672
ha_innobase::update_row(unsigned char const*, unsigned char*) ha_innodb.cc:8257
handler::ha_update_row(unsigned char const*, unsigned char*) handler.cc:8134
write_record(THD*, TABLE*, COPY_INFO*, COPY_INFO*) sql_insert.cc:1831
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 0x00007fff6d3d3109
thread_start 0x00007fff6d3ceb8b
10.插入(4,6),導(dǎo)致本事務(wù)在(5,5)上持有的GAP鎖分裂。因此在(4,6)記錄上加個(gè)LOCK_GAP|LOCK_REC|LOCK_X(512+32+3=547)鎖场绿。
lock_rec_add_to_queue(unsigned long, buf_block_t const*, unsigned long, dict_index_t*, trx_t*, bool) lock0lock.cc:1843
lock_rec_inherit_to_gap_if_gap_lock(buf_block_t const*, unsigned long, unsigned long) lock0lock.cc:2737
lock_update_insert(buf_block_t const*, unsigned char const*) lock0lock.cc:3569
btr_cur_optimistic_insert(unsigned long, btr_cur_t*, unsigned long**, mem_block_info_t**, dtuple_t*, unsigned char**, big_rec_t**, unsigned long, que_thr_t*, mtr_t*) btr0cur.cc:3287
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:2612
row_ins_clust_index_entry(dict_index_t*, dtuple_t*, que_thr_t*, unsigned long, bool) row0ins.cc:3299
row_upd_clust_rec_by_insert(unsigned long, upd_node_t*, dict_index_t*, que_thr_t*, unsigned long, mtr_t*) row0upd.cc:2590
row_upd_clust_step(upd_node_t*, que_thr_t*) row0upd.cc:2979
row_upd(upd_node_t*, que_thr_t*) row0upd.cc:3054
row_upd_step(que_thr_t*) row0upd.cc:3200
row_update_for_mysql_using_upd_graph(unsigned char const*, row_prebuilt_t*) row0mysql.cc:2582
row_update_for_mysql(unsigned char const*, row_prebuilt_t*) row0mysql.cc:2672
ha_innobase::update_row(unsigned char const*, unsigned char*) ha_innodb.cc:8257
handler::ha_update_row(unsigned char const*, unsigned char*) handler.cc:8134
write_record(THD*, TABLE*, COPY_INFO*, COPY_INFO*) sql_insert.cc:1831
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 0x00007fff6d3d3109
thread_start 0x00007fff6d3ceb8b
11.接下來對唯一索引b上的(6,6)進(jìn)行加鎖LOCK_REC_NOT_GAP剖效,因?yàn)楸臼聞?wù)已經(jīng)在該記錄上持有了LOCK_ORDINARY,所以不需要重新加鎖。
lock_rec_lock_slow(unsigned long, unsigned long, buf_block_t const*, unsigned long, dict_index_t*, que_thr_t*) lock0lock.cc:1985
lock_rec_lock(bool, unsigned long, buf_block_t const*, unsigned long, dict_index_t*, que_thr_t*) lock0lock.cc:2069
lock_sec_rec_modify_check_and_lock(unsigned long, buf_block_t*, unsigned char const*, dict_index_t*, que_thr_t*, mtr_t*) lock0lock.cc:6246
btr_cur_del_mark_set_sec_rec(unsigned long, btr_cur_t*, unsigned long, que_thr_t*, mtr_t*) btr0cur.cc:4973
row_upd_sec_index_entry(upd_node_t*, que_thr_t*) row0upd.cc:2305
row_upd_sec_step(upd_node_t*, que_thr_t*) row0upd.cc:2374
row_upd(upd_node_t*, que_thr_t*) row0upd.cc:3083
row_upd_step(que_thr_t*) row0upd.cc:3200
row_update_for_mysql_using_upd_graph(unsigned char const*, row_prebuilt_t*) row0mysql.cc:2582
row_update_for_mysql(unsigned char const*, row_prebuilt_t*) row0mysql.cc:2672
ha_innobase::update_row(unsigned char const*, unsigned char*) ha_innodb.cc:8257
handler::ha_update_row(unsigned char const*, unsigned char*) handler.cc:8134
write_record(THD*, TABLE*, COPY_INFO*, COPY_INFO*) sql_insert.cc:1831
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 0x00007fff6d3d3109
thread_start 0x00007fff6d3ceb8b
12.對唯一索引b上的(6,6)進(jìn)行delete mark璧尸。
btr_rec_set_deleted_flag(unsigned char*, page_zip_des_t*, unsigned long) btr0cur.ic:235
btr_cur_del_mark_set_sec_rec(unsigned long, btr_cur_t*, unsigned long, que_thr_t*, mtr_t*) btr0cur.cc:4995
row_upd_sec_index_entry(upd_node_t*, que_thr_t*) row0upd.cc:2305
row_upd_sec_step(upd_node_t*, que_thr_t*) row0upd.cc:2374
row_upd(upd_node_t*, que_thr_t*) row0upd.cc:3083
row_upd_step(que_thr_t*) row0upd.cc:3200
row_update_for_mysql_using_upd_graph(unsigned char const*, row_prebuilt_t*) row0mysql.cc:2582
row_update_for_mysql(unsigned char const*, row_prebuilt_t*) row0mysql.cc:2672
ha_innobase::update_row(unsigned char const*, unsigned char*) ha_innodb.cc:8257
handler::ha_update_row(unsigned char const*, unsigned char*) handler.cc:8134
write_record(THD*, TABLE*, COPY_INFO*, COPY_INFO*) sql_insert.cc:1831
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 0x00007fff6d3d3109
thread_start 0x00007fff6d3ceb8b
13.進(jìn)行唯一索引(6,4)記錄的插入咒林,因?yàn)槲ㄒ凰饕?6,6)的記錄已經(jīng)被設(shè)置為delete mark,所以會(huì)給(7,7)加上X|LOCK_ORDINARY(3+0=3)的鎖
lock_rec_lock(bool, unsigned long, buf_block_t const*, unsigned long, dict_index_t*, que_thr_t*) lock0lock.cc:2048
lock_sec_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:6349
row_ins_set_exclusive_rec_lock(unsigned long, buf_block_t const*, unsigned char const*, dict_index_t*, unsigned long const*, que_thr_t*) row0ins.cc:1525
row_ins_scan_sec_index_for_duplicate(unsigned long, dict_index_t*, dtuple_t*, que_thr_t*, bool, mtr_t*, mem_block_info_t*) row0ins.cc:2123
row_ins_sec_index_entry_low(unsigned long, unsigned long, dict_index_t*, mem_block_info_t*, mem_block_info_t*, dtuple_t*, unsigned long long, que_thr_t*, bool) row0ins.cc:3040
row_ins_sec_index_entry(dict_index_t*, dtuple_t*, que_thr_t*, bool) row0ins.cc:3388
row_upd_sec_index_entry(upd_node_t*, que_thr_t*) row0upd.cc:2347
row_upd_sec_step(upd_node_t*, que_thr_t*) row0upd.cc:2374
row_upd(upd_node_t*, que_thr_t*) row0upd.cc:3083
row_upd_step(que_thr_t*) row0upd.cc:3200
row_update_for_mysql_using_upd_graph(unsigned char const*, row_prebuilt_t*) row0mysql.cc:2582
row_update_for_mysql(unsigned char const*, row_prebuilt_t*) row0mysql.cc:2672
ha_innobase::update_row(unsigned char const*, unsigned char*) ha_innodb.cc:8257
handler::ha_update_row(unsigned char const*, unsigned char*) handler.cc:8134
write_record(THD*, TABLE*, COPY_INFO*, COPY_INFO*) sql_insert.cc:1831
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 0x00007fff6d3d3109
thread_start 0x00007fff6d3ceb8b
14.執(zhí)行唯一索引(6,4)記錄的插入
page_cur_tuple_insert(page_cur_t*, dtuple_t const*, dict_index_t*, unsigned long**, mem_block_info_t**, unsigned long, mtr_t*, bool) page0cur.ic:269
btr_cur_optimistic_insert(unsigned long, btr_cur_t*, unsigned long**, mem_block_info_t**, dtuple_t*, unsigned char**, big_rec_t**, unsigned long, que_thr_t*, mtr_t*) btr0cur.cc:3224
row_ins_sec_index_entry_low(unsigned long, unsigned long, dict_index_t*, mem_block_info_t*, mem_block_info_t*, dtuple_t*, unsigned long long, que_thr_t*, bool) row0ins.cc:3132
row_ins_sec_index_entry(dict_index_t*, dtuple_t*, que_thr_t*, bool) row0ins.cc:3388
row_upd_sec_index_entry(upd_node_t*, que_thr_t*) row0upd.cc:2347
row_upd_sec_step(upd_node_t*, que_thr_t*) row0upd.cc:2374
row_upd(upd_node_t*, que_thr_t*) row0upd.cc:3083
row_upd_step(que_thr_t*) row0upd.cc:3200
row_update_for_mysql_using_upd_graph(unsigned char const*, row_prebuilt_t*) row0mysql.cc:2582
row_update_for_mysql(unsigned char const*, row_prebuilt_t*) row0mysql.cc:2672
ha_innobase::update_row(unsigned char const*, unsigned char*) ha_innodb.cc:8257
handler::ha_update_row(unsigned char const*, unsigned char*) handler.cc:8134
write_record(THD*, TABLE*, COPY_INFO*, COPY_INFO*) sql_insert.cc:1831
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 0x00007fff6d3d3109
thread_start 0x00007fff6d3ceb8b
15.唯一索引(6,4)記錄的插入爷光,導(dǎo)致(7,7)上的LOCK_ORDINARY的鎖發(fā)生了分裂垫竞,(6,4)記錄上多了LOCK_GAP|LOCK_REC|LOCK_X(512+32+3=547)的鎖。
lock_rec_add_to_queue(unsigned long, buf_block_t const*, unsigned long, dict_index_t*, trx_t*, bool) lock0lock.cc:1849
lock_rec_inherit_to_gap_if_gap_lock(buf_block_t const*, unsigned long, unsigned long) lock0lock.cc:2737
lock_update_insert(buf_block_t const*, unsigned char const*) lock0lock.cc:3569
btr_cur_optimistic_insert(unsigned long, btr_cur_t*, unsigned long**, mem_block_info_t**, dtuple_t*, unsigned char**, big_rec_t**, unsigned long, que_thr_t*, mtr_t*) btr0cur.cc:3287
row_ins_sec_index_entry_low(unsigned long, unsigned long, dict_index_t*, mem_block_info_t*, mem_block_info_t*, dtuple_t*, unsigned long long, que_thr_t*, bool) row0ins.cc:3132
row_ins_sec_index_entry(dict_index_t*, dtuple_t*, que_thr_t*, bool) row0ins.cc:3388
row_upd_sec_index_entry(upd_node_t*, que_thr_t*) row0upd.cc:2347
row_upd_sec_step(upd_node_t*, que_thr_t*) row0upd.cc:2374
row_upd(upd_node_t*, que_thr_t*) row0upd.cc:3083
row_upd_step(que_thr_t*) row0upd.cc:3200
row_update_for_mysql_using_upd_graph(unsigned char const*, row_prebuilt_t*) row0mysql.cc:2582
row_update_for_mysql(unsigned char const*, row_prebuilt_t*) row0mysql.cc:2672
ha_innobase::update_row(unsigned char const*, unsigned char*) ha_innodb.cc:8257
handler::ha_update_row(unsigned char const*, unsigned char*) handler.cc:8134
write_record(THD*, TABLE*, COPY_INFO*, COPY_INFO*) sql_insert.cc:1831
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 0x00007fff6d3d3109
thread_start 0x00007fff6d3ceb8b
https://www.whitewood.me/2017/08/06/%E5%B9%B6%E5%8F%91replace%20into%E5%AF%BC%E8%87%B4MySQL%E6%AD%BB%E9%94%81/
http://mysql.taobao.org/monthly/2015/03/01/
http://mysql.taobao.org/monthly/2020/09/06/