golang数据库事务:如果单个exec语句失败,则继续

时间:2018-09-15 13:47:31

标签: go

我正在编写一个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.Rollback()以防stmt.Exec失败-但这会导致sql: statement is closed

2 个答案:

答案 0 :(得分:1)

对于Postgresql,您可以使用 ON CONFLICT DO NOTHING

我已经尝试了下面的代码,并且将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) ")

答案 1 :(得分:0)

我针对该问题的解决方案如下:

  • 不要创建单个事务并将所有语句添加到其中,而只运行它而不创建事务。
  • 在读取值时,生成新的go例程,并使事务并行运行(请注意连接限制)。
  • 没有并行化,性能下降了大约30%(从25k值的20s下降到30s-我们之前没有使用并行化)。
  • 通过并行化,性能提高了约4倍(至5秒)-请注意不要超出连接范围