pt-online-schema-change中update触发器的bug
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的更多相关文章
- SQL server触发器中 update insert delete 分别给写个例子被。
SQL server触发器中 update insert delete 分别给写个例子以及解释下例子的作用和意思被, 万分感谢!!!! 主要想知道下各个语句的书写规范. INSERT: 表1 (ID, ...
- AppBoxFuture(四). 随需而变-Online Schema Change
需求变更是信息化过程中的家常便饭,而在变更过程中如何尽可能小的影响在线业务是比较头疼的事情.举个车联网监控的例子:原终端设备上传车辆的经纬度数据,新的终端设备支持同时上传速度数据,而旧的车辆状态表 ...
- SQL Server 中的触发器(trigger)
SQL Server 触发器 触发器是一种特殊类型的存储过程,它不同于之前的我们介绍的存储过程.触发器主要是通过事件进行触发被自动调用执行的.而存储过程可以通过存储过程的名称被调用. Ø 什么是触发器 ...
- sql update 触发器 可获得被update的行的信息
类型:转载 sql update 触发器 可获得被update的行的信息,需要的朋友可以参考下. 复制代码 代码如下: create trigger TgName on tb for update ...
- 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 ...
- SQLServer之创建DML AFTER UPDATE触发器
DML AFTER UPDATE触发器创建原理 触发器触发时,系统自动在内存中创建deleted表或inserted表,inserted表临时保存了插入或更新后的记录行,deleted表临时保存了删除 ...
- Sqlserver中的触发器
一 什么是触发器 1.1 触发器的概念 触发器(trigger)是SQL server来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程,它的执行是由事件来触发,当对一个表进行操作( ...
- Oracle中创建触发器示例及注意事项
1.oracle 中创建触发器示例 CREATE TABLE "CONCEPT"."FREQUENCYMODIFYLOG" ( "FREQUENCYI ...
- Unity3D中Update()与FixedUpdate()的区别
Unity3D中Update()与FixedUpdate()的区别是什么呢?从字面上理解,它们都是在更新时会被调用,并且会循环的调用.但是Update会在每次渲染新的一帧时,被调用.而FixedUpd ...
随机推荐
- Android指纹解锁
Android6.0及以上系统支持指纹识别解锁功能:项目中用到,特此抽离出来,备忘. 功能是这样的:在用户将app切换到后台运行(超过一定的时长,比方说30秒),再进入程序中的时候就会弹出指纹识别的界 ...
- Windows2012R2备用域控搭建
Windows2012R2备用域控搭建 前置操作 域控主域控的主dns:自己的ip,备dns:备域控的ip备域控的主dns:自己的ip,备dns:主域控的ip 客户端主dns:主域控的ip,备dns: ...
- SQL Server 2014 新特性——内存数据库
SQL Server 2014 新特性——内存数据库 目录 SQL Server 2014 新特性——内存数据库 简介: 设计目的和原因: 专业名词 In-Memory OLTP不同之处 内存优化表 ...
- Photoshop将普通照片快速制作二次元漫画风格效果
今天为大家分享Photoshop将普通照片快速制作二次元漫画风格效果,教程很不错,对于喜欢漫画的朋友可以参考本文,希望能对大家有所帮助! 一提到日本动画电影,大家第一印象肯定是宫崎骏,但是日本除了宫崎 ...
- 深入.NET平台和C#编程总结大全
对于初学者的你,等到你把这个看完之后就更清楚地认知.NET和C#编程了,好了废话不多说,开始吧! ...
- C#项目中文件的具体含义
1.Bin 目录 用来存放编译的结果,bin是二进制binary的英文缩写,因为最初C编译的程序文件都是二进制文件,它有Debug和Release两个版本,分别对应的文件夹为bin/Debug和bin ...
- [转载]强制不使用“兼容性视图”的HTML代码
在IE8浏览器以后版本,都有一个"兼容性视图",让不少新技术无法使用.那么如何禁止浏览器自动选择"兼容性视图",强制IE以最高级别的可用模式显示内容呢?下面就介 ...
- BPM配置故事之案例7-公式计算
行政主管发来邮件.要求物资明细表增加"单价""总价"."单价"由其审批时填写,"总价"根据"单价"与 ...
- Atitit.attilax软件研发与项目管理之道
Atitit.attilax软件研发与项目管理之道 1. 前言4 2. 鸣谢4 3. Genesis 创世记4 4. 软件发展史4 5. 箴言4 6. 使徒行传 4 7. attilax书 4 8. ...
- join Linq
List<Publisher> Publishers = new List<Publisher>(); Publisher publish1 = new Publisher() ...