MySQl schema 表变更版本管理

https://github.com/nomad-software/snap https://github.com/arstercz/snap

注: 笔者新启 snap 分支项目后, 做了以下改动:

修复 cli 命令行需要返回 error 信息的错误;
增加 clear 选择删除指定库的版本控制信息;

更多信息参见 snap 链接

snap 用途概述 在开发过程中, 我们经常需要对核心的库表结构进行调整, 新建表, 增删字段等都需要记录以便和线上的环境区别开, 亦可以和相关的代码对应起来方便功能的追溯. 在交叉开发的环境中, 多名开发者共同更新一个表的情况很容易引起开发和部署的混乱, 可以通过版本管理的方式避免此类问题.

  1. 适用范围 仅适用于内网开发环境, 线上环境不做改动, 指定库中的所有表的 DDL 语句都需要使用 snap 工具更新.

  2. 风险提示 alter 相关的语句不影响测试表中的数据, create 相关的语句在回滚的时候会清理相关的数据.

  3. 选项说明 snap 以命令行方式运行:

    snap command <arguments...> [optional]
    

    更细节的帮助信息可以通过 snap help command 获得, 以下为全局的帮助信息:

# ./snap 
 ___ _ __   __ _ _ __
/ __| '_ \ / _' | '_ \  v0.1a
\__ \ | | | (_| | |_) |
|___/_| |_|\__,_| .__/
                |_|
Version control for MySql database schemas
by Gary Willoughby <snap@nomad.so>

USAGE:
snap command <arguments...> [optional]

COMMANDS:
commit, ci  <database> <snapfile> <message>
copy, cp    <source-database> <destination-database> [revision]
diff        <database> <from-revision>[..<to-revision>]
dump        <database> [revision]
help        [command]
init        <database>
list, ls    
log         <database>
show        <database> [revision]
update, up  <database> [revision]
clear, cl   <database>
version 
  1. 工作流程 snap 使用类似 git 的方式来实现版本的管理, 每次指定库的 DDL(sql包括 SNAP_UP 和 SNAP_DOWN两部分) 语句更新都会将库中所有的当前表结构信息保存一份到 snap 的配置库中, SNAP_UP 和 SNAP_DOWN 相关的 sql 也会保存到 snap 配置库的相关字段中, 在开发者指定回滚或更新到哪个版本的时候, snap 则根据保存在配置库中的信息进行重建(不变的表不做改动).

从上面描述来看 SNAP_UP 和 SNAP_DOWN 相关的 SQL 是版本管理的根本, 开发者每次更新 sql 前都要写好对应的 SNAP_UP 和 SNAP_DOWN 语句, 比如以下 sql:

-- SNAP_UP
create table snapt(
   id int(10)
) engine = innodb;

-- SNAP_DOWN
drop table snapt;

SNAP_UP 即为 do, SNAP_DOWN 即为 undo.

  1. 操作示例 snap 工具默认读取home目录下的 .snap 文件内容当做配置信息, 多个开发者则在各自的 home 目录下创建配置信息. 如下所示:
# cat /home/arstercz/.snap 
{
    "identity": "zhe.chen <arstercz@gmail.com>",
    "database": {
        "user": "arstercz",
        "password": "xxxxxxxxxxx",
        "protocol": "tcp",
        "host": "127.0.0.1",
        "port": "3306"
    }
}

库表版本管理的流程类似 git, 最开始需要初始化, 将最初的库中的表结构信息保存到 snap 配置库中(默认为 snap_config)

# ./snap init cztest
2016/06/03 17:48:42 Initialising 'cztest' database for managment
2016/06/03 17:48:42 Database initialised successfully.

init 即为第一次提交 cztest 库的全量表结构信息, 如果需要创建新表 snapt, 则在 sql 文件中写好 SNAP_UP 和 SNAP_DOWN 相关的 sql, 之后进行 commit 提交, 如下所示, commit 命令最后的参数为当前操作的注释说明.

# cat 1.sql 
-- SNAP_UP
create table snapt(
   id int(10)
) engine = innodb;

-- SNAP_DOWN
drop table snapt;

# ./snap commit cztest 1.sql "create snapt table"
2016/06/03 17:48:48 File committed successfully.

mysql root@[localhost:s3306 cztest] > show tables;
+------------------+
| Tables_in_cztest |
+------------------+
| snapt            |
| test             |
+------------------+
2 rows in set (0.00 sec)

可以使用 log 命令查看 cztest 库操作的历史记录概要, show 命令会列出指定库名某一版本下所有的表结构信息. 下面中的 Revision 即为版本号:

# ./snap log cztest
Revision: 2
Author: arstercz <arstercz@gmail.com>
Date: 2016-06-03 17:48:48

    create snapt table

Revision: 1
Author: arstercz <arstercz@gmail.com>
Date: 2016-06-03 17:48:42

    Database initialised.

回滚到第1版本:

# ./snap update cztest 1

mysql root@[localhost:s3306 cztest] > show tables;
+------------------+
| Tables_in_cztest |
+------------------+
| test             |
+------------------+
1 row in set (0.00 sec)

如果需要再次更新表呢? snap 如何保证多个用户之间的操作不会冲突? 当前情况下如果再想 commit DDL 操作, 需要先使用 snap 将 cztest 库更新到最高版本, 之后才能提交 DDL 语句. 通过这个流程可以比较有效的避免开发者之间的冲突.

备注 使用 snap 工具后, 所有的 DDL 语句都需要在一个文件里指定好 SNAP_UP 和 SNAP_DOWN 两部分以便版本的更新和回退, 如果在此期间手工更改了相关的表,理论上都不会影响 SNAP_UP 和 SANP_DOWN 之外的表, 但为了避免不必要的麻烦, 开发者可以使用 clear 选项清除指定库相关的所有版本信息, 再重新 init 该库.

同类工具 https://github.com/winebarrel/ridgepole 开发人员可能不习惯这种创建表的方式.