在线对大表进行表结构修改,会有几种做法
####
1、直接在主库操作,然后同步到从库操作(影响主性能;主从延迟影响比较大;5.6以前版本还会影响写,5.6以后的版本DDL影响写的操作可以见官方文档;操作简单)
2、先将从库升级,然后再升级主库 (影响主性能;如果不影响写的操作,可避免主从延迟;)
3、先升级从库,再进行主从切换,再升级老主库(不影响主性能;如果不影响写的DDL,可避免主从延迟;切换会影响短时间写入;对切换工具要求比较高)
4、用pt-online-schmea-change工具对在线数据表做表结构变更(环境依赖-不能有触发器,需要有主键,最好也不要有外键;可避免主从延迟;主库性能影响较小;可能出现死锁)
关于这几种方法,在不同的情况下都有可能用到,对于工具,还有其它的在线修改表结构的工具,但实现基本上与pt工具差不多。这里主要还原下使用pt-online-schmea-change工具遇到的死锁问题:
线上报的死锁错误信息如下:
####
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
后面都是线下复现出来的:
####
*** (1) TRANSACTION:
TRANSACTION 3425, ACTIVE 7 sec fetching rows
mysql tables in use 2, locked 2
LOCK WAIT 5 lock struct(s), heap size 1184, 5 row lock(s), undo log entries 4
MySQL thread id 249896, OS thread handle 0x7f61e5425700, query id 444506 localhost root Sending data
insert into t1_new(id,name) select * from t1 where id >=1 and id<=10 lock in share mode
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 18 page no 3 n bits 72 index `PRIMARY` of table `triggerdb`.`t1` trx id 3425 lock mode S locks rec but not gap waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 8000000b; asc ;;
1: len 6; hex 000000000d5c; asc \;;
2: len 7; hex c40000002d0110; asc - ;;
3: len 8; hex 6664736673667366; asc fdsfsfsf;;
*** (2) TRANSACTION:
TRANSACTION 3420, ACTIVE 10 sec setting auto-inc lock
mysql tables in use 2, locked 2
4 lock struct(s), heap size 1184, 1 row lock(s), undo log entries 2
MySQL thread id 249548, OS thread handle 0x7f61e54a7700, query id 444507 localhost root update
replace into t1_new (id,name)values(NEW.id,NEW.name)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 18 page no 3 n bits 72 index `PRIMARY` of table `triggerdb`.`t1` trx id 3420 lock_mode X locks rec but not gap
Record lock, heap no 6 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 8000000b; asc ;;
1: len 6; hex 000000000d5c; asc \;;
2: len 7; hex c40000002d0110; asc - ;;
3: len 8; hex 6664736673667366; asc fdsfsfsf;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
TABLE LOCK table `triggerdb`.`t1_new` trx id 3420 lock mode AUTO-INC waiting
*** WE ROLL BACK TRANSACTION (2)
从上面的信息可以看出:
thread id 249896 等待t1表记录所在S锁,非gap,(持有表t1_new的auto-inc锁)
thread id 249548 等待t1_new表的auto-inc锁,持有表t1的X锁,非gap
两个事务相互在等待锁资源,导致了死锁。
如下是具体复现过程:
####
CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8
CREATE TABLE `t1_new` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
mysql> select * from t1;
+----+------------+
| id | name |
+----+------------+
| 1 | aaaaaa |
| 2 | bbbbbbb |
| 4 | cccccccccc |
| 10 | ddddddddd |
+----+------------+
#触发器
CREATE DEFINER=`root`@`localhost` trigger update_t1 after insert on t1 for each row begin DECLARE a int; set a=sleep(10);replace into t1_new (id,name)values(NEW.id,NEW.name); end
session1:
####
begin;
insert into t1(id,name)values(3,'fdsfsfsf');--因为触发器中有sleep(10),这里等待
session2:
####
begin;
insert into t1_new(id,name) select * from t1 where id >=1 and id<=10 lock in share mode;
-- 这里卡住,被session1阻塞,等待S锁
10秒过后,执行replace into t1_new (id,name)values(NEW.id,NEW.name)这条语句时,发现被session2阻塞,需要获取auto-inc锁,两个session相互等待锁资源,发生死锁,根据权限将session1回滚。
后话:
触发器在mysql中还是要小心使用,一会影响性能;二有些版本还会触发bug,还会出现类似死锁,等其它不可预期的问题;三维护困难。所以我们是坚决不使用触发器。
文章最后更新时间:
2017年06月13日 12:02:45