以下為分析問題時的随筆。寫的很淩亂,僅做記錄,以備後用。。。。。。
//////////////////////////////////////////////////////////////
ha_innobase::add_index是innodb建立索引的接口函數。
以下所有的讨論都是基于建立一個非聚集的二級索引。是以一些過程是被省略掉了。
1.擷取資料詞典資訊
indexed_table = dict_table_get(prebuilt->table->name, false);
2.檢查索引鍵是否可用
error = innobase_check_index_keys(key_info, num_of_keys, prebuilt->table);
3.檢查索引列長度
4.
a.建立一個trx對象用于操作innodb資料詞典,并建立新的資料詞典資訊
如果是主鍵,加lock_x,否則加lock_s鎖
b.加資料詞典鎖row_mysql_lock_data_dictionary(trx);
c.在ibdata的sys_indexes中加載新的資料詞典資訊
d.trx_commit_for_mysql(trx); 送出剛剛建立的trx
e.row_mysql_unlock_data_dictionary(trx)
以上步驟完成了對ibdata資料詞典内的更新,在 完成後釋放鎖,這時候,如果在後續的row_merge_build_indexes時crash掉。trx_rollback_active不會drop掉新索引。
5.
調用函數row_merge_build_indexes實際建立索引,我們的讨論主要集中于此。
row_merge_build_indexes會讀取表的聚集索引記錄,建立臨時表來儲存這些記錄,并使用合并排序算法進行排序以建立索引
a.
首先初始化merge file相關的資料結構,并初始化
merge_files = mem_alloc(n_indexes * sizeof *merge_files);
block_size = 3 * sizeof *block;
block = os_mem_alloc_large(&block_size);
merge_files用于管理針對每個索引建立的臨時檔案。
block類型為row_merge_block_t,其定義如下:
typedef byte row_merge_block_t[1048576];
是以block_size的值為3* 1048576=3145728位元組
b.
建立臨時檔案
調用row_merge_file_create函數來對該資料的每個成員初始化臨時檔案。
單獨建立一個臨時檔案tmpfd = row_merge_file_create_low();
c.
調用函數row_merge_read_clustered_index,讀取聚集索引記錄
一次scan 聚集索引,但為每一個要建立的索引建立entry,并将其加入到每個索引的sort buffer中(row_merge_buf_add)。
當buffer中的記錄足夠多時,就調用row_merge_buf_sort進行排序,并寫入磁盤(row_merge_buf_write &&row_merge_write).
每個buffer的最大tuple數為:
max_tuples = sizeof(row_merge_block_t)/ ut_max(1, dict_index_get_min_size(index));
d.
現在我們可以對上一步準備好的臨時檔案或buffer進行排序。
排序函數為:
error = row_merge_sort(trx, indexes[i], &merge_files[i],
block, &tmpfd, table);
在row_merge_sort函數中,對剛剛産生的臨時檔案進行歸并排序(row_merge)。
在5.5的mysql中,這裡存在一個bug,歸并排序存在問題(參閱mysql官方 bug#54330上jimmy yang的解釋)
e.
排序完成後,調用row_merge_insert_index_tuples插入索引資料
f.
清理工作,及更新統計資訊(如果開啟了expand_fast_index_creation)