create table t1 (a int primary key, b int)engine=innodb; insert into t1 values (1,1),(2,2),(3,3),(4,4);
alter table t1 add index idx_t1_b(b);
sql_table.cc::mysql_alter_table();
// 判断当前操作是否可以进行Inplace实现,不可进行Inplace Alter的包括:
// 1. Auto Increment字段修改;
// 2. 列重命名;
// 3. 行存储格式修改;等
mysql_compare_tables() -> ha_innobase::check_if_incompatible_data();
// Inplace创建索引第一阶段(主要阶段)
handler0alter.cc::add_index();
…
// 创建索引数据字典
row0merge.c::row_merge_create_index();
index = dict_mem_index_create();
// 每个索引数据字典上,有一个trx_id,记录创建此索引的事务
// 此trx_id有何功能,接着往下看
index->trx_id = trx_id;
// 读取聚簇索引,构造新索引的项,排序并插入新索引
row0merge.c::row_merge_build_indexes();
// 读取聚簇索引,注意:只读取其中的非删除项
// 跳过所有删除项,为什么可以这么做?往下看
row_merge_read_clustered_index();
// 文件排序
row_merge_sort();
// 顺序读取排序文件中的索引项,逐个插入新建索引中
row_merge_insert_index_tuples();
// 等待打开当前表的所有只读事务提交
sql_base.cc::wait_while_table_is_used();
// 创建索引结束,做最后的清理工作
handler0alter.cc::final_add_index();
// Inplace add Index完毕
session 1: session 2:
// 此时创建Global ReadView
select * from t2;
delete from t1 where b = 1;
// idx_t1_b索引上,没有b = 1的项
alter table t1 add index idx_t1_b(b);
// 由于ReadView在delete之前获取
// 因此b = 1这一项应该被读取到
select * from t1 where b = 1;
…
ha_innobase::index_init();
change_active_index();
// 判断session 1事务的ReadView是否可以看到session 2创建索引的事务
// 此处,session 2事务当然不可见,那么prebuilt->index_usable = false
prebuilt->index_usable = row_merge_is_index_usable(readview, index->trx_id);
…
ha_innobase::index_read();
// 判断index_usable属性,此时为false,返回上层表定义修改,查询失败
if (!prebuilt->index_usable)
return HA_ERR_TABLE_DEF_CHANGED;
mysql> select * from t1 where b = 1;
ERROR 1412 (HY000): Table definition has changed, please retry transaction
create table t1 (a int primary key, b int)engine=innodb; insert into t1 values (1,1),(2,2),(3,3),(4,4);
alter table t1 add index idx_t1_b(b);
sql_table.cc::mysql_alter_table();
// 1. 判断当前DDL操作是否可以Inplace进行
check_if_supported_inplace_alter();
…
// 2. 开始进行Online创建的前期准备工作
prepare_inplace_alter_table();
…
// 修改表的数据字典信息
prepare_inplace_alter_table_dict();
…
// 等待InnoDB所有的后台线程,停止操作此表
dict_stats_wait_bg_to_stop_using_tables();
…
// Online Add Index区别与Inplace Add Index的关键
// 在Online操作时,原表同时可以读写,因此需要
// 将此过程中的修改操作记录到row log之中
row0log.cc::row_log_allocate();
row_log_t* log = (row_log_t*)&buf[2 * srv_sort_buf_size];
// 标识当前索引状态为Online创建,那么此索引上的
// DML操作会被写入Row Log,而不在索引上进行更新
dict_index_set_online_status(index, ONLINE_INDEX_CREATION);
…
// 3. 开始进行真正的Online Add Index的操作(最重要的流程)
inplace_alter_table();
// 此函数的操作,前部分与Inplace Add Index基本一致
// 读取聚簇索引、排序、并插入到新建索引中
// 最大的不同在于,当插入完成之后,Online Add Index
// 还需要将row log中的记录变化,更新到新建索引中
row0merge.cc::row_merge_build_index();
…
// 在聚簇索引读取、排序、插入新建索引的操作结束之后
// 进入Online与Inplace真正的不同之处,也是Online操作
// 的精髓部分——将这个过程中产生的Row Log重用
row0log.cc::row_log_apply();
// 暂时将新建索引整个索引树完全锁住
// 注意:只是暂时性锁住,并不是在整个重用Row Log的
// 过程中一直加锁(防止加锁时间过长的优化,如何优化?)
rw_lock_x_lock(dict_index_get_lock(new_index));
…
// InnoDB Online操作最重要的处理流程
// 将Online Copy Table中,记录的Row Log重放到新建索引上
// 重放Row Log的算法如下:
// 1. Row Log中记录的是Online创建索引期间,原表上的DML操作
// 这些操作包括:ROW_OP_INSERT;ROW_OP_DELETE_MARK; …
// 2. Row Log以Block的方式存储,若DML较多,那么Row Logs可能
// 会占用多个Blocks。row_log_t结构中包含两个指针:head与tail
// head指针用于读取Row Log,tail指针用于追加写新的Row Log;
// 3.在重用Row Log时,算法遵循一个原则:尽量减少索引树加锁
// 的时间(索引树加X锁,也意味着表上禁止了新的DML操作)
// 索引树需要加锁的场景:
// (一) 在重用Row Log跨越新的Block时,需要短暂加锁;
// (二) 若应用的Row Log Block是最后一个Block,那么一直加锁
// 应用最后一个Block,由于禁止了新的DML操作,因此此
// Block应用完毕,新索引记录与聚簇索引达到一致状态,
// 重用阶段结束;
// (三) 在应用中间Row Log Block上的row log时,无需加锁,新的
// DML操作仍旧可以进行,产生的row log记录到最后一个
// Row Log Block之上;
// 4. 如果是创建Unique索引,那么在应用Row Log时,可能会出现
// 违反唯一性约束的情况,这些情况会被记录到
// row_merge_dup_t结构之中
row_log_apply_ops(trx, index, &dup);
row_log_apply_op();
row_log_apply_op_low();
…
// 将New Index的Online row log设置为NULL,
// 标识New Index的数据已经与聚簇索引完全一致
// 在此之后,新的DML操作,无需记录Row Log
dict_index_set_online_status();
index->online_status = ONLINE_INDEX_COMPLETE;
index->online_log = NULL;
rw_lock_x_unlock(dict_index_get_block(new_index));
row_log_free();
…
// 4. Online Add Index的最后步骤,做一些后续收尾工作
commit_inplace_alter_table();
…
create table t1 (a int primary key, b int, c char(250))engine=innodb;
insert into t1(b,c) values (1,'aaaaaaa');
// 保证数据量够多
insert into t1(b,c) select b,c from t1;
insert into t1(b,c) select b,c from t1;
insert into t1(b,c) select b,c from t1;
…
// max(a) = 196591
select max(a) from t1;
// b中同样没有相同项
update t1 set b = a;
session 1 session 2
alter table t1 add unique index idx_t1_b(b);
insert into t1(b,c) values (196592,'b');
// 此update,会产生b=196589的重复项
update t1 set b=196589 where a=196582;
delete from t1 where a = 262127;
mysql> show create table t1;
+——-+————————————————– | Table | Create Table +——-+————————————————– | t1 | CREATE TABLE `t1` ( `a` int(11) NOT NULL AUTO_INCREMENT, `b` int(11) DEFAULT NULL, `c` char(250) DEFAULT NULL, PRIMARY KEY (`a`), UNIQUE KEY `idx_t1_b` (`b`) ) ENGINE=InnoDB AUTO_INCREMENT=262129 DEFAULT CHARSET=gbk | +——-+————————————————– mysql> select * from t1 where a in (196582,196589); +——–+——–+———+ | a | b | c | +——–+——–+———+ | 196582 | 196589 | aaaaaaa | | 196589 | 196589 | aaaaaaa | +——–+——–+———+ 2 rows in set (0.04 sec)
机械节能产品生产企业官网模板...
大气智能家居家具装修装饰类企业通用网站模板...
礼品公司网站模板
宽屏简约大气婚纱摄影影楼模板...
蓝白WAP手机综合医院类整站源码(独立后台)...苏ICP备2024110244号-2 苏公网安备32050702011978号 增值电信业务经营许可证编号:苏B2-20251499 | Copyright 2018 - 2025 源码网商城 (www.ymwmall.com) 版权所有