天天看点

[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)

继续阅读