前言
一天,一位许久未见的澳同学,一见面先不是打招呼,直接给我当面一问 澳同学问我:小涛小涛,我这一个表有1T的数据,这时我想给这个表加一个列,如果才能最大程度不影响业务呢? 此刻,我陷入了沉思,赶紧去学习学习
一、分类
在MySQL5.6版本之后,对于部分alter table ,加入新的执行算法,可以进行DDL时,“并行”现有业务(DML操作)。 可以通过aLter table 时添加 ALGORITHM参数控制使用算法。
alter table t1 add a int ALGORITHM=?;
目前可以支持的主流算法有三种: COPY —— MySQL 5.6之前非Online,都是执行这种算法 INPLACE —— MySQL 5.6出现的 INSTANT —— MySQL 8.0.12出现的
这时就有疑问了,这么多算法头都大了 别急,小涛同学这就来通俗易懂的解答一下这几种算法: 我们知道,把澳同学关进冰箱需要三步:打开、放入、关门。
而DDL操作,在执行时,不管何种算法,都会经历三个阶段:准备阶段【prepare】、执行阶段【DDL】、提交阶段【commit】 。不同之处是在三个阶段中分别作了不同的处理,下面咱们来好好聊聊。
二、Copy
一听名词就秒懂,单纯的拷贝忍者。
名词解释:指DDL时,会生成(临时)新表,将原表数据逐行拷贝到新表中,在此期间会阻塞DML,offline(离线的而非在线的)
例如执行此语句:
alter table t1 add age a int;
三个阶段:准备、执行、提交。
准备:
1、对表加元数据共享锁,读取frm元数据(此时DDL不能并行,DML可以并行)
2、共享锁升级为排他锁;(此时DDL、DML都不能并行)
3、在Server层通过create like语句,创建临时表,Engine层也生成对应ibd、frm文件(8.0之后没.frm文件)
执行:
1、修改临时表元数据(加列)
2、拷贝原表数据到临时表【最耗时,1T的数据一行一行copy】
3、删除原表及文件
4、重命令临时表及文件
提交:
1、提交事务,释放锁
澳同学:这个好理解,但现在却不实用吧,因为在整个流程期间,都是加上锁的,此时既不能写入数据,也不能查询数据,容易造成数据库堵塞 卑微涛:嗯嗯,真聪明 澳同学:那怎么个Oline DDL啊,你是不是在忽悠我啊🤔 卑微涛:别急,这不是先易后难吗,下面介绍下一种算法
三、Inplace
在MySQL5.6 出现的新算法,无需拷贝全表数据到新表,但可能还是需要IN-PLACE方式(原地,无需生成新的临时表)重建整表。这种情况下,在DDL的初始准备和最后结束两个阶段时通常需要加排他MDL锁(metadata lock,元数据锁),除此外,DDL期间不会阻塞DML。
准备:
1、对表加元数据共享升级锁,并升级为排他锁;(此时DML不能并行)
2、判断使用inplace算法
3、判断语句是“rebuild table” 还是 “no-rebuild”,rebuild 在原表所在的路径下创建.frm和.ibd临时中转文件;
【在引擎层克隆,而不是像copy那样,在server层创建(create like)】
注意:加列的操作是需要rebuild table的
【rebuild table】
++++
no-rebuild的情况:
除创建二级索引外只创建.frm文件,其中添加二级索引操作最为特殊,该操作属于no-rebuild不会生成.ibd,
但实际上对.ibd文件却做了修改,该操作会在参数tmpdir指定路径下生成临时文件,用于存储索引排序结果,然后再合并到.ibd文件中
++++
4、申请row log空间,用于存放DDL执行阶段产生的DML操作。(no-rebuild不需要)【在innodb_sort_buffer块中】
执行: (online)
1、释放排他锁,保留元数据共享升级锁;(此时DML可以并行)
2、扫描原表主键以及二级索引的所有数据页,生成 B+ 树,存储到临时文件中;【在引擎层扫描,最耗时】
3、将所有对原表的DML操作记录在日志文件row log中,并回放部分row_log。
提交阶段:
1、升级元数据共享升级锁,产生排他锁锁表;(此时DML不能并行了)
2、重做row log中的内容;(no-rebuild不需要)
3、重命名原表文件,将临时文件改名为原表文件名,删除原表文件;
4、提交事务,变更完成。
注意:
- 在DDL期间产生的数据,会按照正常操作一样,写入原表,记redolog、undolog、binlog,并同步到从库去执行,只是额外会记录在row log中,并且写入row log的操作本身也会记录redolog
- 而在提交阶段才进行row log重做,此阶段会锁表,此时主库(新表空间+row log)和从库(表空间)数据是一致的,在主库DDL操作执行完成并提交,这个DDL才会写入binlog传到从库执行,在从库执行该DDL时,这个DDL对于从库本地来讲仍然是online的,也就是在从库本地直接写入数据是不会阻塞的,也会像主库一样产生row log。
- 但是对于主库同步过来DML,此时会被阻塞,是offline的,DDL是排他锁的在复制线程中也是一样,所以不只会阻塞该表,而是后续所有从主库同步过来的操作(主要是在复制线程并行时会排他,同一时间只有他自己在执行)。所以大表的DDL操作,会造成同步延迟。
此时,澳同学又提问了:不错,那为什么是Online呢? 卑微涛:虽然三个阶段(准备、执行、提交)中,有两个阶段(准备、提交)都无法进行CRUD,但实际上,整个DDL中执行阶段时间占比最长,例如30分钟的DDL,准备+提交阶段只占用1分钟的时间,剩下的29分钟都在执行,那么对于业务层来说,绝大部分时间都是能正常访问的,所以就做到了Oline DDL了。 澳同学:诶,你这不是还要1分钟会堵塞嘛,我就是想在那一分钟插入大批量数据呢,你这瓜不熟啊🤪 卑微涛:行行行,那下面介绍下一种算法
四、Instant
只需修改数据字典中的元数据,无需拷贝数据也无需重建整表,同样,也无需加排他MDL锁,原表数据也不受影响。整个DDL过程几乎是瞬间完成的,也不会阻塞DML。
这个新特性是8.0.12引入的(腾讯DBA团队贡献)。执行DDL操作时,ALGORITHM选项可以不指定,这时候MySQL按照INSTANT、INPLACE、COPY的顺序自动选择合适的模式。也可以指定ALGORITHM=DEFAULT,也是同样的效果。如果指定了ALGORITHM选项,但不支持的话,会直接报错。
当采用COPY模式时,这时表里任何的修改数据操作,DDL都会被阻塞。COPY模式下会生成临时新表,操作完成后原表会被删除,新表被重命名为原表名。当DDL开始后,原表上仅能只读,其他的DML操作也都会被阻塞。COPY过程中,唯一会阻塞只读的时机是在清理旧表结构和表定义缓存时。
MySQL 8.0.12 才提出的新算法,目前只支持添加列等少量操作(还不是太成熟,企业中一般都是5.6、5.7版本),利用 8.0 新的表结构设计,可以直接修改表的 metadata 数据,省掉了 rebuild 的过程,极大的缩短了 DDL 语句的执行时间。
五、一些补充
澳同学:那支持 Inplace算法的 DDL 一定是 Online 的吗? 1、从概念上来说,INPLACE 和 Online 是两个不同维度的事情; 2、Ccopy 和Inplace指的是DDL内部的执行算法,可以理解成:COPY 是在 Server层(服务层)的操作,INPLACE 是在 InnoDB层(存储层)的操作; 3、其实,对于用户来说,关心 Online 与否,通常只与一个问题有关:是否允许并发 DML; 4、用一句哲学经典术语:Copy 算法执行的 DDL肯定不是 Online 的,Inplace 算法执行的 DDL 不一定是 Online 的。
六、总结
COPY —— 5.6之前默认适应这种方法,全程无法并行DML INPLACE —— MySQL 5.6出现的,在开始和提交的短租时间里,不能并行DML INSTANT —— MySQL 8.0.12出现的,唯一会阻塞只读的时机是在清理旧表结构和表定义缓存时。 以上纯属节目效果,澳同学可是我的铁哥们,一起毕业了😄 这次,也是真的毕业了,大学四年时间过得真快 "所以,别回头,迈步离开吧。 我大概会怀念林荫道斑驳的树影, 怀念撒在南区篮球场的斜阳, 怀念夜里的灯, 雨里的伞, 清晨的雾, 我大概会怀念每一声笑和每一滴泪。 我原想收获一缕春风,你们却给了我整个春天; 我原想撷取一枚红叶,你们却给了我整片枫林"? 我是励志成为一名优秀DBA的小涛同学,有疑问的小伙伴欢迎在评论区讨论,咱们一起努力,冲冲冲!咱们下篇博文再见了~
|