MySQL5.6 Online DDL
简单介绍
ddl包含了copy和inplace方式,对于不支持online的ddl操作采用copy方式。对于copy方式,copy过程中全程锁表,不允许写(rename阶段会禁止读写),比如修改表字段的类型,包括修改长度,对于inplace方式,mysql内部以是否修改记录格式为基准也分为两类,一类需要重建表(重新组织记录,重建过程中会建立临时ibd文件),比如添加索引、添加/删除列、修改列NULL/NOT NULL属性等;另外一类是只需要修改表的元数据,比如删除索引、修改列名、修改列默认值、修改列自增值等。MySQL将这两类方式分别称为rebuild方式和no-rebuild方式。
online ddl主要包括3个阶段,prepare阶段,ddl执行阶段,commit阶段,rebuild方式比no-rebuild方式实质多了一个ddl执行阶段,prepare阶段和commit阶段类似。
prepare阶段
- 创建新的临时frm文件(MySQL server层建的)
- 升级MDL_SHARED_UPGRADABLE到EXCLUSIVE-MDL锁,禁止读写
- 根据alter类型,确定执行方式(copy,online-rebuild,online-norebuild)
- 更新数据字典的内存对象
- 分配row_log对象记录增量
- 生成新的临时ibd文件
此阶段主要是innodb引擎做一些校验,分配空间,初始化等操作,时间不长,对于用户感觉不出来禁止读写
ddl执行阶段
- 降级EXCLUSIVE-MDL锁到MDL_SHARED_UPGRADABLE,允许读写
- 扫描old_table的聚集索引每一条记录rec
- 遍历新表的聚集索引和二级索引,逐一处理
- 根据rec构造对应的索引项
- 将构造索引项插入sort_buffer块
- 将sort_buffer块插入新的索引
- 处理ddl执行过程中产生的增量(仅rebuild类型需要)
commit阶段
- 升级MDL_SHARED_UPGRADABLE到EXCLUSIVE-MDL锁,禁止读写
- 重做最后row_log中最后一部分增量
- 更新innodb的数据字典表
- 提交事务(刷事务的redo日志)
- 修改统计信息
- rename临时idb文件,frm文件
- 变更完成
对于norebuid模式(如删除索引),InnoDB二级索引只需要更新内部视图,并标记这个索引的空间可用,去掉数据库元数据上该索引的定义即可,都是在原表上操作,不需要重建与copy数据,所以速度非常快,基本可以忽略阻塞读写的时间
row_log说明
- Row Log中记录的是Online创建索引期间,原表上的DML操作这些操作包括:ROW_OP_INSERT;ROW_OP_DELETE_MARK等
- Row Log以Block的方式存储,若DML较多,那么Row Logs可能会占用多个Blocks。row_log_t结构中包含两个指针:head与tail,head指针用于读取Row Log,tail指针用于追加写新的Row Log
- 在重用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之上;
源码分析
虽然画了上面的图,但是里面的细节还是有很多的不懂,不明白,还是只能知道大概的流程,对于每一个点都是一个很深的知识点,commit阶段也还没有整理,待补