Beego迁移执行PostgreSQL CONCURRENTLY创建索引报错如何解决
Beego ORM 迁移模块默认没有提供类似disable_ddl_transaction的配置开关,但可以通过两种方案实现并发创建索引的需求。
方案一:拆分SQL语句,提前提交默认事务(最简单方案)
Beego会为每个迁移任务默认开启一个事务,我们只需先手动提交这个事务,再执行CONCURRENTLY语句,最后重新开启事务适配Beego的默认提交逻辑即可。注意不要将多条语句拼接在同一个m.SQL调用中,拆分单独执行才能生效:
// Run the migrations func (m *AddConstraintToUser_20210928_223906) Up() { // 提交Beego默认开启的事务,退出事务块 m.SQL("COMMIT;") // 独立执行并发创建索引语句,此时处于自动提交模式,无事务包裹 m.SQL("CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS uniq_email ON users (email) WHERE status_id=1;") m.SQL("CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS uniq_name ON users (name) WHERE status_id=1;") // 重新开启事务,适配Beego迁移后续的默认提交逻辑,避免执行报错 m.SQL("BEGIN;") } // Reverse the migrations func (m *AddConstraintToUser_20210928_223906) Down() { // 提交Beego默认开启的事务 m.SQL("COMMIT;") // 并发删除索引 m.SQL("DROP INDEX CONCURRENTLY IF EXISTS uniq_email;") m.SQL("DROP INDEX CONCURRENTLY IF EXISTS uniq_name;") // 重新开启事务 m.SQL("BEGIN;") }
方案二:手动创建数据库连接执行(更可控)
如果方案一因为框架版本差异仍然报错,可以直接绕过Beego迁移的默认SQL执行逻辑,自己初始化数据库连接,开启自动提交模式执行语句:
package main import ( "database/sql" "github.com/beego/beego/v2/client/orm/migration" "github.com/beego/beego/v2/core/config" _ "github.com/lib/pq" ) // DO NOT MODIFY type AddConstraintToUser_20210928_223906 struct { migration.Migration } // DO NOT MODIFY func init() { m := &AddConstraintToUser_20210928_223906{} m.Created = "20210928_223906" migration.Register("AddConstraintToUser_20210928_223906", m) } // Run the migrations func (m *AddConstraintToUser_20210928_223906) Up() { // 读取Beego配置中的数据库连接信息,可根据自己的配置路径调整 pgDSN, err := config.String("postgres::dsn") if err != nil { panic(err) } // 初始化数据库连接,默认开启自动提交模式 db, err := sql.Open("postgres", pgDSN) if err != nil { panic(err) } defer db.Close() // 执行并发创建索引语句 _, err = db.Exec("CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS uniq_email ON users (email) WHERE status_id=1;") if err != nil { panic(err) } _, err = db.Exec("CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS uniq_name ON users (name) WHERE status_id=1;") if err != nil { panic(err) } } // Reverse the migrations func (m *AddConstraintToUser_20210928_223906) Down() { pgDSN, err := config.String("postgres::dsn") if err != nil { panic(err) } db, err := sql.Open("postgres", pgDSN) if err != nil { panic(err) } defer db.Close() _, err = db.Exec("DROP INDEX CONCURRENTLY IF EXISTS uniq_email;") if err != nil { panic(err) } _, err = db.Exec("DROP INDEX CONCURRENTLY IF EXISTS uniq_name;") if err != nil { panic(err) } }
注意事项
- 执行
CREATE INDEX CONCURRENTLY过程中不要中断迁移任务,否则会在数据库中留下无效的索引占位,需要手动清理 - 并发创建索引的耗时会比普通创建更长,但不会锁表,不影响业务正常读写
- 以上逻辑仅适用于PostgreSQL数据库,其他数据库不支持CONCURRENTLY语法
内容的提问来源于stack exchange,提问作者Aashish Passrija
相关产品推荐
相关产品推荐

