数据库内核月报 - 2015 / 06-MySQL · 答疑解惑 · binlog event 中的 error code
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 dont 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逻辑的,需要注意处理这种场景。
Mysql误删,恢复数据,binlog闪回,宝塔面板 binlog是二进制日志文件,用来记录Mysql内部对数据库的改动(只记录对数据的修改操作),主要用于数据库的主从复制以及增量恢复。 当我们搭建mysql主从复制的时候,两个实例之间也是通过binlog来完成数据的备份同步。 所以有这种根据binlog得到执行sql语句、闪回sql语句,我们只需要利用根据分析binlog,然后就可以找到准确的数据改动sql,并得到闪回sql,检查无误后执行就可以恢复数据了
MySQL 日志之 binlog 格式 → 关于 MySQL 默认隔离级别的探讨 背景问题 再讲 binlog 之前,我们先来回顾下主流关系型数据库的默认隔离级别,是默认隔离级别,不是事务有哪几种隔离级别,别会错题意了 1、Oracle、SQL Server 的默认隔离级别是什么,MySQL 的呢 ? 2、为什么 MySQL 的默认隔离级别是 RR ?
mysql中的undo log、redo log 、binlog大致概要 undo log(回滚日志)、redo log(重做日志) 、binlog (归档日志)undo log,事务的原子性,用于事务回滚和MVCC(存储层,记录查询类)redo log,事务的持久性,用于服务器宕机故障恢复(存储层,记录查询类)binlog,用于数据备份和主从复制(服务层,记录更新修改类)日志区别undo log事务开始前的数据值redo log事务完成后的数据值。
db匠 rds内核团队秘密研发的全自动卖萌机. 追加特效: 发数据库内核月报. 月报传送: http://mysql.taobao.org/monthly/
相关文章
- PHP数据库操作:从MySQL原生API到PDO
- linux - mysql 异常:Ignoring query to other database
- 远程连接Mysql数据库问题(ERROR 2003 (HY000))
- CentOS定时备份mysql数据库和清理过期备份文件
- 阿里规范 - 五、MySQL 数据库 - (三)SQL语句 - 1 - 【强制】不要使用 count(列名)或 count(常量)来替代 count(*),count(*)是 SQL92 定义的 标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。
- 数据库内核月报 - 2015 / 05-MySQL · 引擎特性 · InnoDB redo log漫游
- 数据库内核月报 - 2015 / 05-MySQL · 捉虫动态 · 5.6 与 5.5 InnoDB 不兼容导致 crash
- 数据库内核月报 - 2015 / 08-MySQL · 功能分析 · MySQL表定义缓存
- 数据库内核月报 - 2015 / 09-MySQL · 引擎特性 · InnoDB Adaptive hash index介绍
- 数据库内核月报 - 2015 / 09-MySQL · 捉虫动态 · 建表过程中crash造成重建表失败
- 数据库内核月报 - 2015 / 09-MySQL · 特性分析 · 5.6并行复制事件分发机制
- 数据库内核月报 - 2015 / 10-MySQL · 捉虫动态 · start slave crash 诊断分析
- mysql中limit与in不能同时使用的解决方式.
- MySQL索引
- 〖Python 数据库开发实战 - Python与MySQL交互篇⑫〗- 项目实战- 实现新闻管理模块
- 云图说|云数据库MySQL内核小版本升级全攻略
- 【推荐收藏 】Python写入MySQL数据库的三种方式,最后一种方式方便又高效
- Xshell连接mysql数据库乱码问题解决思路总结
- mysql数据库的优化?
- crly-shell centos/windows服务器,Mysql数据库表结构损坏
- MySQL数据库忘记root账号密码解决方法