我正在编写一个Go应用程序,该应用程序应该将文件中的数千个值插入到数据库中。只要所有值都可以插入到数据库中,就可以正常工作。如果其中一个查询失败,则之后的所有查询都将失败,因为pq: : current transaction is aborted, commands ignored until end of transaction block
我想插入所有元素,如果一个元素的插入失败,应该跳过它并插入其他元素。
我的代码:
func (db *Database) Insert(values []Value) (transerr error) {
tx, err := db.Begin()
if transerr != nil {
return nil, err
}
defer func() {
if err != nil {
tx.Rollback()
} else {
tx.Commit()
}
}
stmt, err := tx.Prepare("INSERT INTO foo VALUES (?)")
if err != nil {
return err
}
defer stmt.Close()
for _, value : range values {
_, err = stmt.Exec(value)
if err != nil {
log.Error(err)
}
}
return nil
}
我试图添加一个tx。回滚(( 以防万一 stmt.执行失败 - 但这会导致sql: statement is closed
。
对于 Postgresql,您可以使用在冲突时什么都不做
我已经尝试了下面的代码,上面有 postgresql db,它忽略了有错误的插入行。我做了一些其他的改变来尝试我这边。您可以忽略我的其他更改。
func insert(db *sql.DB, values []string) error {
tx, err := db.Begin()
if err != nil {
return err
}
defer tx.Commit()
stmt, err := tx.Prepare("INSERT INTO foo ( foo_col) VALUES ($1) ON CONFLICT DO NOTHING")
if err != nil {
fmt.Println("errro at stmt", err)
return err
}
defer stmt.Close()
for _, value := range values {
_, err = stmt.Exec(value)
if err != nil {
fmt.Println(value, err)
}
}
return nil
}
对于 mysql,您可以使用INSERT IGNORE
stmt, err := tx.Prepare("INSERT IGNORE INTO foo ( foo_col) VALUES ($1) ")
我的问题解决方案如下所示:
- 不要创建单个事务并将所有语句添加到其中,而只需运行它而不创建事务。
- 读入值时,生成新的 go 例程,并让事务并行运行(注意连接限制(。
- 如果没有并行化,性能下降了大约 30%(从 20k 值的 20 秒下降到 30 秒 - 我们之前没有使用并行化(。
- 通过并行化,性能提高了大约 4 倍(到 5 秒( - 只是要小心,你保持在连接范围内