數據庫遷移工具 golang-migrate
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 數據庫
docker run --name postgres -e POSTGRES_PASSWORD=mysecretpassword -d postgres: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 1
1/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 down
Are you sure you want to apply all down migrations? [y/N]
y
Applying all down migrations
1/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]
y
Applying all down migrations
no 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.sql
ALTER 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: migration failed: syntax error at or near "errtest" (column 46) in line 1: ALTER TABLE users ADD COLUMN role_id INTEGER errtest; (details: pq: syntax error at or near "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 1
error: 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 1
3/d add_roleid_to_users (34.332954ms)
➜ postgres migrate -database 'postgres://postgres:mysecretpassword@192.168.10.212:5432/example?sslmode=disable' -path ./migrations up 1
3/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 up
zsh: 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 up
error: default addr for network '192.168.10.212:3306' unknown
這次的錯誤是因爲 mysql 的 url 書寫格式問題,mysql 的 url 需要寫成 mysql://root:passwd@tcp(192.168.10.212:3306)/database 這樣的格式。
轉自:
blog.csdn.net/doyzfly/article/details/121096806
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Jg1xybreqW0MBOhrzoOm0w