使用 row 格式 binlog 撤销操作

MySQL 在使用 row 格式,并使用默认的 binlog_row_image=full 的时候,binlog 中记录了完整的更新前后的数据镜像。因此,根据 row 格式 binlog 进行撤销操作是可行的。我就试着做了这么个工具

[GitHub 页面] [linux 二进制文件]

例如

./binlog_undo -f /data/mysql/log-bin.000004 -p 3958 -o binlog.out

会扫描 /data/mysql/log-bin.000004 从位置 3958 到末尾的所有事务,倒转其中所有事务顺序和每个事务中的语句顺序,并反向所有的操作,把 WRITE 和 DELETE 反转,把 UPDATE 的前后镜像互换,然后将生成后的 binlog 文件写入 binlog.out 中。之后就可以用 mysqlbinlog 工具来回放这个 binlog 来撤销操作了。

这个工具可以用于在主备切换后,撤销掉原主上未同步到备的操作,避免完全重建;也可以作为误操作的后悔药。

记一次 MySQL 循环复制

有朋友的一对 MySQL 出现远大于其他类似实例的大量更新。因为开启了主备双向复制和 log_slave_updates,所以首先猜测是不是有循环复制,主 stop slave 后暂时正常。看了下主上的 relay_log,验证了这一点。幸而来回复制的只是监控用的心跳更新,不会搞坏数据。

但是 MySQL 会比较 server_id ,一般情况下是不会出问题的。于是又仔细看了下主上不正常的 relay_log ,发现其中 event 的 server_id 和当前的都不同。 问下来确实曾经在线改过 server_id。

想了下怎么触发这个问题,在自己的实例上复现了一把

  1. 开启 主->备 的复制
  2. 主上插入若干记录
  3. 更改主的server_id
  4. 开启 备->主 的复制
  5. 轰!

实际操作中,由于存在复制延时,即使没有先停止备->的复制也有可能触发问题。

然后就是怎么拆掉这个雷了。由于来回复制的只是心跳更新,所以只需要跳过就行,其实如果不是的话也已经完蛋没法救了。首先想到的是 binlog_ignore_db 和 replicate_ignore_db 。但是这两个不是动态变量,重启一次服务代价太大。同事 @Tachikoma 提醒我 change master 还有个 ignore_server_ids 选项。于是就只需要 change master to ignore_server_ids (1,2) 忽略之前的 server_id 就行。待复制正常,change master to ignore_server_ids () 就可以解除掉了。

移植 spider 到 MySQL 5.6

MariaDB 中自带了很多 MySQL 中没有的插件。我对其中的 spider 存储引擎很有兴趣。这个引擎可以让 MySQL 作为一个 proxy ,来实现 sharding、高可用等功能。这些功能已经有一些产品实现了,比如 MaxScale、Cobar、OneProxy、Atlas。但是我觉着 spider 把自己作为一个存储引擎来实现这些功能是有其优势的。SQL 解析和查询优化是个非常复杂而且很难做好的工作。其他替代产品都是自己实现,由于复杂性,这些产品都带来了一下限制,没能支持全部常见的 SQL 语句,给使用和实施带来了困难。而作为一个存储引擎,这些工作都由 MySQL 自身完成了,后面的工作就会简单很多,想做点优化的话也会容易些。

由于 MariaDB 从 MySQL 5.5 时代就分道扬镳了,做过很多改动后,和目前版本的 MySQL 已经有了不小差异,所以插件基本上没法直接拿到 MySQL 里编译使用。我就花了点功夫,把 spider 引擎移植到了 MySQL 5.6。

https://github.com/xiezhenye/mysql-plugin-spider-engine

编译使用和一般的插件差不多

cp -r src /path/to/mysql-src/storage/spider
cd /path/to/mysql-src
cmake . -DBUILD_CONFIG=mysql_release -DCMAKE_INSTALL_PREFIX=<mysql install dir>
cd storage/spider
make
make install

之后,执行附带的 install_spider.sql 安装插件,创建所需要的系统表。

mysql ... < scripts/install_spider.sql

具体文档参见 https://mariadb.com/kb/en/mariadb/spider/

在测试过程中,发现安装插件以后,重启 MySQL 后会 crash,然后再也启动不了,移除 ha_spider.so 后才行。具体来说,是在配置为

log_bin=off
spider_support_xa=on
spider_internal_xa=off

的时候。然而在 MariaDB 中却是正常的。开始以为是自己移植过程带来的 bug,或者有什么兼容问题未解决。追踪到后来,发现这居然是 MySQL 自身的 bug。于是去提交了一下。http://bugs.mysql.com/bug.php?id=78050

在使用外部 XA 的时候,如果没启用 binlog,会把 XA 信息通过 TC_LOG_MMAP 来持久化。然后 bug 就出在了那里。

这个 bug 曾经在 2009 年就被发现过,2012 年被 fix 过。但是显然并没有改对。待我自己做了 fix 以后,进一步发现,这个 bug 在 MariaDB 中已经被修复过,然后发现原来在 MySQL 5.7 分支下也是修复过的,但是并没应用到 5.6 分支。

都是几个相当低级的 bug。有成员未初始化,指针计算时搞错了指针类型,未判断空指针…… 。虽然这个地方确实是一般使用很难碰到,但是这代码质量简直无语。