数据库同步_migrations 调研
migrations 调研
migrations 使用场景
- 团队开发中,每个开发人员对于数据库都修改都必须手动记录,上线时需要人工整理,运维成本极高。而且在多个开发者之间数据结构同步也是很大的问题。Migrations 把数据库变更加入到代码中和代码一起进行版本管理,很好的解决了上述问题。
golang migrations
介绍
-
根据每个迁移文件来说,migrate 库都需要一些规则。迁移文件必须命名为 1_create_XXX.up.sql 和 1 _create_xxx.down.sql。所以基本上,每个迁移都应该有一个 up.sql 和一个 down.sql 文件。在实际运行迁移时将执行 up.sql 文件,而在尝试回滚时将执行 down.sql 文件。
-
单个逻辑迁移表示为两个单独的迁移文件,一个从上一个版本迁移到指定的版本,另一个从上一个版本回退到指定的版本。 这些迁移可以由任何一个受支持的迁移源提供。
-
迁移文件的顺序和方向由它们所使用的文件名决定。 Migrate 希望迁移的文件名的格式是:
{version}_{title}.up.{extension}
{version}_{title}.down.{extension}
-
每个迁移的标题都是未使用的,并且仅用于可读性。 类似地,迁移文件的扩展名不会被库检查,并且应该是正在使用的数据库的适当格式。
-
迁移的版本可以表示为任何64位无符号整数。 所有迁移按照版本号增加的顺序向上应用,按版本号减少的顺序向下应用。
-
常见的版本控制方案包括递增整数:
1_initialize_schema.down.sql
1_initialize_schema.up.sql
2_add_table.down.sql
2_add_table.up.sql
...
- 或者使用时间戳:
1500360784_initialize_schema.down.sql
1500360784_initialize_schema.up.sql
1500445949_add_table.down.sql
1500445949_add_table.up.sql
...
- 任何将不同的、递增的整数作为版本的方案都是有效的。
golang-migrate/migrate 使用
通过migrate CLI 操作(下载方式)
- 创建一个目录 migrations,里面存放我们需要迁移的对象
- 创建迁移文件
migrate create -ext sql -dir migrations create_users_table
// 执行这条命令会创建两个文件,分别是
20191228100730_create_test29_table.down.sql 20191228100730_create_test29_table.up.sql
- 20191228100730是和时间有关的一个标识,用来区分我们的migration的版本。
- 在20191228100730_create_test29_table.up.sql中写入创建users表的操作:
CREATE TABLE test_29(
id INT,
name VARCHAR(100) NOT NULL,
password VARCHAR(40) NOT NULL,
PRIMARY KEY ( id )
);
- 对应的我们就需要在20191228100730_create_test29_table.down.sql中删除这张表:
DROP table IF EXISTS test_29;
- 终端执行命令
migrate -verbose -source file:/Users/js/work/powerlaw/go/controller/migrations -database "mysql://root:xiamingyu123.@tcp(127.0.0.1:3306)/solegalee?collation=utf8mb4_bin&parseTime=true&loc=Local" up 1
日志
2019/12/28 10:38:57 Start buffering 20191228103644/u create_users_table
2019/12/28 10:38:57 Read and execute 20191228103644/u create_users_table
2019/12/28 10:38:57 Finished 20191228103644/u create_users_table (read 11.844954ms, ran 29.308199ms)
2019/12/28 10:38:57 Finished after 44.405306ms
2019/12/28 10:38:57 Closing source and database
-
命令介绍
- up: 表示执行up文件
- 1 : 表示本次操作更新1个版本(可以一次up多个版本)
- -source : migrations 文件来源
- -verbose:打印详细日志
- -database 数据库连接(数据库连接字符串是通过URL指定的。 URL格式取决于驱动程序,但通常采用以下格式:dbdriver://用户名:password@host:port/dbname?option1=true&option2=false 以下字符需要转义: !, #, $, %, &, ', (, ), *, +, ,, /, :, ;, =, ?, @, [, ])
-
执行成功后,数据库会新建 schema_migrations表同时插入一条记录;同时会新增一个test_29表
version dirty
20191228103644 0
- version :表示插入的版本号,也是sql文件的前缀
- dirty: 0表示当前版本执行成功,1表示执行失败
- 接下来执行下面语句回退版本
migrate -verbose -source file:/Users/js/work/powerlaw/go/controller/migrations -database "mysql://root:xiamingyu123.@tcp(127.0.0.1:3306)/solegalee?collation=utf8mb4_bin&parseTime=true&loc=Local" down 1
- 日志
2019/12/28 10:41:55 Start buffering 20191228103644/d create_users_table
2019/12/28 10:41:55 Read and execute 20191228103644/d create_users_table
2019/12/28 10:41:55 Finished 20191228103644/d create_users_table (read 21.480679ms, ran 29.485445ms)
2019/12/28 10:41:55 Finished after 54.122525ms
2019/12/28 10:41:55 Closing source and database
-
执行成功后,test_29 数据表会被删除,同时schema_migrations表中的记录会被删除
-
如果我们正常执行了 up 1 的操作,然后使用命令
migrate create -ext sql -dir migrations create_test30_table
-
这时会有4个文件
20191228103644_create_users_table.down.sql 20191228103644_create_users_table.up.sql 20191228104751_create_test30_table.down.sql 20191228104751_create_test30_table.up.sql
-
接下来我们把新创建的sql文件里写入对应的建表以及删除表的语句
// up CREATE TABLE test_30( id INT, name VARCHAR(100) NOT NULL, password VARCHAR(40) NOT NULL, PRIMARY KEY ( id ) ); // down DROP table IF EXISTS test_30;
-
然后执行下面命令使20191228104751_create_test30_table.up.sql 生效
migrate -verbose -source file:/Users/js/work/powerlaw/go/controller/migrations -database "mysql://root:xiamingyu123.@tcp(127.0.0.1:3306)/solegalee?collation=utf8mb4_bin&parseTime=true&loc=Local" up 1
- 如果我们一次创建了2个up sql文件,并且想同时生效,应该使用下面的命令,一次执行两个版本up操作
migrate -verbose -source file:/Users/js/work/powerlaw/go/controller/migrations -database "mysql://root:xiamingyu123.@tcp(127.0.0.1:3306)/solegalee?collation=utf8mb4_bin&parseTime=true&loc=Local" up 2
通过导入模块在代码里使用
package main
import (
"database/sql"
"flag"
"fmt"
"log"
"os"
_ "github.com/go-sql-driver/mysql"
"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database/mysql"
_ "github.com/golang-migrate/migrate/v4/source/file"
)
func main() {
var migrationDir = flag.String("migration.files", "./migrations", "Directory where the migration files are located ?")
var mysqlDSN = flag.String("mysql.dsn", os.Getenv("MYSQL_DSN"), "Mysql DSN")
flag.Parse()
db, err := sql.Open("mysql", *mysqlDSN)
if err != nil {
log.Fatalf("could not connect to the MySQL database... %v", err)
}
if err := db.Ping(); err != nil {
log.Fatalf("could not ping DB... %v", err)
}
// Run migrations
driver, err := mysql.WithInstance(db, &mysql.Config{})
if err != nil {
log.Fatalf("could not start sql migration... %v", err)
}
m, err := migrate.NewWithDatabaseInstance(
fmt.Sprintf("file://%s", *migrationDir), // file://path/to/directory
"mysql", driver)
if err != nil {
log.Fatalf("migration failed... %v", err)
}
if err := m.Up(); err != nil && err != migrate.ErrNoChange {
log.Fatalf("An error occurred while syncing the database.. %v", err)
}
log.Println("Database migrated")
// actual logic to start your application
os.Exit(0
- 如果有多个up SQL文件,m.Up() 会一起全部执行。
- 如果有多个down SQL文件,m.Down() 会一起全部执行。
- m.Version() 返回当前的版本号,以及执行情况
没有执行过up时:
0 false no migration
执行过up时:
20191228105602 false <nil>
- 通过设置step,up 一个版本(会把当前新增的up sql 文件全部执行)或者down 一个版本
err = m.Steps(1)
if err != nil {
utils.PanicErr(utils.WrapErr(err))
}
if err := m.Up(); err != nil && err != migrate.ErrNoChange {
utils.PanicErr(utils.WrapErr(err))
}
// Steps(-1) 表示回滚一个版本
err = m.Steps(-1)
if err != nil {
utils.PanicErr(utils.WrapErr(err))
}
if err := m.Down(); err != nil && err != migrate.ErrNoChange {
utils.PanicErr(utils.WrapErr(err))
}
fmt.Println(m.Version())
- 如果一个up 文件里面的SQL 有错误
CREATE TABLE test_30(
id INTs,
name VARCHAR(100) NOT NULL,
password VARCHAR(40) NOT NULL,
PRIMARY KEY ( id )
);
- 这时执行,
ERRO[0000]/Users/js/work/powerlaw/go/controller/internal/utils/tools.go:50 powerlaw.ai/solegal-ee/controller/internal/utils.LogErr() migration failed in line 0: CREATE TABLE test_30(
id INTs,
name VARCHAR(100) NOT NULL,
password VARCHAR(40) NOT NULL,
PRIMARY KEY ( id )
); (details: Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INTs,
name VARCHAR(100) NOT NULL,
password VARCHAR(40) NOT NULL,
' at line 2)
- 数据库里会将这个版本的记录 dirty 设置为1,表示未执行成功
version dirty
20191228104751 1
- 将SQL修改正确后再次执行,仍然报错
Dirty database version 20191228104751. Fix and force version.
- 这时在代码里加 m.Force(20191228104751)
err = m.Force(20191228104751)
if err != nil {
utils.PanicErr(utils.WrapErr(err))
}
if err := m.Up(); err != nil && err != migrate.ErrNoChange {
utils.PanicErr(utils.WrapErr(err))
}
- 数据库 schema_migrations表的记录修改成功,同时up SQL 也执行成功
version dirty
20191228104751 0
更多内容欢迎关注我的个人公众号“韩哥有话说”,100G人工智能学习资料,大量后端学习资料等你来拿。
转载自:https://juejin.cn/post/6945746960540237838