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 ...
随机推荐
- In-Memory:内存优化表的事务处理
内存优化表(Memory-Optimized Table,简称MOT)使用乐观策略(optimistic approach)实现事务的并发控制,在读取MOT时,使用多行版本化(Multi-Row ve ...
- solr服务中集成IKAnalyzer中文分词器、集成dataimportHandler插件
昨天已经在Tomcat容器中成功的部署了solr全文检索引擎系统的服务:今天来分享一下solr服务在海量数据的网站中是如何实现数据的检索. 在solr服务中集成IKAnalyzer中文分词器的步骤: ...
- python自动化测试(2)-自动化基本技术原理
python自动化测试(2) 自动化基本技术原理 1 概述 在之前的文章里面提到过:做自动化的首要本领就是要会 透过现象看本质 ,落实到实际的IT工作中就是 透过界面看数据. 掌握上面的这样的本领 ...
- 使用CSS3实现一个3D相册
CSS3系列我已经写过两篇文章,感兴趣的同学可以先看一下CSS3初体验之奇技淫巧,CSS3 3D立方体效果-transform也不过如此 第一篇主要列出了一些常用或经典的CSS3技巧和方法:第二篇是一 ...
- Ubuntu 16.10 开启PHP错误提示
两个步骤: 修改php.ini配置文件中的error_reporting 和 display_errors两地方内容: sudo vim /etc/php/7.0/apache2/php.ini er ...
- nodejs进阶(2)—函数模块调用
函数调用 1. 文件内普通函数调用 创建一个js文件命名为2_callFunction.js,其中定义一个函数fun1,向返回对象输出了一段字符串“你好,我是fun1”. //------------ ...
- 利用Oracle RUEI+EM12c进行应用的“端到端”性能诊断
概述 我们知道,影响一个B/S应用性能的因素,粗略地说,有以下几个大的环节: 1. 客户端环节 2. 网络环节(可能包括WAN和LAN) 3. 应用及中间层环节 4. 数据库层环节 能够对各个环节的问 ...
- jQuery之Deferred源码剖析
一.前言 大约在夏季,我们谈过ES6的Promise(详见here),其实在ES6前jQuery早就有了Promise,也就是我们所知道的Deferred对象,宗旨当然也和ES6的Promise一样, ...
- AFNetworking 3.0 源码解读(七)之 AFAutoPurgingImageCache
这篇我们就要介绍AFAutoPurgingImageCache这个类了.这个类给了我们临时管理图片内存的能力. 前言 假如说我们要写一个通用的网络框架,除了必备的请求数据的方法外,必须提供一个下载器来 ...
- ObserverPattern(观察者模式)
import java.util.ArrayList; import java.util.List; /** * 观察者模式 * @author TMAC-J * 牵一发而动全身来形容观察者模式在合适 ...