Go數(shù)據(jù)庫遷移的實(shí)現(xiàn)步驟
0. 簡介
本文將介紹兩個Go
生態(tài)中常見的數(shù)據(jù)庫遷移工具——golang-migrate
和gormigrate
。
1. golang-migrate
golang-migrate
的官方Github
是鏈接。它提供了客戶端的方式使用,也可以使用Go SDK
調(diào)用的方式使用。其各種安裝方式詳見鏈接。
對于每次遷移而言,都需要有一個遷移文件,遷移文件需要命名為{number}_xxx.up.sql
以及{number}_xxx.down.sql
,其中{number}
是數(shù)字,可以使用migrate create
命令產(chǎn)生,如下,-seq
表示按順序產(chǎn)生文件:
migrate create -ext sql -dir ./migration_files -seq init_schema
{number}
將會是從1開始的遞增的數(shù)字。如下,默認(rèn)按照時間格式產(chǎn)生,-tz
可以設(shè)置時區(qū):
migrate create -ext sql -dir ./migration_files -tz Asia/Shanghai init_schema
{number}
將會是上海時區(qū)的時間格式。反正不管是什么格式,golang-migration
按照從小到大的順序依次執(zhí)行。
在實(shí)際運(yùn)行時,遷移時順序運(yùn)行{number}_xxx.up.sql
文件,回滾時倒序運(yùn)行{number}_xxx.down.sql
。
1.1 通過migrate命令操作
1.1.1 創(chuàng)建sql文件
初始化數(shù)據(jù)庫
我們可以通過安裝migrate
工具,然后通過指令進(jìn)行操作如下,生成20230616164949_init.up.sql
和20230616164949_init.down.sql
文件:
$ migrate create -ext sql -dir ./migration_files -tz Asia/Shanghai init xxx/migration_files/20230616164949_init.up.sql xxx/migration_files/20230616164949_init.down.sql
在20230616164949_init.up.sql
中填寫:
-- ---------------------------- -- Table structure for person -- ---------------------------- DROP TABLE IF EXISTS `person`; CREATE TABLE `person` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(256) COLLATE utf8mb4_unicode_ci DEFAULT NULL, `age` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
在20230616164949_init.down.sql
中填寫:
DROP TABLE IF EXISTS `person`;
新增一列
然后我們給person
表新增一個性別gender
列,先使用命令創(chuàng)建sql
文件:
$ migrate create -ext sql -dir ./migration_files -tz Asia/Shanghai add_gender xxx/migration_files/20230616165624_add_gender.up.sql xxx/migration_files/20230616165624_add_gender.down.sql
20230616165624_add_gender.up.sql
:
ALTER TABLE `person` ADD COLUMN `gender` BIGINT(20) DEFAULT NULL AFTER `age`;
20230616165624_add_gender.down.sql
:
ALTER TABLE `person` DROP COLUMN `gender`;
新增name為index
接下來,我們?yōu)?code>name列創(chuàng)建一個索引,同樣需要用命令創(chuàng)建sql
文件:
$ migrate create -ext sql -dir ./migration_files -tz Asia/Shanghai add_index_name xxx/migration_files/20230619104829_add_index_name.up.sql xxx/migration_files/20230619104829_add_index_name.down.sql
20230619104829_add_index_name.up.sql
:
ALTER TABLE `person` ADD INDEX `idx_name`(`name`);
20230619104829_add_index_name.down.sql
:
ALTER TABLE `person` DROP INDEX `idx_name`;
此時在遷移文件中有以下文件:
$ ll migration_files total 48 -rw-r--r-- 1 chenyiguo staff 30B Jun 16 16:55 20230616164949_init.down.sql -rw-r--r-- 1 chenyiguo staff 396B Jun 16 16:55 20230616164949_init.up.sql -rw-r--r-- 1 chenyiguo staff 42B Jun 16 17:02 20230616165624_add_gender.down.sql -rw-r--r-- 1 chenyiguo staff 77B Jun 16 17:01 20230616165624_add_gender.up.sql -rw-r--r-- 1 chenyiguo staff 43B Jun 19 10:57 20230619104829_add_index_name.down.sql -rw-r--r-- 1 chenyiguo staff 50B Jun 19 10:57 20230619104829_add_index_name.up.sql
1.1.2 進(jìn)行遷移
一步遷移
我們可以使用如下指令每次執(zhí)行一步遷移:
$ migrate --path ./migration_files --database="mysql://root:IBHojwND.yo@tcp(10.117.49.6:13306)/migration_test?charset=utf8mb4&parseTime=true" -verbose up 1 2023/06/19 11:01:28 Start buffering 20230616164949/u init 2023/06/19 11:01:28 Read and execute 20230616164949/u init 2023/06/19 11:01:28 Finished 20230616164949/u init (read 133.166833ms, ran 286.737042ms) 2023/06/19 11:01:28 Finished after 566.083083ms 2023/06/19 11:01:28 Closing source and database
然后查看表person
:
MariaDB [migration_test]> DESC `person`; +-------+---------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+---------------------+------+-----+---------+----------------+ | id | bigint(20) unsigned | NO | PRI | NULL | auto_increment | | name | varchar(256) | YES | | NULL | | | age | bigint(20) | YES | | NULL | | +-------+---------------------+------+-----+---------+----------------+ 3 rows in set (0.001 sec)
這時候,會發(fā)現(xiàn)數(shù)據(jù)庫會生成一個名為schema_migrations
的表,可以看到其只有兩列,其中第一列version
表示現(xiàn)階段的版本,比如以上我們只是執(zhí)行了遷移的第一步,所以版本是20230616164949
;第二列是dirty
,0表示正常,1表示被出錯了,一般而言需要手動處理。
MariaDB [migration_test]> SELECT * FROM `schema_migrations`; +----------------+-------+ | version | dirty | +----------------+-------+ | 20230616164949 | 0 | +----------------+-------+ 1 row in set (0.001 sec)
同樣的,我們可以使用以下指令回滾這一次操作:
$ migrate --path ./migration_files --database="mysql://root:IBHojwND.yo@tcp(10.117.49.6:13306)/migration_test?charset=utf8mb4&parseTime=true" -verbose down 1 2023/06/19 11:12:57 Start buffering 20230616164949/d init 2023/06/19 11:12:57 Read and execute 20230616164949/d init 2023/06/19 11:12:58 Finished 20230616164949/d init (read 238.931375ms, ran 167.683125ms) 2023/06/19 11:12:58 Finished after 552.185792ms 2023/06/19 11:12:58 Closing source and database
可以發(fā)現(xiàn)操作被回滾,整個數(shù)據(jù)庫只保留了schema_migrations
表,執(zhí)行了20230616164949_init.down.sql
中的指令,person
表被刪除了。
整體升級
$ migrate --path ./migration_files --database="mysql://root:IBHojwND.yo@tcp(10.117.49.6:13306)/migration_test?charset=utf8mb4&parseTime=true" -verbose up 2023/06/19 11:14:52 Start buffering 20230616164949/u init 2023/06/19 11:14:52 Start buffering 20230616165624/u add_gender 2023/06/19 11:14:52 Start buffering 20230619104829/u add_index_name 2023/06/19 11:14:53 Read and execute 20230616164949/u init 2023/06/19 11:14:53 Finished 20230616164949/u init (read 143.943417ms, ran 167.257042ms) 2023/06/19 11:14:53 Read and execute 20230616165624/u add_gender 2023/06/19 11:14:53 Finished 20230616165624/u add_gender (read 463.350333ms, ran 304.728208ms) 2023/06/19 11:14:53 Read and execute 20230619104829/u add_index_name 2023/06/19 11:14:54 Finished 20230619104829/u add_index_name (read 987.893333ms, ran 227.057417ms) 2023/06/19 11:14:54 Finished after 1.391094s 2023/06/19 11:14:54 Closing source and database
這時候可以發(fā)現(xiàn),person
表的所有改動都被付諸實(shí)現(xiàn):
MariaDB [migration_test]> DESC `person`; +--------+---------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------+---------------------+------+-----+---------+----------------+ | id | bigint(20) unsigned | NO | PRI | NULL | auto_increment | | name | varchar(256) | YES | MUL | NULL | | | age | bigint(20) | YES | | NULL | | | gender | bigint(20) | YES | | NULL | | +--------+---------------------+------+-----+---------+----------------+ 4 rows in set (0.001 sec)
而schema_migrations
表里的數(shù)據(jù)版本變成了最新的20230619104829
,可以發(fā)現(xiàn),此表中并沒有存儲歷史版本。
MariaDB [migration_test]> SELECT * FROM `schema_migrations`; +----------------+-------+ | version | dirty | +----------------+-------+ | 20230619104829 | 0 | +----------------+-------+ 1 row in set (0.000 sec)
同樣,也可以使用down
來回滾整個表。
當(dāng)然,golang-migrate
還有一些其他的操作,大家可以使用migrate -help
命令學(xué)習(xí)。
1.2 通過Go SDK實(shí)現(xiàn)
除了以上通過命令的方式使用golang-migrate
,也可以使用其Go SDK
的方式運(yùn)用于Go project
中。
sql
文件的創(chuàng)建這里就不贅述了,當(dāng)然可以為了方便,使用以下的shell
文件簡化創(chuàng)建sql
的流程:
#!/bin/bash read -p "Please input sql change tag: " tag if [ x"${tag}" = x ]; then echo "Please input sql change tag!!!" exit 1 fi # TIMEZONE是時區(qū)的環(huán)境變量,默認(rèn) Asia/Shanghai if [ x"${TIMEZONE}" = x ]; then echo "Not set TIMEZONE, set default Asia/Shanghai" TIMEZONE="Asia/Shanghai" fi migrate create -ext sql -dir ./migration_files -tz "${TIMEZONE}" ${tag}
1.2.1 代碼實(shí)現(xiàn)
package main import ( "context" "database/sql" "errors" "fmt" "github.com/golang-migrate/migrate/v4" "github.com/sirupsen/logrus" "os" "time" _ "github.com/go-sql-driver/mysql" _ "github.com/golang-migrate/migrate/v4/database/mysql" _ "github.com/golang-migrate/migrate/v4/source/file" ) const ( dbUser = "DB_USER" dbPassWord = "DEVOPS_INFRA_PASSWORD" dbUrl = "DB_URL" ) var ( username = "root" password = "IBHojwND.yo" hostname = "10.117.49.6:13306" dbname = "migration_test" errUpFailed = errors.New("migration up failed") ) func dsn(dbName string) string { return fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=true", username, password, hostname, dbName) } func createDBIfNotExist() error { db, err := sql.Open("mysql", dsn("")) if err != nil { logrus.Errorf("opening DB err : %+v\n", err) return err } defer db.Close() ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() res, err := db.ExecContext(ctx, "CREATE DATABASE IF NOT EXISTS "+dbname) if err != nil { logrus.Errorf("creating DB err: %+v\n", err) return err } no, err := res.RowsAffected() if err != nil { logrus.Errorf("affected rows err: %+v", err) return err } logrus.Infof("rows affected %d\n", no) return nil } func migration() (e error) { // 新建migrate對象 m, err := migrate.New("file://migration_files", "mysql://"+dsn(dbname)) if err != nil { logrus.Errorf("new migrate err: %+v", err) return err } // 進(jìn)行up操作 err = m.Up() version, dirty, _ := m.Version() if err == nil || err == migrate.ErrNoChange { logrus.Infof("migrate up success, version: %+v, dirty: %+v", version, dirty) return } logrus.Errorf("migrate up failed, version: %+v, dirty: %+v, err: %+v", version, dirty, err) // 只要up沒有成功,后續(xù)都是失敗 e = errUpFailed // 如果up失敗,嘗試回滾一步 version, dirty, _ = m.Version() err = m.Steps(-1) if err == nil || err == os.ErrNotExist { logrus.Infof("migrate down -1 success, version: %+v, dirty: %+v", version, dirty) return } logrus.Errorf("migrate down -1 failed, version: %+v, dirty: %+v, err: %+v", version, dirty, err) // 如果回滾失敗,判斷是不是因?yàn)閐irty er, ok := err.(migrate.ErrDirty) if !ok { // 不是dirty錯誤 return } // 是dirty錯誤,那我們強(qiáng)制設(shè)置version,再利用這個版本進(jìn)行回滾 err = m.Force(er.Version) if err != nil { logrus.Printf("migrate force %+v err: %+v", er.Version, err) return } err = m.Steps(-1) if err == nil || err == os.ErrNotExist { logrus.Printf("migrate down -1 after force success, version: %+v, dirty: %+v", version, dirty) return } logrus.Printf("migrate down -1 after force failed, version: %+v, dirty: %+v, err: %+v", version, dirty, err) return } func main() { // 如果數(shù)據(jù)庫不存在則創(chuàng)建數(shù)據(jù)庫 err := createDBIfNotExist() if err != nil { os.Exit(1) } // migration操作 err = migration() if err != nil { os.Exit(1) } }
這樣,程序執(zhí)行后,就能達(dá)到和命令執(zhí)行一樣的效果,實(shí)現(xiàn)數(shù)據(jù)庫的遷移。
2. gormigrate
當(dāng)然,如果我們使用的是gorm
,那么推薦使用gromigrate
,gorm
本身提供了AutoMigrate
以及相應(yīng)的Migrator
的DDL接口,但是其更著重于ORM
層面的功能,在ORM Schema Version Control(數(shù)據(jù)庫版本控制)
方面有所欠缺。而gromigrate
就是一個輕量化的Schema Migration Helper(遷移助手)
,基于GORM AutoMigrate
和Migrator
進(jìn)行封裝,用于彌補(bǔ)這一塊的缺失。
和golang-migrate
不同的是,AutoMigrate
會根據(jù)程序中數(shù)據(jù)結(jié)構(gòu)的變化來改變表結(jié)構(gòu),無需自己寫sql
文件,我們仿照上述例子,來實(shí)現(xiàn)一遍。
2.1 InitSchema
應(yīng)用于初始化沒有表的場景,可以通過InitSchema
函數(shù)實(shí)現(xiàn)注冊函數(shù),注意,這里的注冊函數(shù)只有初始化函數(shù),沒有Rollback
操作。
package main import ( "context" "database/sql" "fmt" "log" "time" "github.com/go-gormigrate/gormigrate/v2" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/schema" ) type Person struct { ID int64 `gorm:"autoIncrement:true;primaryKey;column:id;type:bigint(20);not null"` Name string `gorm:"column:name;type:varchar(64);not null;comment:'姓名'"` Age int `gorm:"column:age;type:int(11);not null;comment:'年齡'"` } const ( username = "root" password = "IBHojwND.yo" hostname = "10.117.49.6:13306" dbname = "migration_test" ) func dsn(dbName string) string { return fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=true", username, password, hostname, dbName) } func createDBIfNotExist() error { db, err := sql.Open("mysql", dsn("")) if err != nil { log.Printf("Error %s when opening DB\n", err) return err } defer db.Close() ctx, cancelfunc := context.WithTimeout(context.Background(), 5*time.Second) defer cancelfunc() res, err := db.ExecContext(ctx, "CREATE DATABASE IF NOT EXISTS "+dbname) if err != nil { log.Printf("Error %s when creating DB\n", err) return err } no, err := res.RowsAffected() if err != nil { log.Printf("Error %s when fetching rows", err) return err } log.Printf("rows affected %d\n", no) return nil } func initScheme(db *gorm.DB) { m := gormigrate.New(db, gormigrate.DefaultOptions, []*gormigrate.Migration{}) m.InitSchema(func(db *gorm.DB) error { err := db.AutoMigrate( &Person{}, ) if err != nil { panic(err) } return nil }) err := m.Migrate() if err != nil { panic(err) } } func main() { err := createDBIfNotExist() if err != nil { panic(err) } db, err := gorm.Open(mysql.New(mysql.Config{ DSN: dsn(dbname), // DSN data source name DefaultStringSize: 256, // string 類型字段的默認(rèn)長度 DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的數(shù)據(jù)庫不支持 DontSupportRenameIndex: true, // 重命名索引時采用刪除并新建的方式,MySQL 5.7 之前的數(shù)據(jù)庫和 MariaDB 不支持重命名索引 DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的數(shù)據(jù)庫和 MariaDB 不支持重命名列 SkipInitializeWithVersion: false, // 根據(jù)當(dāng)前 MySQL 版本自動配置 }), &gorm.Config{ NamingStrategy: &schema.NamingStrategy{ TablePrefix: "", SingularTable: true, }, //SkipDefaultTransaction: true, // 開啟提高性能,https://gorm.io/docs/transactions.html }) if err != nil { panic(err) } initScheme(db) }
可以看到,此時的Person
結(jié)構(gòu)體:
type Person struct { ID int64 `gorm:"autoIncrement:true;primaryKey;column:id;type:bigint(20);not null"` Name string `gorm:"column:name;type:varchar(64);not null;comment:'姓名'"` Age int `gorm:"column:age;type:int(11);not null;comment:'年齡'"` }
然后在可以看到生成了兩個表:
MariaDB [migration_test6]> SHOW TABLES; +---------------------------+ | Tables_in_migration_test6 | +---------------------------+ | migrations | | person | +---------------------------+ 2 rows in set (0.000 sec)
其中,person
是我們生成的表,其結(jié)構(gòu)和Person
結(jié)構(gòu)體一致
MariaDB [migration_test6]> DESC `person`; +-------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | name | varchar(64) | NO | | NULL | | | age | int(11) | NO | | NULL | | +-------+-------------+------+-----+---------+----------------+ 3 rows in set (0.001 sec)
而migrations
表是遷移版本記錄表,可以發(fā)現(xiàn)其只有一列,記錄的就是版本號,InitSchema
成功后版本號是SCHEMA_INIT
。
MariaDB [migration_test6]> SELECT * FROM `migrations`; +-------------+ | id | +-------------+ | SCHEMA_INIT | +-------------+ 1 row in set (0.000 sec)
2.2 增量遷移
需要注意的是,當(dāng)使用InitSchema
+增量遷移
的時候,不能使用同一個實(shí)例對象。
2.2.1 新增一列
比如接下來,我們將Person
結(jié)構(gòu)體新增一個屬性Gender
表示性別:
type Person struct { ID int64 `gorm:"autoIncrement:true;primaryKey;column:id;type:bigint(20);not null"` Name string `gorm:"column:name;type:varchar(64);not null;comment:'姓名'"` Age int `gorm:"column:age;type:int(11);not null;comment:'年齡'"` Gender int `gorm:"column:gender;type:int(11);not null;comment:'性別:0-未知,1-男性,2-女性'"` }
其實(shí)這時候調(diào)用gorm.AutoMigrate
就已經(jīng)能夠自動創(chuàng)建列了,但是為了版本的管理,我們建立以下的版本管理:
func migration(db *gorm.DB) { m := gormigrate.New(db, gormigrate.DefaultOptions, []*gormigrate.Migration{ { ID: "20230616165624", Migrate: func(tx *gorm.DB) error { return tx.AutoMigrate(&Person{}) }, Rollback: func(tx *gorm.DB) error { return tx.Migrator().DropColumn("person", "gender") }, }, }) if err := m.Migrate(); err != nil { log.Fatalf("Could not migrate: %v", err) } log.Printf("Migration did run successfully") }
然后在main
函數(shù)最后加上migration(db)
:
func main() { ... // 初始化 initScheme(db) // 增量遷移 migration(db) }
執(zhí)行完之后,person
表的結(jié)構(gòu)變?yōu)?/p>
MariaDB [migration_test6]> DESC `person`; +--------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | name | varchar(64) | NO | | NULL | | | age | int(11) | NO | | NULL | | | gender | int(11) | NO | | NULL | | +--------+-------------+------+-----+---------+----------------+ 4 rows in set (0.001 sec)
然后會發(fā)現(xiàn)migrations
表里變?yōu)閮蓷l:
MariaDB [migration_test6]> SELECT * FROM `migrations`; +----------------+ | id | +----------------+ | 20230616165624 | | SCHEMA_INIT | +----------------+ 2 rows in set (0.000 sec)
2.2.2 新增name為index
同樣的,首先修改Person
結(jié)構(gòu)體,給Name
列加上了名為idx_name
的索引:
type Person struct { ID int64 `gorm:"autoIncrement:true;primaryKey;column:id;type:bigint(20);not null"` Name string `gorm:"index:idx_name;column:name;type:varchar(64);not null;comment:'姓名'"` Age int `gorm:"column:age;type:int(11);not null;comment:'年齡'"` Gender int `gorm:"column:gender;type:int(11);not null;comment:'性別:0-未知,1-男性,2-女性'"` }
然后新增版本:
func migration(db *gorm.DB) { m := gormigrate.New(db, gormigrate.DefaultOptions, []*gormigrate.Migration{ { ID: "20230616165624", Migrate: func(tx *gorm.DB) error { return tx.AutoMigrate(&Person{}) }, Rollback: func(tx *gorm.DB) error { return tx.Migrator().DropColumn("person", "gender") }, }, { ID: "20230619104829", Migrate: func(tx *gorm.DB) error { return tx.AutoMigrate(&Person{}) }, Rollback: func(tx *gorm.DB) error { return tx.Migrator().DropIndex("person", "idx_name") }, }, }) if err := m.Migrate(); err != nil { log.Fatalf("Could not migrate: %v", err) } log.Printf("Migration did run successfully") }
執(zhí)行完會發(fā)現(xiàn),person
表結(jié)構(gòu)如下:
MariaDB [migration_test6]> DESC `person`; +--------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | name | varchar(64) | NO | MUL | NULL | | | age | int(11) | NO | | NULL | | | gender | int(11) | NO | | NULL | | +--------+-------------+------+-----+---------+----------------+ 4 rows in set (0.001 sec)
而migrations
表里會新增一個版本:
MariaDB [migration_test6]> SELECT * FROM `migrations`; +----------------+ | id | +----------------+ | 20230616165624 | | 20230619104829 | | SCHEMA_INIT | +----------------+ 3 rows in set (0.000 sec)
2.2.3 加聯(lián)合索引
假如我們希望加一個名為idx_gender_name
的聯(lián)合索引,使用gender
和name
列作為索引,那么需要修改Person
結(jié)構(gòu)體如下,一定要在每個相關(guān)字段上標(biāo)注聯(lián)合索引idx_gender_name
,并且需要按照先后順序利用priority
確定優(yōu)先級,數(shù)字越低,優(yōu)先級越高。
type Person struct { ID int64 `gorm:"autoIncrement:true;primaryKey;column:id;type:bigint(20);not null"` Name string `gorm:"index:idx_name;index:idx_gender_name,priority:2;column:name;type:varchar(64);not null;comment:''姓名''"` // '姓名' Age int `gorm:"column:age;type:int(11);not null;comment:''年齡''"` // '年齡' Gender int `gorm:"index:idx_gender_name,priority:1;column:gender;type:int(11);not null;comment:''性別:0-未知,1-男性,2-女性''"` // '性別:0-未知,1-男性,2-女性' }
然后同樣加上版本規(guī)劃:
{ ID: "20230619112345", Migrate: func(tx *gorm.DB) error { return tx.AutoMigrate(&Person{}) }, Rollback: func(tx *gorm.DB) error { return tx.Migrator().DropIndex("person", "idx_gender_name") }, },
可以發(fā)現(xiàn)person
的索引如下:
MariaDB [migration_test6]> SHOW INDEX FROM `person`; +--------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +--------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | person | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | | | person | 1 | idx_name | 1 | name | A | 0 | NULL | NULL | | BTREE | | | | person | 1 | idx_gender_name | 1 | gender | A | 0 | NULL | NULL | | BTREE | | | | person | 1 | idx_gender_name | 2 | name | A | 0 | NULL | NULL | | BTREE | | | +--------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 4 rows in set (0.000 sec)
可以發(fā)現(xiàn),gormigrate
可以實(shí)現(xiàn)數(shù)據(jù)庫的版本遷移,并且是對Go
語言友好的。在微服務(wù)中,如果數(shù)據(jù)庫版本管理不是很復(fù)雜,且使用的是gorm
組件,那么可以使用gormigrate
。
3. 參考文獻(xiàn)
在 Golang 利用 golang-migrate 實(shí)現(xiàn) database migration
Golang后端學(xué)習(xí)筆記 — 3.使用Golang編寫和執(zhí)行數(shù)據(jù)庫遷移
對比 11 個 Go 數(shù)據(jù)庫遷移(migration)工具
Go 語言編程 — gormigrate GORM 的數(shù)據(jù)庫遷移助手
到此這篇關(guān)于Go數(shù)據(jù)庫遷移的實(shí)現(xiàn)步驟的文章就介紹到這了,更多相關(guān)Go數(shù)據(jù)庫遷移的實(shí)現(xiàn)步驟內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Go數(shù)據(jù)庫遷移的實(shí)現(xiàn)步驟
- Django切換數(shù)據(jù)庫和遷移數(shù)據(jù)詳解
- mongodb數(shù)據(jù)庫遷移變更的解決方案
- Django數(shù)據(jù)庫遷移常見使用方法
- django中的數(shù)據(jù)庫遷移的實(shí)現(xiàn)
- Django 遷移、操作數(shù)據(jù)庫的方法
- django遷移數(shù)據(jù)庫錯誤問題解決
- 詳解關(guān)于Django中ORM數(shù)據(jù)庫遷移的配置
- 關(guān)于django 數(shù)據(jù)庫遷移(migrate)應(yīng)該知道的一些事
相關(guān)文章
Go基本數(shù)據(jù)類型與string類型互轉(zhuǎn)
本文主要介紹了Go基本數(shù)據(jù)類型與string類型互轉(zhuǎn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03Go Asynq異步任務(wù)處理的實(shí)現(xiàn)
Asynq是一個新興的異步任務(wù)處理解決方案,它提供了輕量級的、易于使用的API,本文主要介紹了Go Asynq異步任務(wù)處理的實(shí)現(xiàn),具有一定的參考價值,感興趣的可以了解一下2023-06-06go語言實(shí)現(xiàn)mqtt協(xié)議的實(shí)踐
MQTT是一個基于客戶端-服務(wù)器的消息發(fā)布/訂閱傳輸協(xié)議。本文主要介紹了go語言實(shí)現(xiàn)mqtt協(xié)議的實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09Golang Printf,Sprintf,Fprintf 格式化詳解
這篇文章主要介紹了Golang Printf,Sprintf,Fprintf 格式化詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03RabbitMQ延時消息隊(duì)列在golang中的使用詳解
延時隊(duì)列常使用在某些業(yè)務(wù)場景,使用延時隊(duì)列可以簡化系統(tǒng)的設(shè)計(jì)和開發(fā)、提高系統(tǒng)的可靠性和可用性、提高系統(tǒng)的性能,下面我們就來看看如何在golang中使用RabbitMQ的延時消息隊(duì)列吧2023-11-11