MySQL的寫入資料儲存過程

aoerqileng發表於2022-06-10

insert 寫入資料ha_innobase::write_row方法寫入資料,

write_row方法中進行自增列的鎖定更新

構建對映的template,然後透過innodb_srv_conc_enter_innodb進入innodb,這裡會判斷是否會超過innodb的併發限制

innodb_srv_conc_enter_innodb(
/*=========================*/
trx_t*  trx)    /* in: transaction handle */
{
if (UNIV_LIKELY(srv_thread_concurrency >= SRV_CONCURRENCY_THRESHOLD)) {
return;
}
srv_conc_enter_innodb(trx);
}



srv_conc_enter_innodb 方法中描述了,執行緒是等待還是休眠

如果執行緒有ticket,可以直接進入,如果innodb的當前執行緒沒有超過srv_thread_concurrency限制,可以進入,否則執行緒沒有持有latch 的情況下,進入休眠狀態SRV_THREAD_SLEEP_DELAY,然後重新嘗試

,如果持有latch 會進入佇列進行排隊


在進入innodb 後,執行row_insert_for_mysql進行資料的寫入,這個方法就是innodb 提供的對外介面。函式傳入的記錄是mysql的格式,

函式中先進行row_mysql_delay_if_needed的判斷,如果purge有滯後,會延遲dml的操作,進入srv_dml_needed_delay指定的時間休眠。

然後開始事務,繼續呼叫row_mysql_convert_row_to_innobase 進行格式的轉換,將mysql的格式轉換成innodb的格式。

針對每列,呼叫row_mysql_store_col_in_innobase_format進行儲存


轉換完成後,呼叫row_ins 進行寫入,在row_ins中進行row_id 分配,獲取到table的cluster index後,呼叫row_ins_index_entry_step 進行插入,row_ins_index_entry_set_vals


在插入完cluster index後,dict_table_get_next_index 查詢下一個index,寫入二級索引 寫入結束


row_ins_index_entry_step(que_thr_t * thr, ins_node_t * node) (/root/mysql-5.0.15/innobase/row/row0ins.c:2214)
row_ins(ins_node_t * node, que_thr_t * thr) (/root/mysql-5.0.15/innobase/row/row0ins.c:2346)
row_ins_step(que_thr_t * thr) (/root/mysql-5.0.15/innobase/row/row0ins.c:2450)
row_insert_for_mysql(unsigned char * mysql_rec, row_prebuilt_t * prebuilt) (/root/mysql-5.0.15/innobase/row/row0mysql.c:1141)
ha_innobase::write_row(ha_innobase * const this, mysql_byte * record) (/root/mysql-5.0.15/sql/ha_innodb.cc:3371)
write_record(THD * thd, TABLE * table, COPY_INFO * info) (/root/mysql-5.0.15/sql/sql_insert.cc:1146)
mysql_insert(THD * thd, TABLE_LIST * table_list, List<Item> & fields, List<List<Item> > & values_list, List<Item> & update_fields, List<Item> & update_values, enum_duplicates duplic, bool ignore) (/root/mysql-5.0.15/sql/sql_insert.cc:531)
mysql_execute_command(THD * thd) (/root/mysql-5.0.15/sql/sql_parse.cc:3239)
mysql_parse(THD * thd, char * inBuf, uint length) (/root/mysql-5.0.15/sql/sql_parse.cc:5536)
dispatch_command(enum_server_command command, THD * thd, char * packet, unsigned int packet_length) (/root/mysql-5.0.15/sql/sql_parse.cc:1697)
do_command(THD * thd) (/root/mysql-5.0.15/sql/sql_parse.cc:1498)
handle_one_connection(void * arg) (/root/mysql-5.0.15/sql/sql_parse.cc:1143)
libpthread.so.0!start_thread (Unknown Source:0)
libc.so.6!clone (Unknown Source:0)



從這個過程中,我們可以看到mysql的寫入跟事務系統是完全解耦合的,事務系統的呼叫就是呼叫開始事務的方法,

寫入後,然後開始寫入binlog,然後開始進入提交階段,進行兩階段提交,兩階段提交的過程另外blog有記錄。


關於purge導致的dml delay,檢視trx_purge函式,該函式控制delay多久

/* Close and free the old purge view */    
read_view_close(purge_sys->view);
purge_sys->view = NULL;
mem_heap_empty(purge_sys->heap);
/* Determine how much data manipulation language (DML) statements
    need to be delayed in order to reduce the lagging of the purge
    thread. */
srv_dml_needed_delay = 0; /* in microseconds; default: no delay */
/* If we cannot advance the 'purge view' because of an old
    'consistent read view', then the DML statements cannot be delayed.
    Also, srv_max_purge_lag <= 0 means 'infinity'. */
if (srv_max_purge_lag > 0
&& !UT_LIST_GET_LAST(trx_sys->view_list)) {
float   ratio = (float) trx_sys->rseg_history_len
/ srv_max_purge_lag;
if (ratio > ULINT_MAX / 10000) {
/* Avoid overflow: maximum delay is 4295 seconds */
srv_dml_needed_delay = ULINT_MAX;
} else if (ratio > 1) {
/* If the history list length exceeds the
            innodb_max_purge_lag, the
            data manipulation statements are delayed
            by at least 5000 microseconds. */
srv_dml_needed_delay = (ulint) ((ratio - .5) * 10000);
}
}
purge_sys->view = read_view_oldest_copy_or_open_new(NULL,
purge_sys->heap);
mutex_exit(&kernel_mutex);
rw_lock_x_unlock(&(purge_sys->latch));
purge_sys->state = TRX_PURGE_ON;
/* Handle at most 20 undo log pages in one purge batch */
一次最多20個undo log page
purge_sys->handle_limit = purge_sys->n_pages_handled + 20;
old_pages_handled = purge_sys->n_pages_handled;
mutex_exit(&(purge_sys->mutex));
mutex_enter(&kernel_mutex);
thr = que_fork_start_command(purge_sys->query);
ut_ad(thr);
/*  thr2 = que_fork_start_command(purge_sys->query);
    
    ut_ad(thr2); */
mutex_exit(&kernel_mutex);
/*  srv_que_task_enqueue(thr2); */
if (srv_print_thread_releases) {
fputs("Starting purge\n", stderr);
}
que_run_threads(thr);
if (srv_print_thread_releases) {
fprintf(stderr,
"Purge ends; pages handled %lu\n",
(ulong) purge_sys->n_pages_handled);
}
return(purge_sys->n_pages_handled - old_pages_handled);

innodb_max_purge_lag 這個引數預設是0,沒有延時

 有興趣學習原始碼的加群一起學習啊 QQ:    700072075


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/25719946/viewspace-2899893/,如需轉載,請註明出處,否則將追究法律責任。

相關文章