前言
在开发到发布的过程中,数据库的迁移是一件较为繁琐的事,而goose的出现,让数据库的迁移变得很轻松。
我们只需要简单的一句
goose up
就能将数据库重新部署,一句goose down
就能将原有的数据库清除,是不是感觉很easy!
安装
go get bitbucket.org/liamstask/goose/cmd/goose
安装完成后可以运行goose
查看是否安装成功。
如果提示 command not found: goose
你可以查看一下你的$GOPATH/bin
下有没有goose的binary文件,如果有那么安装成功了,只是PATH中没把这个路径添加进去。
设置~/.bash_profile
将GOPATH/bin
加入PATH即可,类似如下。
export GOPATH=/Users/go export GOBIN=/Users/go/bin export PATH=$PATH:$GOBIN
配置
在安装完成后,你就可以在你的项目中配置goose了。
1.首先,在你的项目下需要一个db
的文件夹,并且在文件夹的下面有一个数据库配置文件dbconf.yml
(当然,该文件夹 也可以用-path
来指定,只要该文件夹下有数据库配置文件dbconf.yml即可
)。
2.配置dbconf.yml
的内容。 一个简单的dbconf.yml
文件的格式如下所示:
development: driver: postgres open: user=liam dbname=tester sslmode=disable
这里的driver
和open
是直接通过go的database/sql
库去与数据库交互。driver
即数据库驱动,常见的驱动(sqlite,mysql,postgres)database/sql
都支持,其他驱动稍后也有介绍。 open
即数据库连接参数,常见的数据库操作实例如下:
# sqlite 配置 driver : sqlite open : ./my_test_sqlite.sdb # sqlite数据库路径 # postgres 配置 driver : postgres open : user=postgres password=123456 dbname=test sslmode=disable # mysql 配置 driver : mysql open : user:password@tcp(localhost:3306)/dbname?charset=utf8
而这里development
是你可用自己可以指定的一个环境名字。假如你在开发的时候和正式发布的时候数据库的环境并不相同,你就可以很轻松的用这个关键来分开部署。
实例如下,在开发过程中使用development
标识,在生产环境中用production
标识,两种情况的数据库环境不相同:
development: driver: postgres open: user=postgres dbname=test sslmode=disable production: driver: postgres open: user=myusername password=112233 dbname=mytest sslmode=disable
上面例子很明显的看到,两个环境下的数据库连接是不同的。在具体的使用时我们就可以用-env
来制定具体的环境。有关其使用,后面有介绍。
其他驱动
除了常见的sqlite,mysql,postgres三个驱动外,goose还可以使用一些支持database/sql
的驱动。这时就需要一个import path
和known dialect
了,如下所示:
customdriver: driver: custom open: custom open string import: github.com/custom/driver dialect: mysql
注 因为在SQL中编写的迁移由goose二进制直接执行,所以只有编译到goose中的驱动程序才可以用于这些迁移。
使用
在完成文件夹的创建以及数据库配置文件的参数设置后,就可已使用goose迁移你的数据库了。
Create
创建一个迁移语句,你可以选择使用go
或者sql
来创建。 例如,我需要创建一个基础的数据库脚本。
# 使用golang迁移 goose create basic_go # 创建成功, 输出 goose: created goose/db/migrations/20161130232822_basic_go.go # 或者使用 sql脚本 goose create basic_sql sql # 创建成功, 输出 goose: created goose/db/migrations/20161130232902_basic_sql.sql
注 执行goose 相关命令的目录与db
目录应该同一层。
我们可以查看文件。创建的go脚本如下:
package main import ( "database/sql" ) // Up is executed when this migration is applied func Up_20161129103859(txn *sql.Tx) { } // Down is executed when this migration is rolled back func Down_20161129103859(txn *sql.Tx) { }
创建的sql脚本如下:
-- +goose Up -- SQL in section 'Up' is executed when this migration is applied -- +goose Down -- SQL section 'Down' is executed when this migration is rolled back
数据库迁移语句就可以写在Up与Down有关区域内。
在其中写上需要的数据库迁移的操作。如下:
- File:goose/db/migrations/20161130232822_basic_go.go
package main import ( "database/sql" "fmt" ) // Up is executed when this migration is applied func Up_20161130232822(txn *sql.Tx) { fmt.Println("Up Out ...") } // Down is executed when this migration is rolled back func Down_20161130232822(txn *sql.Tx) { fmt.Println("Down Out ...") }
- File: goose/db/migrations/20161130232902_basic_sql.sql
-- +goose Up -- SQL in section 'Up' is executed when this migration is applied CREATE TABLE IF NOT EXISTS test ( id serial, con text ); -- +goose Down -- SQL section 'Down' is executed when this migration is rolled back DROP TABLE test;
注 sql语句以分号结束,不然可能无法识别。
Up
执行goose up
就是执行脚本Up区域的内容,所有可用的迁移脚本都将被执行。
在上面的例子中,执行goose up
得到结果:
goose: migrating db environment 'development', current version: 0, target: 20161130232902 Up Out ... OK 20161130232822_basic_go.go OK 20161130232902_basic_sql.sql
到数据库查看,表格已经创建好了。当然,上面说的指定环境,此时就可以指定了,如果不指定默认就是development
,在真正产的时候,我们可以如下使用:
goose -env production up # 结果 goose: migrating db environment 'production', current version: 0, target: 20161130232902 Up Out ... OK 20161130232822_basic_go.go OK 20161130232902_basic_sql.sql
在实际生产的时候,我们也可以指定执行某一个脚本,如下所示:
# 再创建一个脚本 goose create add_cloumn sql # 结果 goose: created /Users/trustasia/dev/go/src/demo_mi/goose/db/migrations/20161130235504_add_cloumn.sql # 写上迁移语句 -- +goose Up -- SQL in section 'Up' is executed when this migration is applied ALTER TABLE test ADD new_con text; -- +goose Down -- SQL section 'Down' is executed when this migration is rolled back ALTER TABLE test DROP COLUMN new_con; # 指定脚本迁移 goose goose up add_cloumn # 结果 goose: migrating db environment 'development', current version: 20161130232902, target: 20161130235504 OK 20161130235504_add_cloumn.sq
Down
执行goose down
向前回滚一个版本。
在上面的例子中,我们回滚。
goose down # 结果 goose: migrating db environment 'development', current version: 20161130235504, target: 20161130232902 OK 20161130235504_add_cloumn.sql
我们也可以指定环境,或指定脚本来回滚。
# 指定生产环境回滚 goose -env production down # 结果 goose: migrating db environment 'production', current version: 20161130232902, target: 20161130232822 OK 20161130232902_basic_sql.sql # 制定版本回滚 goose down basic_sql # 结果 goose: migrating db environment 'development', current version: 20161130232902, target: 20161130232822 OK 20161130232902_basic_sql.sql
redo
向上回滚一个版本,然后重新执行一次上次的操作。
goose redo goose: migrating db environment 'development', current version: 20161130232822, target: 0 Down Out ... OK 20161130232822_basic_go.go goose: migrating db environment 'development', current version: 0, target: 20161130232822 Up Out ... OK 20161130232822_basic_go.go
status
查看当前所有的迁移执行状态。
goose status goose: status for environment 'development' Applied At Migration ======================================= Thu Dec 1 09:25:17 2016 -- 20161130232822_basic_go.go Pending -- 20161130232902_basic_sql.sql Pending -- 20161130235504_add_cloumn.sql # 查看production环境下的 goose -env production status goose: status for environment 'production' Applied At Migration ======================================= Wed Nov 30 23:51:00 2016 -- 20161130232822_basic_go.go Pending -- 20161130232902_basic_sql.sql Pending -- 20161130235504_add_cloumn.sql
dbversion
查看当前数据库版本。
goose dbversion goose: dbversion 20161130232822
总结与注意事项
- up,down区域脚本对应
goose up
,goose down
; - 可用
-env
指定数据库环境,-path
指定包含数据库信息的路径; - 数据库语句以分号介绍,不可缺少;
- 复杂的函数语句写在
-- +goose StatementBegin
与-- +goose StatementEnd
之间,例下面的如创建函数。
-- +goose Up -- +goose StatementBegin CREATE OR REPLACE FUNCTION histories_partition_creation( DATE, DATE ) returns void AS $$ DECLARE create_query text; BEGIN FOR create_query IN SELECT 'CREATE TABLE IF NOT EXISTS histories_' || TO_CHAR( d, 'YYYY_MM' ) || ' ( CHECK( created_at >= timestamp ''' || TO_CHAR( d, 'YYYY-MM-DD 00:00:00' ) || ''' AND created_at < timestamp ''' || TO_CHAR( d + INTERVAL '1 month', 'YYYY-MM-DD 00:00:00' ) || ''' ) ) inherits ( histories );' FROM generate_series( $1, $2, '1 month' ) AS d LOOP EXECUTE create_query; END LOOP; -- LOOP END END; -- FUNCTION END $$ language plpgsql; -- +goose StatementEnd