pt-online-schema-change在对表进行表结构变更时,会创建三个触发器。

如下文测试案例中的t2表,表结构如下:

mysql> show create table t2\G
*************************** 1. row ***************************
Table: t2
Create Table: CREATE TABLE `t2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.07 sec)

只有一个自增列字段id。

创建的触发器如下:

CREATE TRIGGER `pt_osc_test_t2_del` AFTER DELETE ON `test`.`t2` FOR EACH ROW DELETE IGNORE FROM `test`.`__t2_new` WHERE `test`.`__t2_new`.`id` <=> OLD.`id`
CREATE TRIGGER `pt_osc_test_t2_upd` AFTER UPDATE ON `test`.`t2` FOR EACH ROW REPLACE INTO `test`.`__t2_new` (`id`) VALUES (NEW.`id`)
CREATE TRIGGER `pt_osc_test_t2_ins` AFTER INSERT ON `test`.`t2` FOR EACH ROW REPLACE INTO `test`.`__t2_new` (`id`) VALUES (NEW.`id`)

DELETE触发器和INSERT触发器逻辑上没有任何问题。

但对于UPDATE触发器来说,如果某条记录已经拷贝到中间表中,此时,有针对该记录的UPDATE操作,且修改的是主键,此时,针对中间表触发的“REPLACE INTO `test`.`__t2_new` (`id`) VALUES (NEW.`id`)”操作只会插入一条新的记录,而不会删除原来的记录。

下面重现该场景

创建触发器构造测试数据

delimiter //
create procedure p1()
begin
declare v1 int default 1;
set autocommit=0;
while v1 <=10000000 do
insert into test.t2(id) values(null);
set v1=v1+1;
if v1%1000 =0 then
commit;
end if;
end while;
end //
delimiter ;
call p1;

此时,会生成1千万的数据

mysql> select count(*),min(id),max(id) from t2;
+----------+---------+----------+
| count(*) | min(id) | max(id) |
+----------+---------+----------+
| 10000000 | 1 | 10000000 |
+----------+---------+----------+
1 row in set (4.29 sec)

利用pt-online-schema-change对t2表添加一列

# pt-online-schema-change --execute --alter "ADD COLUMN c1 DATETIME" --print D=test,t=t2

No slaves found.  See --recursion-method if host localhost.localdomain has slaves.
Not checking slave lag because no slaves were found and --check-slave-lag was not specified.
Operation, tries, wait:
analyze_table, ,
copy_rows, , 0.25
create_triggers, ,
drop_triggers, ,
swap_tables, ,
update_foreign_keys, ,
Altering `test`.`t2`...
Creating new table...
CREATE TABLE `test`.`___t2_new` (
`id` int() NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT= DEFAULT CHARSET=utf8
Created new table test.___t2_new OK.
Altering new table...
ALTER TABLE `test`.`___t2_new` ADD COLUMN c1 DATETIME
Altered `test`.`___t2_new` OK.
--23T20:: Creating triggers...
CREATE TRIGGER `pt_osc_test_t2_del` AFTER DELETE ON `test`.`t2` FOR EACH ROW DELETE IGNORE FROM `test`.`___t2_new` WHERE `test`.`___t
2_new`.`id` <=> OLD.`id`CREATE TRIGGER `pt_osc_test_t2_upd` AFTER UPDATE ON `test`.`t2` FOR EACH ROW REPLACE INTO `test`.`___t2_new` (`id`) VALUES (NEW.`id`)
CREATE TRIGGER `pt_osc_test_t2_ins` AFTER INSERT ON `test`.`t2` FOR EACH ROW REPLACE INTO `test`.`___t2_new` (`id`) VALUES (NEW.`id`)
--23T20:: Created triggers OK.
--23T20:: Copying approximately rows...
INSERT LOW_PRIORITY IGNORE INTO `test`.`___t2_new` (`id`) SELECT `id` FROM `test`.`t2` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= ?)) AND
((`id` <= ?)) LOCK IN SHARE MODE /*pt-online-schema-change 2456 copy nibble*/SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `test`.`t2` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= ?)) ORDER BY `id` LIMIT ?, /*next chun
k boundary*/
Copying `test`.`t2`: % : remain
Copying `test`.`t2`: 52% 00:54 remain
Copying `test`.`t2`: % : remain
--23T20:: Copied rows OK.
--23T20:: Analyzing new table...
--23T20:: Swapping tables...
RENAME TABLE `test`.`t2` TO `test`.`_t2_old`, `test`.`___t2_new` TO `test`.`t2`
--23T20:: Swapped original and new tables OK.
--23T20:: Dropping old table...
DROP TABLE IF EXISTS `test`.`_t2_old`
--23T20:: Dropped old table `test`.`_t2_old` OK.
--23T20:: Dropping triggers...
DROP TRIGGER IF EXISTS `test`.`pt_osc_test_t2_del`;
DROP TRIGGER IF EXISTS `test`.`pt_osc_test_t2_upd`;
DROP TRIGGER IF EXISTS `test`.`pt_osc_test_t2_ins`;
--23T20:: Dropped triggers OK.
Successfully altered `test`.`t2`.

当输出到上述红色信息时,打开另外一个终端窗口,执行如下命令

 mysql -e 'update test.t2 set id=-1 where id=1'
mysql -e 'update test.t2 set id=-2 where id=2'
mysql -e 'update test.t2 set id=-3 where id=3'
mysql -e 'update test.t2 set id=-4 where id=4'
mysql -e 'update test.t2 set id=-5 where id=5'
mysql -e 'update test.t2 set id=-6 where id=6'
mysql -e 'update test.t2 set id=-7 where id=7'
mysql -e 'update test.t2 set id=-8 where id=8'
mysql -e 'update test.t2 set id=-9 where id=9'
mysql -e 'update test.t2 set id=-10 where id=10'

查看t2表修改完表结构后的数据情况

mysql> select count(*),min(id),max(id) from t2;
+----------+---------+----------+
| count(*) | min(id) | max(id) |
+----------+---------+----------+
| 10000010 | -10 | 10000000 |
+----------+---------+----------+
1 row in set (3.00 sec) mysql> select * from t2 order by id limit 20;
+-----+------+
| id | c1 |
+-----+------+
| -10 | NULL |
| -9 | NULL |
| -8 | NULL |
| -7 | NULL |
| -6 | NULL |
| -5 | NULL |
| -4 | NULL |
| -3 | NULL |
| -2 | NULL |
| -1 | NULL |
| 1 | NULL |
| 2 | NULL |
| 3 | NULL |
| 4 | NULL |
| 5 | NULL |
| 6 | NULL |
| 7 | NULL |
| 8 | NULL |
| 9 | NULL |
| 10 | NULL |
+-----+------+
20 rows in set (0.08 sec)

可见,在执行pt-online-schema-change命令的过程中,针对原表执行的update操作并没有理所当然的反应到中间表上。

总结

1. 上述测试使用的pt-online-schema-change是2.2.19版本。

2. 欲进行表结构变更的表中必须存在主键或者唯一索引。

体现在以下方面:

1> 针对DELETE触发器

CREATE TRIGGER `pt_osc_test_t2_del` AFTER DELETE ON `test`.`t2` FOR EACH ROW DELETE IGNORE FROM `test`.`_t2_new` WHERE `test`.`_t2_new`.`id` <=> OLD.`id`

DELETE触发器是基于主键或者唯一索引进行删除的。如果id是普通索引,则原表中可能只有一行记录的删除(譬如delete from t where id=1 and name='victor'),导致中间表中所有id为1的记录的删除。

2> 针对UPDATE触发器

如果原表中不存在主键或者唯一索引,则replace操作会直接插入,而不会进行替换。

mysql> create table t3(id int,name varchar(10));
Query OK, 0 rows affected (0.08 sec) mysql> insert into t3 values(1,'a');
Query OK, 1 row affected (0.05 sec) mysql> replace into t3 values(1,'b');
Query OK, 1 row affected (0.06 sec) mysql> select * from t3;
+------+------+
| id | name |
+------+------+
| 1 | a |
| 1 | b |
+------+------+
2 rows in set (0.00 sec) mysql> alter table t3 modify id int primary key;
ERROR 1062 (23000): Duplicate entry '' for key 'PRIMARY'
mysql> delete from t3 where id=1 and name='b';
Query OK, 1 row affected (0.07 sec) mysql> alter table t3 modify id int primary key;
Query OK, 0 rows affected (0.24 sec)
Records: 0 Duplicates: 0 Warnings: 0 mysql> select * from t3;
+----+------+
| id | name |
+----+------+
| 1 | a |
+----+------+
1 row in set (0.00 sec) mysql> replace into t3 values(1,'b');
Query OK, 2 rows affected (0.01 sec) mysql> select * from t3;
+----+------+
| id | name |
+----+------+
| 1 | b |
+----+------+
1 row in set (0.01 sec)

3. 即便欲进行表结构变更的表中存在主键或者唯一索引,如果在利用pt-online-schema-change进行online ddl过程中,有针对主键的更新操作,则会导致记录的新增。这点需引起注意。

pt-online-schema-change中update触发器的bug的更多相关文章

  1. SQL server触发器中 update insert delete 分别给写个例子被。

    SQL server触发器中 update insert delete 分别给写个例子以及解释下例子的作用和意思被, 万分感谢!!!! 主要想知道下各个语句的书写规范. INSERT: 表1 (ID, ...

  2. AppBoxFuture(四). 随需而变-Online Schema Change

      需求变更是信息化过程中的家常便饭,而在变更过程中如何尽可能小的影响在线业务是比较头疼的事情.举个车联网监控的例子:原终端设备上传车辆的经纬度数据,新的终端设备支持同时上传速度数据,而旧的车辆状态表 ...

  3. SQL Server 中的触发器(trigger)

    SQL Server 触发器 触发器是一种特殊类型的存储过程,它不同于之前的我们介绍的存储过程.触发器主要是通过事件进行触发被自动调用执行的.而存储过程可以通过存储过程的名称被调用. Ø 什么是触发器 ...

  4. sql update 触发器 可获得被update的行的信息

    类型:转载   sql update 触发器 可获得被update的行的信息,需要的朋友可以参考下. 复制代码 代码如下: create trigger TgName on tb for update ...

  5. Online Schema Change for MySQL

    It is great to be able to build small utilities on top of an excellent RDBMS. Thank you MySQL. This ...

  6. SQLServer之创建DML AFTER UPDATE触发器

    DML AFTER UPDATE触发器创建原理 触发器触发时,系统自动在内存中创建deleted表或inserted表,inserted表临时保存了插入或更新后的记录行,deleted表临时保存了删除 ...

  7. Sqlserver中的触发器

    一 什么是触发器 1.1  触发器的概念   触发器(trigger)是SQL server来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程,它的执行是由事件来触发,当对一个表进行操作(  ...

  8. Oracle中创建触发器示例及注意事项

    1.oracle 中创建触发器示例 CREATE TABLE "CONCEPT"."FREQUENCYMODIFYLOG" ( "FREQUENCYI ...

  9. Unity3D中Update()与FixedUpdate()的区别

    Unity3D中Update()与FixedUpdate()的区别是什么呢?从字面上理解,它们都是在更新时会被调用,并且会循环的调用.但是Update会在每次渲染新的一帧时,被调用.而FixedUpd ...

随机推荐

  1. 自定义基于 VLC 的视频播放器

    前言(蛋疼的背景故事) 前段时间,接了一个小项目,有个需求是要在系统待机一段时间以后,循环播放 MV(类似于 Windows 系统的屏幕保护). 听到这个需求,我首先想到的是 MediaPlayer ...

  2. mysql 7下载安装及问题解决

    mysql 7安装及问题解决 一.mysql下载 下载地址:https://www.mysql.com/downloads/ Community (GPL) Downloads MySQL Commu ...

  3. ASP.NET MVC5+EF6+EasyUI 后台管理系统(76)-微信公众平台开发-网页授权

    前言 网页授权是:应用或者网站请求你用你的微信帐号登录,同意之后第三方应用可以获取你的个人信息 网上说了一大堆参数,实际很难理解和猜透,我们以实际的代码来演示比较通俗易懂 配置 实现之前我们必须配置用 ...

  4. iptables

    一.在服务器上打开 22.80.9011端口: iptables -A INPUT -p tcp --dport 9011 -j ACCEPT iptables -A OUTPUT -p tcp -- ...

  5. FreeMarker:怎么使用

    第一个FreeMarker程序 1. 建立一个普通的java项目:testFreeMarker 2. 引入freemarker.jar包 3. 在项目目录下建立模板目录:templates 4. 在t ...

  6. Canvas讲解

    1.Canvas是什么? 简单地说canvas是画布,可以进行画任何的线.图形.填充等一系列的操作,而且操作的画图就是js, 提供简单的二维矢量绘图. 2.步骤: <canvas id=&quo ...

  7. AFNetworking图片上传

    //上传图片 -(void)upLoadImage:(UIImage *)upImage { //创建管理 AFHTTPRequestOperationManager *manager = [AFHT ...

  8. git添加GitHub远程库

    已经在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作 首先,登陆GitHub, ...

  9. linux之查看系统命令

    cpu信息 1.查看逻辑cpu核数 # cat /proc/cpuinfo| grep "processor"| wc -l 2.查看物理cpu个数 # cat /proc/cpu ...

  10. BZOJ 4196: [Noi2015]软件包管理器 [树链剖分 DFS序]

    4196: [Noi2015]软件包管理器 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1352  Solved: 780[Submit][Stat ...