今天在线上报出了数据库错误:
SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction
搜索这段错误发现mysql发生了死锁,把等待的事务给回滚了。网上有太多死锁的原理,这里就不说了
现象:
回想这次操作,最近一次大操作 执行了 修改表的字段类型,造成了锁表时间比较长,导致正在执行的事务执行时间过长,照成了死锁。
sql语句是通过公司的sql上线平台上线的,原以为运维会给我们做 热修改操作,结果没有才造成了这次执行失败。原来运维也不可信啊。
innodb存储引擎在DDL时执行操作如下:
1. 按照原始表(original_table)的表结构和ddl语句,新建一个不可见的临时表(temporary_table)
2.在原表上面加上WRITE LOCK,阻塞所有的更新操作(insert、delete、update等操作)
3.执行insert into tmp_table select * from original_table
4.rename original_table和tmp_table,最后drop original_table
5.最后释放掉write lock
网上发现如果要解决ddl的同时可以写入操作,解决办法是 使用第三方工具或者自己实现。
方案:
在mysql 5.7之后增加了一个如果是修改varchar 字段大小通过alter table命令,以in-place的方式修改。
VARCHAR 大小可通过 ALTER TABLE语句进行原地(in place)修改,而无需table-copy。如:ALTER TABLE t1 ALGORITHM=INPLACE, CHANGE COLUMN c1 c1 VARCHAR(255);但存在限制,即只支持0~255字节内的或者255以上字节间的增加,也就是说若从254增到256时不能使用INPLACE
算法,必须使用COPY算法,否侧报错.这是因为0~255内的VARCHAR值需要一个额外的字节来编码,而256以上的VARCHAR值需要两个字节来编码。另外使用INPLACE算法缩小VARCHAR的ALTER TABLE也是不支持的,必须用COPY算法。
例如 :
sql语句:
Create Table: CREATE TABLE `data1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(80) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4652973 DEFAULT CHARSET=utf8
1.对name varchar(80),修改为 name varchar(256)是不行的。
ERROR 1846 (0A000): ALGORITHM=INPLACE is not supported. Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY.
2. 对name varchar(80),修改为 name varchar(79)是不行的。
ERROR 1846 (0A000): ALGORITHM=INPLACE is not supported. Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY.
3.对于name varchar(80),修改为 name varchar(86)是不行的。
ERROR 1846 (0A000): ALGORITHM=INPLACE is not supported. Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY.
修改为100和86 就报错修改为85就没问题 官方文档不是说0-255 只要存储的比特没变就可以吗? 为啥这里不行呢?
其实官方文档是对的! 因为英文没错,但是换成其他字符集的话 存储占位不同。
一个中文字符集占位UTF8 是三个bit
85*3=255 所以修改为85 可以修改为86 就超过了255 了只能通过copy 的方式了
性能:
表里面有 465.2 w 的数据
普通的alter 修改name的长度,因为mysql会根据当前是否满足 是 inplace条件自动执行的,所以要加上强制的 copy才能看出效果
Query OK, 4194307 rows affected (34.49 sec)
执行了 34.49 sec
满足条件的 inplace
Query OK, 0 rows affected (0.01 sec)
只需要 0.01 sec