数据库内核月报 - 2015 / 05-MySQL · 捉虫动态 · 临时表操作导致主备不一致

bug描述

在binlog_format=row模式下,事务中create或drop临时表后,后面再执行DML(影响多行的DML)如果失败,那么失败的DML会回滚,但DML仍然记录了binlog。这个 binlog 应用到备库后会导致主备不一致。
此bug已提给官方bug#76940。 以下是重现的测例:

主库执行

create table t1(c1 int primary key)  engine=innodb;
insert into t1 values(1),(2),(3),(4),(5);
create table t2 (c1 int, c2 int, foreign key(c2) references t1(c1)) engine=innodb;
insert into t2 values(1,1),(2,2),(5,5);

create temporary table tmp as select * from t1;

begin;
drop temporary table if exists tmp;
delete from t1 where c1 > 2;
--delete 失败: ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`zy`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`c2`) REFERENCES `t1` (`c1`))
commit;

mysql> select * from t1;
+----+
| c1 |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+

备库结果

mysql> select * from t1;
+----+
| c1 |
+----+
|  1 |
|  2 |
|  5 |
+----+

查看主库生成的binlog,delete from t1 where c1 > 2 失败了也记入了binlog。

BEGIN
/*!*/;
# at 1226
#150515 17:27:07 server id 1979399682  end_log_pos 1349     Query   thread_id=6263054   exec_time=0 error_code=0
SET TIMESTAMP=1431682027/*!*/;
SET @@session.pseudo_thread_id=6263054/*!*/;
DROP TEMPORARY TABLE IF EXISTS `tmp` /* generated by server */
/*!*/;
# at 1349
# at 1388
#150515 17:27:07 server id 1979399682  end_log_pos 1388     Table_map: `zy`.`t1` mapped to number 42174
#150515 17:27:07 server id 1979399682  end_log_pos 1427     Delete_rows: table id 42174 flags: STMT_END_F

BINLOG '
67tVVRMCPvt1JwAAAGwFAAAAAL6kAAAAAAEAAnp5AAJ0MQABAwAA
67tVVRkCPvt1JwAAAJMFAAAAAL6kAAAAAAEAAf/+AwAAAP4EAAAA
'/*!*/;
### DELETE FROM zy.t1
### WHERE
###   @1=3 /* INT meta=0 nullable=0 is_null=0 */
### DELETE FROM zy.t1
### WHERE
###   @1=4 /* INT meta=0 nullable=0 is_null=0 */
# at 1427
#150515 17:27:09 server id 1979399682  end_log_pos 1494     Query   thread_id=6263054   exec_time=0 error_code=0
SET TIMESTAMP=1431682029/*!*/;
COMMIT

bug分析

binlog有两个cache用来缓存事务的binlog。

  binlog_cache_data stmt_cache; //存放非事务表和临时表binlog
  binlog_cache_data trx_cache;  //存放事务表binlog

事务和语句回滚时应清理相应的cache, 事务提交时cache会刷入binlog文件中。

临时表在 drop 或 create 时不管成功还是失败都会记binlog。

当 drop 或 create 临时表操作和其他DML在一个事务中时,drop 或 create 临时表不管成功还是失败都会记binlog。查看源码中逻辑是只要事务中出现过 drop 或 create 临时表操作,那么事务后来的语句不管成功还是失败binlog cache都不会清理(参考函数binlog_rollbackbinlog_truncate_trx_cache)。

对于前面的例子,当事务执行到以下语句时,由于违反引用约束失败语句回滚时trx_cache应该清理。
delete from t1 where c1 > 2;
因此 delete 3,4 两条记录的binlog是应该不记入binlog的。

bug修复方法

当 drop 或 create 临时表操作和其他DML在一个事务中时,如果当前执行的语句不是 drop 或 create 临时表并且失败,则 binlog cache 应该清理。如果当前执行的语句是drop或create临时表,不管成功还是失败,cache都不用清理,都应记入binlog。

时间: 2024-09-20 17:27:17

数据库内核月报 - 2015 / 05-MySQL · 捉虫动态 · 临时表操作导致主备不一致的相关文章

MySQL内核月报 2014.09-MySQL· 捉虫动态·GTID 和 DELAYED

描述 这是一个MySQL 5.6才有的bug,影响包含最新版本.涉及到的概念有GTID.DELAYED. 现象 在5.6主备都开启GTID-MODE的时候,备库同步线程停止,且Last_SQL_Error显示"When @@SESSION.GTID_NEXT is set to a GTID, you must explicitly set it to a different value after a COMMIT or ROLLBACK. Please check GTID_NEXT var

MySQL内核月报 2014.10-MySQL· 捉虫动态·binlog重放失败

背景 在 MySQL 日常维护中,要回滚或者恢复数据,我们经常会用 binlog 来在数据库上重放,执行类似下面的语句: mysqlbinlog mysql-bin.000001 | mysql -hxxxx -Pxx -u 最近遇到了这样一个问题,在重放 binlog 时,mysqld 报这样的错 ERROR 1064 (42000) at line 25: You have an error in your SQL syntax; check the manual that correspo

MySQL内核月报 2014.11-MySQL· 捉虫动态·SIGHUP 导致 binlog 写错

bug描述 这是5.6中和gtid相关的一个bug,当 mysqld 收到 sighup 信号 (比如 kill -1) 的时候,会 flush binlog,但是新生成binlog开头没写 Previous_gtids_log_event,这会导致下面 2 个问题: 这个时候 mysqld 重启的话,会发现再也起不来了,error log 里有这样的错 The binary log file 'mysql/mysql-bin.000020' is logically corrupted: Th

MySQL内核月报 2014.09-MySQL· 捉虫动态·auto_increment

背景: Innodb引擎使用B_tree结构保存表数据,这样就需要一个唯一键表示每一行记录(比如二级索引记录引用). Innodb表定义中处理主键的逻辑是: 1.如果表定义了主键,就使用主键唯一定位一条记录 2.如果没有定义主键,Innodb就生成一个全局唯一的rowid来定位一条记录 auto_increment的由来: 1.Innodb强烈推荐在设计表中自定义一个主键,因为rowid是全局唯一的,所以如果有很多表没有定义主键,就会在生成rowid上产生争用. row_id由mutex保护,并

MySQL内核月报 2014.10-MySQL· 捉虫动态·从库OOM

bug背景 官方最近发布的版本(5.7.5)修复了这样一个bug,主备复制场景下,如果主库和备库对应的表结构中有数据类型不一致,并且主库的 binlog 是 row 格式的,这时候如果主库对不一致的表做了一个大事务更新,备库在应用 relay-log 的时候报OOM(Out of Memory).bug地址在这里,主备数据类型不一致主要发生在这2种情况下: 主备库版本不一致,不同版本之间的数据类型可能存在不一致.用户在报这个bug时,就是在5.5到5.6的复制场景下,用到了时间类型,时间类型在5

MySQL内核月报 2014.12-MySQL· 捉虫动态·Opened tables block read only

背景 MySQL通过read_only参数来设置DB只读,这样MySQL实例就可以作为slave角色,只应用binlog,不接受用户修改数据.这样就可以保护master-slave结构中的数据一致性,防止双写风险. global read_only的实现方式 MySQL5.5版本通过三个步骤来设置read_only: 步骤1:获取global read lock,阻塞所有的写入请求 步骤2:flush opened table cache,阻塞所有的显示读写锁 步骤3:获取commit lock

MySQL内核月报 2014.09-MySQL· 捉虫动态·GTID 和 binlog_checksum

现象描述 在5.6主备环境下,主备都开启GTID-MODE,备库开启crc校验,主库不开.重启备库sql线程后,备库sql线程停止Last_Error显示:Relay log read failure: Could not parse relay log event entry. The possible reasons are: the master's binary log is corrupted(you can check this by running 'mysqlbinlog' on

MySQL · 捉虫动态 · 删除索引导致表无法打开

问题背景 最近线上遇到一个问题,用户重启实例后发现有张表打不开了,经调研后发现是用户之前的霸蛮操作导致的,下面给出复现步骤: create table t1 (id int not null primary key, name varchar(100) not null) engine=innodb; create table t2 (id int not null primary key, fid int not null, name varchar(100) not null, CONSTR

MySQL内核月报 2014.08-MySQL· 捉虫动态·Count(Distinct) ERROR

背景 MySQL现行版本中存在一个count(distinct)语句返回结果错误的bug,表现为,实际结果存在值,但是用count(distinct)统计后返回的是0. 原因分析 Count(distinct f)的语义就是计算字段f的去重总数,计算流程大致如下: 流程一: 1. 构造一个unique集合A1(用tree实现) 2. 对每个值都试图插入集合A1中 3. 若和A1中现有item重复则直接跳过,不重复则插入并+1 4. 完成后计算集合中元素个数. 细心的同学会看到上面的语句中有一个s