golang实现MySQL和PostgreSQL多库迁移管理的插件库libschema的使用
Golang实现MySQL和PostgreSQL多库迁移管理的插件库libschema的使用
安装
go get github.com/muir/libschema
库介绍
Libschema为Go库提供了一种管理自己数据库迁移的方式。将迁移与库绑定支持两个功能:一是源代码局部性,迁移可以靠近使用迁移所涉及表的代码;二是支持第三方库中的迁移。
注册和执行迁移
迁移首先被注册:
schema := libschema.New(ctx, libschema.Options{})
sqlDB, err := sql.Open("postgres", "....")
database, err := lspostgres.New(logger, "main-db", schema, sqlDB)
// 或者
database, singlestoreHelper, err := lssinglestore.New(logger, "main-db", schema, sqlDB)
// 或者
database, mysqlHelper, err := lssinglestore.New(logger, "main-db", schema, sqlDB)
database.Migrations("MyLibrary",
lspostgres.Script("createUserTable", `
CREATE TABLE users (
name text,
id bigint
)`
}),
lspostgres.Script("addLastLogin", `
ALTER TABLE users
ADD COLUMN last_login timestamp
`
}),
)
然后按注册顺序运行迁移:
err := schema.Migrate(context)
计算迁移
迁移可以是SQL字符串,也可以在Go中完成:
database.Migrations("MyLibrary",
lspostgres.Computed("importUsers", func(_ context.Context, _ Migration, tx *sql.Tx) error {
// 在这里导入用户的代码
}),
)
异步迁移
迁移的默认模式是在调用schema.Migrate()
时同步运行。异步迁移在调用schema.Migrate()
时启动,但在后台的goroutine中运行。如果在异步迁移之后有后续迁移,它们将强制异步迁移变为同步,除非它们也是异步的。
版本阻塞
迁移可以与特定的代码版本绑定,以便在满足条件之前不运行。这是通过SkipRemainingIf
完成的。可以用于回填数据。
database.Migrations("MyLibrary",
...
lspostgres.Script("addColumn", `
ALTER TABLE users
ADD COLUMN rating`,
libschema.SkipThisAndFollowingIf(func() bool {
return semver.Compare(version(), "3.11.3") < 1
})),
lspostgres.Script("fillInRatings", `
UPDATE users
SET rating = ...
WHERE rating IS NULL;
ALTER TABLE users
MODIFY COLUMN rating SET NOT NULL;`,
libschema.Asychronous),
)
跨库依赖
尽管最好一个库的模式独立于另一个库的模式,但有时这是不可能的,特别是如果你想强制执行外键约束。使用After()
指定跨库依赖。
database.Migrations("users",
...
lspostgres.Script("addOrg", `
ALTER TABLE users
ADD COLUMN org TEXT,
ADD ADD CONSTRAINT orgfk FOREIGN KEY (org)
REFERENCES org (name) `,
libschema.After("orgs", "createOrgTable")),
)
database.Migrations("orgs",
...
lspostgres.Script("createOrgTable", `
...
`),
)
事务
对于支持元数据事务的数据库,所有迁移都将用BEGIN
和COMMIT
包装。对于不支持元数据事务的数据库,迁移将被拆分为单个命令并逐个运行。如果只有部分命令成功,迁移将被标记为部分完成。如果迁移被修改,只要不修改早期部分,可以重新尝试后面的部分。这不适用于Compute()
迁移。
命令行
OverrideOptions
可以作为命令行标志添加,改变调用schema.Migrate()
的行为:
--migrate-only 在完成迁移后调用os.Exit()
--migrate-database 仅迁移一个逻辑数据库(必须匹配NewDatabase)
--migrate-dsn 覆盖*sql.DB
--no-migrate 跳过所有迁移
--error-if-migrate-needed 如果有未完成的同步迁移则返回错误
--migrate-all-synchronously 将异步迁移视为同步
完整示例
以下是一个完整的示例,展示如何使用libschema管理MySQL和PostgreSQL的迁移:
package main
import (
"context"
"database/sql"
"log"
"os"
"github.com/muir/libschema"
"github.com/muir/libschema/lsmysql"
"github.com/muir/libschema/lspostgres"
)
func main() {
ctx := context.Background()
logger := log.New(os.Stdout, "", log.LstdFlags)
// 创建schema实例
schema := libschema.New(ctx, libschema.Options{
Logger: logger,
})
// PostgreSQL连接
pgDB, err := sql.Open("postgres", "user=postgres dbname=test sslmode=disable")
if err != nil {
log.Fatal(err)
}
// MySQL连接
mysqlDB, err := sql.Open("mysql", "user:password@/test")
if err != nil {
log.Fatal(err)
}
// 创建PostgreSQL数据库实例
pgDatabase, err := lspostgres.New(logger, "postgres-db", schema, pgDB)
if err != nil {
log.Fatal(err)
}
// 创建MySQL数据库实例
mysqlDatabase, mysqlHelper, err := lsmysql.New(logger, "mysql-db", schema, mysqlDB)
if err != nil {
log.Fatal(err)
}
// 注册PostgreSQL迁移
pgDatabase.Migrations("MyApp",
lspostgres.Script("createUsersTable", `
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
)`,
),
lspostgres.Script("addUserStatus", `
ALTER TABLE users
ADD COLUMN status TEXT DEFAULT 'active'`,
),
)
// 注册MySQL迁移
mysqlDatabase.Migrations("MyApp",
lsmysql.Script("createProductsTable", `
CREATE TABLE products (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
price DECIMAL(10,2) NOT NULL
)`,
),
lsmysql.Computed("importSampleProducts", func(ctx context.Context, _ libschema.Migration, tx *sql.Tx) error {
_, err := tx.ExecContext(ctx, `
INSERT INTO products (name, price) VALUES
('Product 1', 10.99),
('Product 2', 20.50),
('Product 3', 15.75)`)
return err
}),
)
// 运行所有迁移
err = schema.Migrate(ctx)
if err != nil {
log.Fatal(err)
}
log.Println("所有迁移成功完成")
// 使用mysqlHelper进行MySQL特定操作
err = mysqlHelper.Ping()
if err != nil {
log.Fatal(err)
}
}
支持的数据库
Libschema需要特定数据库的驱动程序:
- PostgreSQL支持在
"github.com/muir/libschema/lspostgres"
- MySQL支持在
"github.com/muir/libschema/lsmysql"
- SingleStore支持在
"github.com/muir/libschema/lssinglestore"
当前libschema支持:PostgreSQL、SingleStore、MySQL。添加其他数据库相对容易。
总结
Libschema是一个强大的Go库,用于管理MySQL和PostgreSQL等多数据库的迁移。它提供了灵活的迁移定义方式,支持同步和异步迁移,跨库依赖,版本控制等功能。通过将迁移与代码紧密结合,它简化了数据库模式的管理和维护。
更多关于golang实现MySQL和PostgreSQL多库迁移管理的插件库libschema的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang实现MySQL和PostgreSQL多库迁移管理的插件库libschema的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
使用libschema实现MySQL和PostgreSQL多库迁移管理
libschema是一个用于管理数据库schema迁移的Go库,支持多种数据库包括MySQL和PostgreSQL。下面我将介绍如何使用libschema来管理多数据库的迁移。
基本概念
libschema的核心概念包括:
- Migration:表示一个数据库迁移操作
- Library:管理迁移的集合
- Schema:代表一个数据库schema的迁移状态
安装
go get github.com/muir/libschema
基本使用示例
1. 初始化配置
package main
import (
"context"
"database/sql"
"fmt"
"log"
"os"
"github.com/muir/libschema"
"github.com/muir/libschema/postgres"
"github.com/muir/libschema/mysql"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
)
func main() {
// 创建数据库连接
pgDB, err := sql.Open("postgres", os.Getenv("POSTGRES_DSN"))
if err != nil {
log.Fatalf("PostgreSQL连接失败: %v", err)
}
defer pgDB.Close()
mysqlDB, err := sql.Open("mysql", os.Getenv("MYSQL_DSN"))
if err != nil {
log.Fatalf("MySQL连接失败: %v", err)
}
defer mysqlDB.Close()
// 创建libschema实例
options, err := libschema.OptionsFromEnvironment()
if err != nil {
log.Fatalf("配置错误: %v", err)
}
schema, err := libschema.New(context.Background(), options)
if err != nil {
log.Fatalf("初始化libschema失败: %v", err)
}
// 添加PostgreSQL数据库
pgSchema := postgres.New("myapp", schema, pgDB)
// 添加MySQL数据库
mysqlSchema := mysql.New("myapp", schema, mysqlDB)
// 定义迁移
err = defineMigrations(pgSchema, mysqlSchema)
if err != nil {
log.Fatalf("定义迁移失败: %v", err)
}
// 执行迁移
err = schema.Migrate(context.Background())
if err != nil {
log.Fatalf("迁移失败: %v", err)
}
fmt.Println("迁移完成!")
}
2. 定义迁移
func defineMigrations(pgSchema *postgres.Schema, mysqlSchema *mysql.Schema) error {
// PostgreSQL迁移
_, err := pgSchema.Migrations("DBSchema",
libschema.Script("create_users_table", `
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
`),
libschema.Script("add_user_status", `
ALTER TABLE users ADD COLUMN status TEXT DEFAULT 'active';
`),
)
if err != nil {
return fmt.Errorf("PostgreSQL迁移定义失败: %w", err)
}
// MySQL迁移
_, err = mysqlSchema.Migrations("DBSchema",
libschema.Script("create_products_table", `
CREATE TABLE products (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
price DECIMAL(10,2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
`),
libschema.Script("add_product_category", `
ALTER TABLE products ADD COLUMN category VARCHAR(100) DEFAULT 'general';
`),
)
if err != nil {
return fmt.Errorf("MySQL迁移定义失败: %w", err)
}
return nil
}
高级功能
1. 条件迁移
// 只在开发环境中执行的迁移
if os.Getenv("ENV") == "development" {
_, err = pgSchema.Migrations("DevSchema",
libschema.Script("dev_data", `
INSERT INTO users (name, email) VALUES
('Dev User', 'dev@example.com');
`),
)
}
2. Go函数迁移
// 使用Go函数而不是SQL脚本的迁移
_, err = pgSchema.Migrations("Functions",
libschema.Do("create_hello_function", func(ctx context.Context, db *sql.DB) error {
_, err := db.ExecContext(ctx, `
CREATE OR REPLACE FUNCTION hello(name TEXT)
RETURNS TEXT AS $$
BEGIN
RETURN 'Hello ' || name;
END;
$$ LANGUAGE plpgsql;
`)
return err
}),
)
3. 迁移依赖
// 定义有依赖关系的迁移
_, err = pgSchema.Migrations("Dependent",
libschema.Script("create_posts_table", `
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id),
title TEXT NOT NULL,
content TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
`, libschema.After("create_users_table")), // 依赖users表的创建
)
最佳实践
- 版本控制:将迁移脚本与代码一起纳入版本控制
- 命名规范:使用有意义的迁移名称,如"create_users_table"
- 原子性:每个迁移应该是原子的,要么完全成功,要么完全失败
- 回滚考虑:设计迁移时要考虑如何回滚
- 环境分离:区分开发、测试和生产环境的迁移
错误处理
libschema提供了详细的错误信息,建议在生产环境中记录这些错误:
err = schema.Migrate(context.Background())
if err != nil {
if migrationErr, ok := err.(libschema.MigrationErrors); ok {
for dbName, dbErr := range migrationErr {
log.Printf("数据库 %s 迁移失败: %v", dbName, dbErr)
}
} else {
log.Printf("迁移错误: %v", err)
}
os.Exit(1)
}
libschema是一个功能强大且灵活的数据库迁移管理工具,特别适合需要在多种数据库上维护schema的项目。通过合理组织迁移脚本和利用其高级功能,可以有效地管理复杂的数据库变更。