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

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
云数据库 RDS SQL Server,基础系列 2核4GB
简介:

问题描述

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

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

Table xxxx already exists
AI 代码解读

查看对应 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
AI 代码解读

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
AI 代码解读

查看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
/*!*/;
AI 代码解读

可以清楚的看到,第二次 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;
}
AI 代码解读

正常的想法应该是执行出错,就不应该记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逻辑的,需要注意处理这种场景。

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
db匠
+关注
目录
打赏
0
0
0
0
9495
分享
相关文章
【YashanDB知识库】原生mysql驱动配置连接崖山数据库
【YashanDB知识库】原生mysql驱动配置连接崖山数据库
【YashanDB知识库】原生mysql驱动配置连接崖山数据库
docker拉取MySQL后数据库连接失败解决方案
通过以上方法,可以解决Docker中拉取MySQL镜像后数据库连接失败的常见问题。关键步骤包括确保容器正确启动、配置正确的环境变量、合理设置网络和权限,以及检查主机防火墙设置等。通过逐步排查,可以快速定位并解决连接问题,确保MySQL服务的正常使用。
244 82
MySQL Binlog 日志查看方法及查看内容解析
本文介绍了 MySQL 的 Binlog(二进制日志)功能及其使用方法。Binlog 记录了数据库的所有数据变更操作,如 INSERT、UPDATE 和 DELETE,对数据恢复、主从复制和审计至关重要。文章详细说明了如何开启 Binlog 功能、查看当前日志文件及内容,并解析了常见的事件类型,包括 Format_desc、Query、Table_map、Write_rows、Update_rows 和 Delete_rows 等,帮助用户掌握数据库变化历史,提升维护和排障能力。
mysql的undo log、redo log、bin log、buffer pool
MySQL的undo log、redo log、bin log和buffer pool是确保数据库高效、安全和可靠运行的关键组件。理解这些组件的工作原理和作用,对于优化数据库性能和保障数据安全具有重要意义。通过适当的配置和优化,可以显著提升MySQL的运行效率和数据可靠性。
46 16
【YashanDB知识库】YDC连接数据库报错yasdb return code is zero
【YashanDB知识库】YDC连接数据库报错yasdb return code is zero
缓存与数据库的一致性方案,Redis与Mysql一致性方案,大厂P8的终极方案(图解+秒懂+史上最全)
缓存与数据库的一致性方案,Redis与Mysql一致性方案,大厂P8的终极方案(图解+秒懂+史上最全)
mysql的undo log、redo log、bin log、buffer pool
MySQL的undo log、redo log、bin log和buffer pool是确保数据库高效、安全和可靠运行的关键组件。理解这些组件的工作原理和作用,对于优化数据库性能和保障数据安全具有重要意义。通过适当的配置和优化,可以显著提升MySQL的运行效率和数据可靠性。
31 4
MySQL生产环境迁移至YashanDB数据库深度体验
这篇文章是作者将 MySQL 生产环境迁移至 YashanDB 数据库的深度体验。介绍了 YashanDB 迁移平台 YMP 的产品相关信息、安装步骤、迁移中遇到的各种兼容问题及解决方案,最后总结了迁移体验,包括工具部署和操作特点,也指出功能有优化空间及暂不支持的部分,期待其不断优化。
如何排查和解决PHP连接数据库MYSQL失败写锁的问题
通过本文的介绍,您可以系统地了解如何排查和解决PHP连接MySQL数据库失败及写锁问题。通过检查配置、确保服务启动、调整防火墙设置和用户权限,以及识别和解决长时间运行的事务和死锁问题,可以有效地保障应用的稳定运行。
176 25
云数据库:从零到一,构建高可用MySQL集群
在互联网时代,数据成为企业核心资产,传统单机数据库难以满足高并发、高可用需求。云数据库通过弹性扩展、分布式架构等优势解决了这些问题,但也面临数据安全和性能优化挑战。本文介绍了如何从零开始构建高可用MySQL集群,涵盖选择云服务提供商、创建实例、配置高可用架构、数据备份恢复及性能优化等内容,并通过电商平台案例展示了具体应用。

相关产品

  • 云数据库 RDS MySQL 版
  • AI助理

    你好,我是AI助理

    可以解答问题、推荐解决方案等