天天看點

[MySQL源碼] 一條簡單insert語句的調用棧

以下僅用于本人調試mysql所用,不具備可讀性。

———————————————————–

create table t1 (a int primary key, b int not null) engine=innodb;

insert into t1 values (4,2);

ha_innobase::write_row

 |–>row_insert_for_mysql 

     |–>轉換記錄格式row_mysql_convert_row_to_innobase

     |–>儲存檢查點savept = trx_savept_take(trx);

     |–>row_ins_step

        |–>加ix鎖lock_table(0, node->table, lock_ix, thr)

        |–> row_ins     //輪詢索引,向表中插入記錄,這裡隻有聚集索引

        .   |–>row_ins_index_entry_step

        .      |–>建構索引記錄(node->entry)row_ins_index_entry_set_vals

        .      |–>row_ins_index_entry(node->index, node->entry, 0, true, thr)

        .      .   |–>檢查外鍵限制row_ins_check_foreign_constraints

        .      .   |->使用如下兩步嘗試插入記錄

                   >>step1.row_ins_index_entry_low(btr_modify_leaf, index, entry,n_ext, thr)/* try first optimistic descent to the b-tree */

                   >>step2,若step1失敗,row_ins_index_entry_low(btr_modify_tree, index, entry,n_ext, thr);/* try then pessimistic descent to the b-tree */

                       |–> row_ins_index_entry_low

                             |–>确定search_mode

                             1)如果是聚集索引,search_mode為傳參btr_modify_leaf或btr_modify_tree

                             2)目前事務的check_unique_secondary為false時(由變量unique_checks控制,預設為true,表示檢查唯一索引限制),search_mode = mode | btr_insert | btr_ignore_sec_unique

                             3)否則,search_mode = mode | btr_insert

                             |–>btr_cur_search_to_nth_level  查詢索引樹,将cursor移動到記錄相應的位置(待分析)

                             |–>檢測是否有dup key,主鍵索引調用row_ins_duplicate_error_in_clust,二級索引調用row_ins_scan_sec_index_for_duplicate

                             |–>modify = row_ins_must_modify  (待分析)

                             >>當modify!=0時,表明已經有一個足夠長的common prefix,直接覆寫插入記錄(待分析)

                             >>當modify=0時:

                               1)當mode=btr_modify_leaf時,調用btr_cur_optimistic_insert,嘗試向一個索引page中插入記錄,如果頁面空閑空間太小,則傳回失敗

                                   |–>btr_cur_ins_lock_and_undo //檢查是否有鎖沖突,并插入undolog

                                         |–>lock_rec_insert_check_and_lock //檢查鎖沖突

                                               |–>沒有下一個記錄的鎖沖突,即lock_rec_get_first傳回null,更新二級索引trx id,傳回

                                               |–>調用lock_rec_other_has_conflicting,檢查是否有其他事務鎖有下一個記錄的lock_x, lock_gap和lock_insert_intention鎖

                                               |–>檢測到有沖突lock_rec_enqueue_waiting

                                                       |–> 建立鎖記錄lock_rec_create,type_code為lock_x | lock_gap|lock_insert_intention

                                                       |–>檢測死鎖

                                         |–>如果是聚集索引且不是insert buffer

                                               |–>寫入undo資訊 trx_undo_report_row_operation(待分析)

                                               |–>設定聚集索引記錄的trx id和復原段指針,row_upd_index_entry_sys_field

                                    |–>插入記錄page_cur_tuple_insert

                                    |–>更新該page的哈希索引btr_search_update_hash_on_insert(待分析)

                                    |–>繼承下一條記錄的gap鎖?(inherit為true),調lock_update_insert,這裡不調用 (待分析) 

                                 2)否則

                                    a)調用buf_lru_buf_pool_running_out,傳回true,表示少于25%的buffer pool可用。根據注釋,對于大事務,會将鎖存儲在buffer pool中(待證明)

                                    b)調用btr_cur_pessimistic_insert(待分析)

drop table t1;

insert into t1 values (1,2);

insert into t1 values (1,5) on duplicate key update b = b+1;

————————————————-

函數調用邏輯:

write_record->handler::ha_write_row->ha_innobase::write_row->

row_insert_for_mysql

     —->row_ins_step->row_ins->row_ins_index_entry

          |–>row_ins_index_entry_low

             |–>調用row_ins_duplicate_error_in_clust檢測是否有dup key錯誤。

                   |–>對記錄加鎖

                      >>對于replace, load datafile replace以及insert on duplicate key update操作,對記錄加一個排他鎖(lock_x)row_ins_set_exclusive_rec_lock

                            a)聚集索引lock_clust_rec_read_check_and_lock

                            b)二級索引lock_sec_rec_read_check_and_lock

                      >>否則,加一個共享鎖(lock_s)row_ins_set_shared_rec_lock

                   |–>調用row_ins_dupl_error_with_rec檢查實體記錄和将要插入的記錄是否相同

從innodb層傳回到server層後,write_record函數根據傳回的錯誤值,繼續下面的邏輯

|–>判斷是否是dup key錯誤

|–>if (table->file->extra(ha_extra_flush_cache))    //  bug#52020相關點,稍後再議

|–>一堆亂七八糟的檢查,建構記錄等….

|–>row_update_for_mysql傳遞record到innodb更新資料

     |–>row_upd_step->row_upd

       |–>row_upd_clust_step

          |–>如果沒有x鎖,則嘗試加鎖lock_clust_rec_modify_check_and_lock

          |–>row_upd_clust_rec(待分析)

                   |–>btr_cur_optimistic_update

繼續閱讀