以下为分析问题时的随笔。写的很凌乱,仅做记录,以备后用。。。。。。
//////////////////////////////////////////////////////////////
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的解释)
尽管Buglist中标注为已经fix,但事实上因为某些意外并没有merge到主干。Percona已经修复了这个问题(https://code.launchpad.net/~laurynas-biveinis/percona-server/bug54330/+merge/94510)
e.
排序完成后,调用row_merge_insert_index_tuples插入索引数据
f.
清理工作,及更新统计信息(如果开启了expand_fast_index_creation)