【问题标题】:golang database transaction: continue if single exec statement failsgolang 数据库事务:如果单个 exec 语句失败,则继续
【发布时间】:2018-09-21 06:14:30
【问题描述】:

我正在编写一个 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

【问题讨论】:

  • 我建议您对查询进行结构化,这样就不会发生这些类型的错误,但无论如何......底层数据库技术是什么?
  • 数据库是 MySQL/PostgreSQL - 构造查询非常困难,因为输入是随机的 - 但我会试一试,感谢您的提示!
  • 您的插入失败是因为INSERTed 值与数据库中已经存在的其他行的约束(例如UNIQUE 索引)冲突,还是因为其他问题?
  • 事务的重点是确保所有或NONE的语句都执行。这不是你想要的。不要为交易烦恼。
  • 数据互不相关——数据库只是一个存储,数据互不相关。我考虑过不使用任何事务——这对性能有影响吗?谢谢你的回答:)

标签: go


【解决方案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) ")

【讨论】:

    【解决方案2】:

    我的问题解决方案如下:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-01-30
      • 2017-02-19
      • 2014-12-31
      • 1970-01-01
      • 2020-10-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多