天天看點

[MySQL 源碼] innodb如何建立二級索引

以下為分析問題時的随筆。寫的很淩亂,僅做記錄,以備後用。。。。。。

//////////////////////////////////////////////////////////////

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)

繼續閱讀