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. JS调用Android、Ios原生控件

    在上一篇博客中已经和大家聊了,关于JS与Android.Ios原生控件之间相互通信的详细代码实现,今天我们一起聊一下JS调用Android.Ios通信的相同点和不同点,以便帮助我们在进行混合式开发时, ...

  2. C# 发送邮件 附件名称为空

     示例代码: // 1.创建邮件 MailMessage mailMsg = new MailMessage(); mailMsg.To.Add(new MailAddress("test@ ...

  3. 预览github里面的网页或dome

    1.问题所在: 之前把项目提交到github都可以在路径前面加上http://htmlpreview.github.io/?来预览demo,最近发现这种方式预览的时候加载不出来css,js(原因不详) ...

  4. HTML文档声明

    前面的话   HTML文档通常以类型声明开始,该声明将帮助浏览器确定其尝试解析和显示的HTML文档类型.本文将详细介绍文档声明DOCTYPE 特点   文档声明必须是HTML文档的第一行.且顶格显示, ...

  5. Java学习之反射机制及应用场景

    前言: 最近公司正在进行业务组件化进程,其中的路由实现用到了Java的反射机制,既然用到了就想着好好学习总结一下,其实无论是之前的EventBus 2.x版本还是Retrofit.早期的View注解框 ...

  6. 编写自己的PHP MVC框架笔记

    1.MVC MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model).视图(View)和控制器(Controller). ...

  7. 代码的坏味道(18)——依恋情结(Feature Envy)

    坏味道--依恋情结(Feature Envy) 特征 一个函数访问其它对象的数据比访问自己的数据更多. 问题原因 这种气味可能发生在字段移动到数据类之后.如果是这种情况,你可能想将数据类的操作移动到这 ...

  8. 使用po模式读取豆瓣读书最受关注的书籍,取出标题、评分、评论、题材 按评分从小到大排序并输出到txt文件中

    #coding=utf-8from time import sleepimport unittestfrom selenium import webdriverfrom selenium.webdri ...

  9. SharePonit 2010 更改另存为列表模板的语言类型

    从朋友处得来一个列表模板:AccessApplicationSharePoint.stp 将其通过:网站操作----网站设置----列表模板,上传进去.然后去创建列表,发现找不到此模板. 根据多年老司 ...

  10. Pramp mock interview (4th practice): Matrix Spiral Print

    March 16, 2016 Problem statement:Given a 2D array (matrix) named M, print all items of M in a spiral ...