——————————————————–
ha_innobase::write_row是向innodb寫入記錄的函數,進入函數時自增列的值還沒被設定(如何是null的話),會調用handler::update_auto_increment來擷取并更新自增列,然後再調用row_insert_for_mysql來實際插入記錄。我們的精力主要集中在update_auto_increment及其調用的函數上。
先了解下handler的幾個跟自增列相關的成員變量(根據注釋及gdb推測):
a. ulonglong next_insert_id;
下一個插入自增列的值,當一次插入多行記錄時(例如,insert select 操作),第一個沒有指定自增列值的記錄會從get_auto_increment函數擷取的值并指派給next_insert_id,這樣對于剩下的記錄就可以直接使用next_insert_id(每次都修改該值)。
b. ulonglong insert_id_for_cur_row;
目前記錄的insert id 。第一次成功插入後,這個值被存儲到thd::first_successful_insert_id_in_cur_stmt
c. discrete_interval auto_inc_interval_for_cur_row;
從get_auto_increment函數擷取的insert id區間。
discrete_interval 結構體又包含三個值
interval_min 區間的最小值
interval_values 區間内可用自增值的個數
interval_max 最大值
d. uint auto_inc_intervals_count;
目前插入語句保留的自增區間數
e. estimation_rows_to_insert
估計将要插入的記錄數,在函數handler::ha_start_bulk_insert()中被設定,值為0表示未知。
//////////////////////////////////////////////////////////////////////////////////////////////////////
下面來分析下handler::update_auto_increment主要流程
1.首先判斷自增列是否已經指派,或者是否不可以為null&&sql_mode為mode_no_auto_value_on_zero時,不做處理
if ((nr= table->next_number_field->val_int()) != 0 ||
(table->auto_increment_field_not_null &&
thd->variables.sql_mode & mode_no_auto_value_on_zero)){
handler::adjust_next_insert_id_after_explicit_value // 根據nr和offset設定下一個自增值next_insert_id
insert_id_for_cur_row= 0;
dbug_return(0);
}
2.當預取的insert id區間用完時,需要取更多的insert id。
if ((nr= next_insert_id) >= auto_inc_interval_for_cur_row.maximum())
{
a.如果預取了下一個區間,則使用下一個區間的server id。
nr= forced->minimum();
nb_reserved_values= forced->values();
b.否則:
(1)确定需要自增值的數目(nb_desired_values)
當沒有預留值,且估值大于0時(auto_inc_intervals_count == 0) && (estimation_rows_to_insert > 0),設定nb_desired_values=estimation_rows_to_insert
否則: (例如insert into t values (null,12),(98,51),(null,67),第3個記錄會走到這個邏輯)
–auto_inc_intervals_count <= auto_inc_default_nb_max_bits時
nb_desired_values= auto_inc_default_nb_rows *
(1 << auto_inc_intervals_count);
set_if_smaller(nb_desired_values, auto_inc_default_nb_max);
nb_desired_values值為1*(1<< auto_inc_intervals_count)且不大于auto_inc_default_nb_max(65535)
–auto_inc_intervals_count> auto_inc_default_nb_max_bits時
nb_desired_values值為最大值65535
實際上,對于innodb而言,請求的數量可能跟實際獲得的自增值數量不同,這取決于trx->n_autoinc_rows
每寫完一次記錄後, trx->n_autoinc_rows 會被減1(在函數ha_innobase::write_row 中)
例如,對于sql:insert into t values (null,12),(98,51),(null,67),(120,44),(null,123);
假設目前值為90
第一次調用update_auto_increment時,請求保留5個值
trx->n_autoinc_rows=5,保留5個值
{interval_min = 90, interval_values = 5, interval_max = 95, next = 0x0}
第二次調用update_auto_increment時,使用98
trx->n_autoinc_rows=4
第三次調用update_auto_increment時,請求保留2個值
trx->n_autoinc_rows=3, 保留3個值
{interval_min = 99, interval_values = 3, interval_max = 102, next = 0x0}
prebuilt->trx->n_autoinc_rows
第四次調用update_auto_increment時,使用120
trx->n_autoinc_rows==2
第五次調用update_auto_increment時,請求保留4個值
trx->n_autoinc_rows=1,保留1個值
{interval_min = 121, interval_values = 1, interval_max = 122, next = 0x0}
(2)調用ha_innobase::get_auto_increment擷取自增區間,更新table->autoinc (函數後續詳解)
(3)nr= compute_next_insert_id(nr-1, variables); 根據offset等配置項計算nr
(4)當(table->s->next_number_keypart == 0時,設定append=true
3.table->next_number_field->store((longlong) nr, true)
4.append為true時(為false表示使用的是之前預取的值,無需執行如下步驟)
設定目前auto_inc_interval_for_cur_row
auto_inc_intervals_count++
如果為statement模式,則記錄該自增值到binlog
5.insert_id_for_cur_row= nr
6.set_next_insert_id(compute_next_insert_id(nr, variables)); 設定next_insert_id的值
///////////////////////////////////////////////////////////////////////////////////////////////////
ha_innobase::get_auto_increment是實際配置設定自增值的主要函數。流程如下:
1.調用函數innobase_get_autoinc
a.
ha_innobase::innobase_lock_autoinc
根據不同的autoinc_lock_mode選擇加鎖模式,我們的環境下這個值都是1,是以選擇的case是autoinc_new_style_locking
注意,以load 或者 insert ..select的方式插入資料時會回退到autoinc_old_style_locking
dict_table_autoinc_lock->mutex_enter(&table->autoinc_mutex); //擷取autoinc mutex
再判斷是否有别的事務已經擷取或在等待autoinc lock,如果是的話,解除mutex并回退到autoinc_old_style_locking,否則break,并傳回。
在old style locking模式下,調用row_lock_table_autoinc_for_mysql加auto_inc lock(調用lock_table函數加lock_auto_inc鎖),随後如果成功了,再調用dict_table_autoinc_lock(prebuilt->table)加autoinc mutex
在lock_table->lock_table_create函數裡會對table->n_waiting_or_granted_auto_inc_locks++
b.
調用dict_table_autoinc_read(prebuilt->table)擷取table->autoinc值
2.
如果tx->n_autoinc_row=0,設定該值為nb_desired_values或者1(如果nb_desired_values=0),然後set_if_bigger(*first_value, autoinc),将first_value設定為目前的table->autoinc值
如果prebuilt->autoinc_last_value == 0,也就是不在一次muti-insert的中間,也設定set_if_bigger(*first_value, autoinc)
*nb_reserved_values = trx->n_autoinc_rows;
3.當不是使用autoinc_old_style_locking時
a.計算目前值current和需要保留的區間長度need
b.調用next_value = innobase_next_autoinc函數計算保留區間的最後一個值 (bug#61209 fix點)
c.prebuilt->autoinc_last_value = next_value
d.更新table->autoinc的值(dict_table_autoinc_update_if_greater)
4.
prebuilt->autoinc_offset = offset;
prebuilt->autoinc_increment = increment;
dict_table_autoinc_unlock(prebuilt->table)->mutex_exit(&table->autoinc_mutex); //解除mutex