介绍

从系统管理员或 DBA 的角度来讲, 总期望将线上的各种变更限制在一个可控的范围内, 减少一些不确定的因素. 这样做有几点好处:

. 记录线上的库表变更;
. 对线上的库表变更有全局的了解;
. 如果有问题, 方便回滚操作;

从这三点来看, 有很多种方式可以实现, 比如通过 migrate 等工具强制所有的操作都以统一的方式执行, 这需要开发人员做更多的配合, 所以这类工具在非规模话的业务场景中较难实现; 另外管理员或 DBA 也可以通过知识库比如 redmine 等类似的方式记录变更, 不过不可控因素很多, 特别依赖上线的流程, 也容易出现纰漏. 这就引申出本文要介绍的如何跟踪线上库表的变更, 下文以 MySQL 数据库介绍说明.

跟踪的方式

在 Postgresql 中, 由于触发器对各种操作都有很好的支持, 我们完全可以通过触发器的形式来记录所有 DDL 语句的变更. 与此相比, MySQL 则显得较为弱小, 我们只能以其它方式实现类似的目标. 下面以中间件, log, binlog, 注册 slave, mysqldiff 五种方式进行介绍.

1. 中间件

现有的中间件 atlaskingshardmycat 等, 都以 proxy 的角色部署于程序和 MySQL 之间, 所有发往 MySQL 的 sql 都通过 proxy 进行转发. 如下图所示, 我们可以在 proxy 层面增加一些 DDL, DML 相关语句的记录, 达到跟踪变更的目的.

    +------+        +-------+        +-------+
| app | ---> | proxy | ---> | MySQL |
+------+ +-------+ +-------+

这种方式自由度较高, 大家都可以随意定制. 不过需要一些开发能力, 另外 sql 的过滤也会影响到查询的性能, 通过中间件来直接修改表结构等操作也是有风险较大的方式.

2. log

这种方式很简单, 打开 MySQL 的 general log 或 audit log 即可记录所有的 sql 语句. 这种方式比较适合开发环境, 线上环境如果开启会产生很多日志, 弊远远大于利, 也不利于维护;

3. binlog

管理员或 DBA 同样可以解析 MySQL 的 binlog 来过滤表或权限的变更. 这种方式本质上等同第二种方式, 线上数据库需要开启 binlog 选项, 解析 binlog 也是很耗资源的操作. 线上如果实例较多, 这种方式特别不可取.

4. 注册 slave

注册 slave 的意思即通过 MySQL 的主从协议伪造一个假的 slave, 这样 master 会把所有的更新都发送过来, 再进行一些过滤的操作. 这种方式在同步数据或增量消费的场景特别适合, 这里只用于记录表或权限的变更确实是大材小用, 线上实例较多的话也不可取. 典型的工具有 myreplicationtungsten-replicator 以及阿里的 canal 等.

5. mysqldiff

实际上权限和表变更本身是低频率的操作事件, 上述的四种方式虽然都可以达到目标, 但本质上都是很耗费资源的操作. 考虑到这点, 我们可以通过对比的方式来实现权限及表结构变更的跟踪, 详见 sys-mysql-diff 工具. 考虑到通用性, sys-mysql-diff 工具每次都需要获取指定库的所有表的定义语句, 通过对比来生成对应的 DDL 语句. mysqldiff 则是对 sys-mysql-diff 工具的封装, 可以批量跟踪多个实例.

如何使用 mysqldiff

mysqldiff 工具是在 sys-mysql-diff 工具的基础上进行了一层封装, 所以本质上是通过 sys-mysql-diff 工具跟踪线上库的变化. 在实际的运用中, 需要注意以下几点:

1. 配置文件

mysqldiff 所需要的配置参考以下:

[backend]
dsn = user_mysqlmon:xxxxxxxx@tcp(10.0.21.17:)/mysqldiff?charset=utf8 [test3301]
host = 10.0.21.5
port =
db = test
user = user_mysqldiff
pass = xxxxxxxx
tag = host_location [test3306]
host = 10.0.21.7
port =
db = percona
user = user_mysqldiff
pass = xxxxxxxx
tag = host_location

2. 权限

所有的变更结果都会保存到指定的 MySQL 库中的 mysql_diff 表, 即上述的 [backend] 部分, 对于该表需要 select, insert, update 相关的权限. 被跟踪的实例则是 [testXXXX] 部分, 由于需要查看表结构和用户权限所以需要 select 和 grant option 权限. 我们以 user_mysqlmon 用户为 [backend] 的用户, 以 user_mysqldiff 为 [testXXXX] 部分的用户为例, 需要赋予他们以下权限:

grant select,insert,update on mysqldiff.* to user_mysqlmon@`10.0.21.%`;
grant select on *.* to user_mysqldiff@`10.0.21.%` with grant option;

配置中的 db = information_schema 则表示跟踪所有的数据库;

3. 运行

运行 mysqldiff 命令进行跟踪:

# ./mysqldiff -conf conf.cnf -verbose
// :: ---------------------------
changes from 10.0.21.5:
changes from 10.0.21.7:
DROP TABLE `emp`;
SET GLOBAL wait_timeout = ;
// :: insert 10.0.21.17:/percona ok
// :: ---------------------------

insert ... ok 一行表示将结果插入到了 [backend] 中.

总结

以 mysqldiff 方式跟踪库表及权限的变化相对简单方便, 比起其它方式算得上轻便. 另外也不受业务场景和管理员习惯的制约, 相对很通用. 不过其也有自身的缺陷, 在短时间内经常变更的表则很难跟踪, mysqldiff 仅能记录最后一次的变更. 另外管理员需要严格限制配置文件的权限, 最好给予 0600 的权限仅限当前用户查看. 不过整体而言, 要跟踪线上库表权限的变更, mysqldiff 是一个较为合适且通用的工具.

05-08 08:34