数据库内核月报 - 2015 / 06-MySQL · 答疑解惑 · binlog event 中的 error code

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
PolarDB Agent Express,2核4GB
RDS AI 助手,专业版
简介:

问题描述

RDS 有个任务叫做恢复到任意时间点,相当于一个数据时光机,可以将数据恢复到过去任意一个时间点,在用户出现误操作需要将数据找回时非常有用。这个功能主要是通过备份集恢复 + binlog回放实现,在用备份集恢复出的实例上应用 binlog 到指定时间点。

然而最近线上重放binlog时遇到了这样一个错误:

Table xxxx already exists

查看对应 binlog 的,发现这是一个 CREATE VIEW 语句,而备份集恢复出来的实例上确实已经有了这个view,再往前翻看binlog,并没有发现 DROP 这个 view 的记录,倒是找到了CREATE 这个 view 的记录,仔细比较2处 CREATE VIEW 的binlog event,会发现后者多了个 error_code=1050,这个是什么错呢:

$perror 1050
MySQL error code 1050 (ER_TABLE_EXISTS_ERROR): Table '%-.192s' already exists

1050 对应的错就是 Table already exists

就是说 CREATE VIEW 失败了,仍然记入 binlog 了,但是当时备库并没有这个错误中断掉。

复现步骤

复现非常简单,连着执行同一个create view语句即可。

mysql> create table t1(id int, name varchar(30)) engine=innodb;
Query OK, 0 rows affected (0.02 sec)

mysql> create view t1_v as select id from t1;
Query OK, 0 rows affected (0.01 sec)

mysql> create view t1_v as select id from t1;
ERROR 1050 (42S01): Table 't1_v' already exists

查看binlog event

#150614 23:15:02 server id 36302  end_log_pos 2651 CRC32 0x8f8b6c61     GTID [commit=yes]
SET @@SESSION.GTID_NEXT= '94cdda9b-a2d0-11e4-ade1-a0d3c1f20ae4:68157343'/*!*/;
# at 2651
#150614 23:15:02 server id 36302  end_log_pos 2856 CRC32 0x703fbe6d     Query   thread_id=21475 exec_time=0     error_code=0
SET TIMESTAMP=1434294902/*!*/;
CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `t1_v` AS select id from t1
/*!*/;
# at 2856
#150614 23:15:02 server id 36302  end_log_pos 2904 CRC32 0xfc2ef7cb     GTID [commit=yes]
SET @@SESSION.GTID_NEXT= '94cdda9b-a2d0-11e4-ade1-a0d3c1f20ae4:68157344'/*!*/;
# at 2904
#150614 23:15:02 server id 36302  end_log_pos 3109 CRC32 0x0e807965     Query   thread_id=21475 exec_time=0     error_code=1050
SET TIMESTAMP=1434294902/*!*/;
CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `t1_v` AS select id from t1
/*!*/;

可以清楚的看到,第二次 CREATE VIEW 时error_code 为 1050。

分析

查看 binlog 对应的代码,发现 error_code 这个字段是 Query_log_event 的专属,其它的如 row_event、gtid event等都没有这个字段。而备库在执行Query_log_event 时会检查event 的 error_code(存入expected_error),如果非0的话,就和当前SQL线程执行出错(存入actual_error)比较,看是否一致,如果一致的话就算执行成功,如果不一致的话,就再检查这个错是否能够忽略,如配置了 slave_skip_errors,代码片段如下(在Query_log_event::do_apply_event中):

/*
If we expected a non-zero error code, and we don't get the same error
code, and it should be ignored or is related to a concurrency issue.
*/
actual_error= thd->is_error() ? thd->get_stmt_da()->sql_errno() : 0;
DBUG_PRINT("info",("expected_error: %d  sql_errno: %d",
expected_error, actual_error));

if ((expected_error && expected_error != actual_error &&
!concurrency_error_code(expected_error)) &&
!ignored_error_code(actual_error) &&
!ignored_error_code(expected_error))
{
rli->report(ERROR_LEVEL, 0,
"\
Query caused different errors on master and slave.     \
Error on master: message (format)='%s' error code=%d ; \
Error on slave: actual message='%s', error code=%d. \
Default database: '%s'. Query: '%s'",
ER_SAFE(expected_error),
expected_error,
actual_error ? thd->get_stmt_da()->message() : "no error",
actual_error,
print_slave_db_safe(db), query_arg);
thd->is_slave_error= 1;
}

正常的想法应该是执行出错,就不应该记binlog,为什么会有这样的设计呢,主库错,记binlog,然后备库要求同样的错。
因为DDL是不能回滚的,如果DDL执行到一半报错,主库又不能回滚,那么应该如何通知备库它做了一半呢?就是把错记下去,期待备库也报同样的错。

挖一下黑历史,Query_log_event 中的 error_code 字段最早是在这个commit中加入的,目的是将主库上执行出错的信息传给备库,备库执行的时候会检测实际的出错信息和主库传过来的binlog中记录的是否是一样的,不一样就报错。

在此之前,备库对于 Query_log_event 执行出错是这样处理的,先检查SQL线程执行出错是不是因为表不存在,如果是的话,就单独再开个连接,从主库把不存在的表导过来(fetch_nx_table),然后再重试执行失败的event,如果还有不存在的表,就再拉,再重复执行;对于其它的错就直接报错。
现在看起来是不是很奇葩,2000年的时候,MySQL还是很年青的哇 =_=

总结

我们在回放binlog的时候用的是mysql client,不是SQL线程,mysql client中并没有对error_cocd的处理逻辑,因此遇到执行出错就直接报错了。

所以如果脚本或者代码里有这种重放binlog逻辑的,需要注意处理这种场景。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
目录
相关文章
|
9月前
|
SQL 运维 关系型数据库
深入探讨MySQL的二进制日志(binlog)选项
总结而言,对MySQL binlogs深度理解并妥善配置对数据库运维管理至关重要;它不仅关系到系统性能优化也是实现高可靠性架构设计必须考虑因素之一。通过精心规划与周密部署可以使得该机能充分发挥作用而避免潜在风险带来影响。
280 6
|
10月前
|
存储 SQL 关系型数据库
MySQL中binlog、redolog与undolog的不同之处解析
每个都扮演回答回溯与错误修正机构角色: BinLog像历史记载员详细记载每件大大小小事件; RedoLog则像紧急救援队伍遇见突發情況追踪最后活动轨迹尽力补救; UndoLog就类似时间机器可倒带历史让一切归位原始样貌同时兼具平行宇宙观察能让多人同时看见各自期望看见历程而互不干扰.
557 9
|
11月前
|
存储 SQL 关系型数据库
MySQL的Redo Log与Binlog机制对照分析
通过合理的配置和细致的管理,这两种日志机制相互配合,能够有效地提升MySQL数据库的可靠性和稳定性。
347 10
|
SQL 监控 关系型数据库
MySQL日志分析:binlog、redolog、undolog三大日志的深度探讨。
数据库管理其实和写小说一样,需要规划,需要修订,也需要有能力回滚。理解这些日志的作用与优化,就像把握写作工具的使用与运用,为我们的数据库保驾护航。
865 23
|
SQL 运维 关系型数据库
MySQL Binlog 日志查看方法及查看内容解析
本文介绍了 MySQL 的 Binlog(二进制日志)功能及其使用方法。Binlog 记录了数据库的所有数据变更操作,如 INSERT、UPDATE 和 DELETE,对数据恢复、主从复制和审计至关重要。文章详细说明了如何开启 Binlog 功能、查看当前日志文件及内容,并解析了常见的事件类型,包括 Format_desc、Query、Table_map、Write_rows、Update_rows 和 Delete_rows 等,帮助用户掌握数据库变化历史,提升维护和排障能力。
|
数据库
【YashanDB知识库】YDC连接数据库报错yasdb return code is zero
【YashanDB知识库】YDC连接数据库报错yasdb return code is zero
|
存储 SQL 关系型数据库
mysql的undo log、redo log、bin log、buffer pool
MySQL的undo log、redo log、bin log和buffer pool是确保数据库高效、安全和可靠运行的关键组件。理解这些组件的工作原理和作用,对于优化数据库性能和保障数据安全具有重要意义。通过适当的配置和优化,可以显著提升MySQL的运行效率和数据可靠性。
271 4
|
SQL 存储 关系型数据库
简单聊聊MySQL的三大日志(Redo Log、Binlog和Undo Log)各有什么区别
在MySQL数据库管理中,理解Redo Log(重做日志)、Binlog(二进制日志)和Undo Log(回滚日志)至关重要。Redo Log确保数据持久性和崩溃恢复;Binlog用于主从复制和数据恢复,记录逻辑操作;Undo Log支持事务的原子性和隔离性,实现回滚与MVCC。三者协同工作,保障事务ACID特性。文章还详细解析了日志写入流程及可能的异常情况,帮助深入理解数据库日志机制。
1894 0
|
9月前
|
缓存 关系型数据库 BI
使用MYSQL Report分析数据库性能(下)
使用MYSQL Report分析数据库性能
544 158
|
9月前
|
关系型数据库 MySQL 数据库
自建数据库如何迁移至RDS MySQL实例
数据库迁移是一项复杂且耗时的工程,需考虑数据安全、完整性及业务中断影响。使用阿里云数据传输服务DTS,可快速、平滑完成迁移任务,将应用停机时间降至分钟级。您还可通过全量备份自建数据库并恢复至RDS MySQL实例,实现间接迁移上云。

相关产品

  • 云数据库 RDS MySQL 版
  • 推荐镜像

    更多