Mysql在线DDL操作
在Mysql5.6版本之前,InnoDB引擎的Mysql DDL操作的基本流程是:
- 按照原表的表结构和DDL语句,新建一个不可见的临时表;
- 在原表上加MDL写锁,阻塞所有需要获取MDL读锁或者写锁的操作;
- 执行insert into tmp_table select * from ,拷贝原表中的数据到新表;
- 重命名老表,临时表修改为新表tmp_table;
- 删除老表;
- 释放MDL写锁。
上面就是基础的DDL流程,该流程最大的问题是它是完全阻塞的操作,即在整个执行过程中,业务都是不可用的。如果表很大的话,那整个过程要持续很长一段时间。
先说下MDL锁,元数据锁,是一种表级锁,主要是为了阻止DDL,DML等更改表结构操作的并发执行。当我们执行CRUD操作时,都是自动持有MDL读锁的,只有DML,DLL等操作才会持有MDL写锁,注意这里MDL锁是表级别的锁,且不需要使用者加,在insert,update,delete等操作加的都是行级别的写锁,此外还加了MDL读锁。读写锁,写写锁之间是互斥的,读读之间不互斥。也就是说上面进行DDL操作是加了MDL写锁,因此所有操作都不可执行。针对这个问题,Mysql后续推出了Online-DDL。相比之前,其最大的变化是在执行DDL过程中,会将MDL写锁降级为读锁。下面是网上的一张比较好的OnlineDDL过程。
用法:
ALTER TABLE tbl_name ADD PRIMARY KEY (column), ALGORITHM=INPLACE, LOCK=NONE;
目前DBA在操作时通过会通过工具来实现DDL等操作,比如pt-osc,它可以实现在DDL操作的过程中,不必再完全阻塞,业务仍然可以使用,可以进行insert,update和delete 操作,此外在原表中创建触发器,自动实现执行DDL过程中的增量数据。
它基本流程是:
1、创建临时新表;
2、新表执行DDL操作;
3、创建触发器,用于记录从拷贝数据开始之后,对源数据表继续进行数据修改的操作记录下来,用于数据拷贝结束后,执行这些操作,保证数据不会丢失。如果表中已经定义了触发器这个工具就不能工作了。
触发器的动作都会转换为replace into
4、拷贝数据(insert into from select).
5、重命名老表,临时表修改为新表tmp_table;
6、删除老表;
通过pt-osc解决了原生执行DDL过程中阻塞的问题,但同时呢,它也有比较大的隐患,就是在数据表数量级较大的时侯,比如上千万,上亿,容易出现死锁。
比如。
事务1,执行update操作。它获取锁的操作:
1、首先获得旧表数据的排他锁(X锁);
2、接着要获取新表的自增锁(auto-inc)锁;
事务2,执行数据拷贝操作 insert into from select ;
1、首先会在新表添加自增锁;
2、其次到旧表获取记录锁;
看到上面的过程,如果事务1和事务2都执行到第2步,两个事务都在等待对方释放已获取的锁,这样就产生了死锁。之所以说在数量级大的数据表容易出现,因为 insert into from select 执行时间较长。
所以,为了尽量避免上述问题,就尽量选择业务较少的时侯执行DDL操作。也可以控制单次insert into的行数,通过使用 --chunk-size,简单点说就是分片。
当然为了完全避免,可以通过完全创建一个新表,然后通过binlog将原数据都同步过来,这样完全独立的不会出错。
参考资料:
mysql MDL读写锁阻塞,以及online ddl造成的“插队”现象
微信分享/微信扫码阅读