1 項目地址
https://github.com/golang-migrate/migrate
2 使用情景
一些項目功能會涉及到數據庫和代碼邏輯的修改,對于數據庫的修改,雖然 gorm 之類的工具能夠在代碼里面適配大部分情況,但是不能覆蓋所有數據庫變更情況,而且也不夠清晰。為清楚表示某次代碼提交設計的數據庫的修改并且方便 devops 部署服務,可以使用 golang-migrate 這樣的工具明確的標識對于數據的某次修改,可以對這些修改做部署和回滾。
使用 golang-migrate 有兩種方式,一種是使用 migrate 提供的 CLI,一種是使用 golang library。本次測試使用 migrate CLI。
2.1 支持數據庫類型
Source drivers: github-ee, godoc-vfs, s3, bitbucket, go-bindata, gcs, file, github, gitlab
Database drivers: cockroachdb, firebird, postgresql, redshift, clickhouse, postgres, cockroach, firebirdsql, mysql, crdb-postgres, mongodb, mongodb+srv, neo4j, pgx, spanner, sqlserver, stub, cassandra
3 使用方法
一開始使用 mysql 做測試,結果一直提示錯誤,就先用 github 上的教程用 postgres 測試了一遍。之后再解決 mysql 測試的問題。
3.1 安裝 migrate CLI
3.1.1 參考
https://github.com/golang-migrate/migrate/tree/master/cmd/migrate
3.1.2 安裝
直接 Release Downloads 下載對應版本即可。官網上的說明會讓人誤以為用 migrate CLI 的版本只支持某種數據庫,但是實際測試發現下載的 migrate CLI 可以支持各種 Database drivers。
3.2 postgres 測試
3.2.1 參考
https://github.com/golang-migrate/migrate/blob/master/database/postgres/TUTORIAL.md
3.2.2 啟動 postgres 數據庫
在 192.168.10.212 這臺測試服務器上用 docker 啟動 postgres 數據庫
dockerrun--namepostgres-ePOSTGRES_PASSWORD=mysecretpassword-dpostgres:14.0
進入 postgres 容器運行 psql -h localhost -U postgres -w -c "create database example;"來創建 example 數據庫。
3.2.3 創建某次數據庫變更的 sql 文件
mkdir migrations #首次執行,用于存放變更的 sql 文件
比如說這次修改我們需要新增一個 users 的表,
migrate create -ext sql -dir ./migrations -seq create_users_table
這個命令會在 migrations 目錄下生成兩個文件000001_create_users_table.up.sql 和000001_create_users_table.down.sql , 000001 是某次修改的版本號,000001_create_users_table.up.sql 用來存放創建 users 表的 sql 腳本,000001_create_users_table.down.sql 用來存放回滾這次操作的 sql 腳本。migrate create 命令只負責創建文件,sql 文件的內容需要我們手動編輯。
migrations├── 000001_create_users_table.down.sql├── 000001_create_users_table.up.sql
編輯000001_create_users_table.up.sql
CREATE TABLE IF NOT EXISTS users( user_id serial PRIMARY KEY, username VARCHAR (50) UNIQUE NOT NULL, password VARCHAR (50) NOT NULL, email VARCHAR (300) UNIQUE NOT NULL);
編輯000001_create_users_table.down.sql
DROP TABLE IF EXISTS users;
3.2.4 應用某次數據庫修改
migrate 的 up 子命令用來應用某一次數據庫變更, down 子命令用來回滾數據庫變更操作。
比如 up [N], N 表示執行多少個數據庫變更任務,那具體是執行 ./migrations 下面的哪些 sql 腳本呢?
up [N] Apply all or N up migrations down [N] [-all] Apply all or N down migrations Use -all to apply all down migrations
首次執行 migrate up 命令后,會在數據庫中添加 schema_migrations 表,這個表有兩個字段:version 和 dirty,version 用來表示當前數據庫對應 ./migrations 下面的那個版本,比如第一次執行 migrate up 1 后,如果執行成功了, schema_migrations 表中的 version=1,說明當前數據庫對應的是000001_create_users_table.up.sql 這個版本 dirty=f(false),執行到 version=1 這次的變更沒有出錯。如果 dirty=t(true) 會涉及要怎么修復這個錯誤的問題。
3.2.4.1 首次執行 migrate up
migrate -database 'postgres://postgres:mysecretpassword@192.168.10.212:5432/example?sslmode=disable' -path ./migrations up 11/u create_users_table (47.755135ms)
執行 migrate up 后,數據庫中增加了 users 表和 schema_migrations 表
example=# d List of relations Schema | Name | Type | Owner--------+-------------------+----------+---------- public | schema_migrations | table | postgres public | users | table | postgres public | users_user_id_seq | sequence | postgres(3 rows) example=# d users; Table "public.users" Column | Type | Collation | Nullable | Default----------+------------------------+-----------+----------+---------------------------------------- user_id | integer | | not null | nextval('users_user_id_seq'::regclass) username | character varying(50) | | not null | password | character varying(50) | | not null | email | character varying(300) | | not null |Indexes: "users_pkey" PRIMARY KEY, btree (user_id) "users_email_key" UNIQUE CONSTRAINT, btree (email) "users_username_key" UNIQUE CONSTRAINT, btree (username)
example=# select * from schema_migrations; version | dirty---------+------- 1 | f(1 row)
3.2.4.2 回滾操作
migrate -database 'postgres://postgres:mysecretpassword@192.168.10.212:5432/example?sslmode=disable' -path ./migrations downAre you sure you want to apply all down migrations? [y/N]yApplying all down migrations1/d create_users_table (31.550358ms)
migrations down 后,數據庫恢復到應用 create_users 之前的狀態,users 表被刪除,schema_migrations 表記錄被刪除
example=# select * from schema_migrations ; version | dirty---------+-------(0 rows)
3.2.5 應用多次修改
為了測試 migrations up [N] 執行多次修改的情形,第二次修改我們使用事務為 users 表增加 COLUMN,
migrate create -ext sql -dir ./migrations -seq add_mood_to_users , migrations 目錄下會增加000002_add_mood_to_users.up.sql 和000002_add_mood_to_users.down.sql 兩個文件。
000002_add_mood_to_users.up.sql :
BEGIN;CREATE TYPE enum_mood AS ENUM ( 'happy', 'sad', 'neutral');ALTER TABLE users ADD COLUMN IF NOT EXISTS mood enum_mood;COMMIT;
000002_add_mood_to_users.down.sql :
BEGIN; ALTER TABLE users DROP COLUMN IF EXISTS mood;DROP TYPE enum_mood; COMMIT;
第三次修改為 users 表增加 role_id 這個 COLUMN
migrate create -ext sql -dir ./migrations -seq add_roleid_to_users , migrations 目錄下會增加000003_add_roleid_to_users.up.sql 和000003_add_roleid_to_users.down.sql 兩個文件。
000003_add_roleid_to_users.up.sql
ALTER TABLE users ADD COLUMN IF NOT EXISTS role_id INTEGER;
000003_add_roleid_to_users.down.sql
ALTER TABLE users DROP COLUMN IF EXISTS role_id;
這樣 migrations 目錄下有如下6個 sql 文件:
.└── migrations ├── 000001_create_users_table.down.sql ├── 000001_create_users_table.up.sql ├── 000002_add_mood_to_users.down.sql ├── 000002_add_mood_to_users.up.sql ├── 000003_add_roleid_to_users.down.sql └── 000003_add_roleid_to_users.up.sql
3.2.5.1 應用多次修改
執行 migrate down 回滾 migrations 目錄中所有修改。migrate -database 'postgres://postgres:mysecretpassword@192.168.10.212:5432/example?sslmode=disable' -path ./migrations down
migrate up 表示執行 migrations 目錄中所有 up.sql 腳本。migrate -database 'postgres://postgres:mysecretpassword@192.168.10.212:5432/example?sslmode=disable' -path ./migrations up
migrate up [N], 表示從 schema_migrations 表的 version 的值后再執行 N 個部署。假設 schema_migration 中 version=1,migrate up [2] 就會執行 migrations 目錄中 000001* 之后的000002_add_mood_to_users.up.sql 和000003_add_roleid_to_users.up.sql 兩個操作。
3.2.6 測試執行失敗的情況
首先執行 migrate down 回滾所有操作,然后執行 migrate up 2 應用000001_create_users_table.up.sql 和000002_add_mood_to_users.up.sql 。接下來我們修改000003_add_roleid_to_users.up.sql ,使得000003_add_roleid_to_users.up.sql 語法錯誤
? postgres migrate -database 'postgres://postgres:mysecretpassword@192.168.10.212:5432/example?sslmode=disable' -path ./migrations down # 回滾所有操作Are you sure you want to apply all down migrations? [y/N]yApplying all down migrationsno change? postgres? postgres? postgres migrate -database 'postgres://postgres:mysecretpassword@192.168.10.212:5432/example?sslmode=disable' -path ./migrations up 2 # 應用 `000001_create_users_table.up.sql` 和 `000002_add_mood_to_users.up.sql`1/u create_users_table (47.883494ms)2/u add_mood_to_users (82.025579ms)? postgres? postgres cat migrations/000003_add_roleid_to_users.up.sqlALTER TABLE users ADD COLUMN role_id INTEGER errtest;%# 修改 000003_add_roleid_to_users.up.sql 增加 errtest 到語句末尾,使得 sql 語法錯誤 ? postgres migrate -database 'postgres://postgres:mysecretpassword@192.168.10.212:5432/example?sslmode=disable' -path ./migrations up 1 # 執行錯誤error:migrationfailed:syntaxerroratornear"errtest"(column46)inline1:ALTERTABLEusersADDCOLUMNrole_idINTEGERerrtest;(details:pq:syntaxerroratornear"errtest")
這個時候查詢 postgres 數據庫的 schema_migrations 表,version=3,說明當前執行到 000003, 但是 dirty=t 說明執行有錯誤。
example=# select * from schema_migrations ; version | dirty---------+------- 3 | t(1 row)
然后修正000003_add_roleid_to_users.up.sql 的語法錯誤(去掉多余的 “errtest”),再次執行 migrate up 1,這個時候還是提示錯誤,因為 version=3 的 dirty=t,這個時候需要使用 migrate force 3 來確認說 version=3 的錯誤問題已修復,而且需要執行 migrate down 1 將 version 回退到 version=2 ,才能繼續執行。
postgres migrate -database 'postgres://postgres:mysecretpassword@192.168.10.212:5432/example?sslmode=disable' -path ./migrations up 1error: migration failed: syntax error at or near "errtest" (column 60) in line 1: ALTER TABLE users ADD COLUMN IF NOT EXISTS role_id INTEGER errtest; (details: pq: syntax error at or near "errtest")? postgres migrate -database 'postgres://postgres:mysecretpassword@192.168.10.212:5432/example?sslmode=disable' -path ./migrations force 3? postgres migrate -database 'postgres://postgres:mysecretpassword@192.168.10.212:5432/example?sslmode=disable' -path ./migrations down 13/d add_roleid_to_users (34.332954ms)? postgres migrate -database 'postgres://postgres:mysecretpassword@192.168.10.212:5432/example?sslmode=disable' -path ./migrations up 13/u add_roleid_to_users (36.832839ms)
3.3 mysql 測試
說回前面說的測試 mysql 出現錯誤的問題,錯誤提示如下:
migrate -database mysql://root:x*xxx@192.168.10.212:3306/temp -path ./migrations upzsh: no matches found: mysql://root:x*xxx@192.168.10.212:3306/temp
查了一下這個錯誤是因為我使用的 shell 是 zsh, zsh 會自動解釋 * ?等字符,而不是把 * ?留給命令 migrate 來解析,導致了錯誤,解決方案是在 ~/.zshrc 中加入:setopt no_nomatch 。另外一個解決方法是把 -database 參數的值加上引號 migrate -database 'mysql://root:x*xxx@192.168.10.212:3306/temp' -path ./migrations up
修正了 zsh 的問題后,再次執行 migrate up,還是提示錯誤:
migrate -database mysql://root:x*xxx@192.168.10.212:3306/temp -path ./migrations uperror: default addr for network '192.168.10.212:3306' unknown
這次的錯誤是因為 mysql 的 url 書寫格式問題,mysql 的 url 需要寫成 mysql://root:passwd@tcp(192.168.10.212:3306)/database 這樣的格式。
-
服務器
+關注
關注
12文章
9233瀏覽量
85627 -
數據庫
+關注
關注
7文章
3823瀏覽量
64506 -
Docker
+關注
關注
0文章
478瀏覽量
11873
原文標題:Go 語言數據庫遷移工具 golang-migrate
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論