Транзакция базы данных golang: продолжить, если не удастся выполнить одну инструкцию - PullRequest
0 голосов
/ 15 сентября 2018

Я пишу приложение 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 голосов
/ 21 сентября 2018

Мое решение проблемы выглядит так:

  • Не создавайте одну транзакцию и не добавляйте в нее все операторы, вместо этого просто запустите ее, не создавая транзакции.
  • Когда значения считываются, порождают новые процедуры go и позволяют транзакции выполняться параллельно (будьте осторожны с ограничениями соединения).
  • Без распараллеливания производительность упала примерно на 30% (с 20 с для значений 25 К до 30 с - мы раньше не использовали распараллеливание).
  • При распараллеливании производительность увеличилась примерно в 4 раза (до 5 секунд) - просто будьте осторожны, оставаясь в пределах диапазона подключения
0 голосов
/ 16 сентября 2018

Для 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) ")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...