Sobes.tech
Principal
28

// У нас есть таблица с товаром, у которого есть счетчик просмотра // table goods: // ID | cnt_view // ----------- // 1 | 100 // 2 пользователя одновременно начали смотреть товар с id = 1 // и у нас в базе запустилось 2 параллельные транзакции // нужно объяснить какой будет результат? postgress func IncrementView(ctx context.Context, db *sql.DB, id int64) error { tx, err := db.BeginTx(ctx, nil) if err != nil { return err } defer tx.Rollback() var cnt int err = tx.QueryRowContext(ctx, "SELECT cnt_view FROM goods WHERE id = $1", id, ).Scan(&cnt) if err != nil { return err } cnt++ _, err = tx.ExecContext(ctx, "UPDATE goods SET cnt_view = $1 WHERE id = $2", cnt, id, ) if err != nil { return err } return tx.Commit() }

Компании, где спрашивали
101ХР101ХР

Ответ от нейросети

sobes.tech AI

В PostgreSQL при двух параллельных транзакциях, которые читают и обновляют одно и то же значение счетчика просмотров, возможна ситуация гонки (race condition).

Что происходит:

  1. Обе транзакции начинают и читают текущее значение cnt_view = 100.
  2. Обе увеличивают локальную переменную cnt до 101.
  3. Обе пытаются записать cnt_view = 101 обратно в таблицу.

В итоге, несмотря на два просмотра, счетчик увеличится только на 1, а не на 2.

Это происходит потому, что транзакции не синхронизированы и не используют блокировки или атомарные операции.

Чтобы избежать этой проблемы, можно:

  • Использовать оператор UPDATE с инкрементом напрямую, без предварительного чтения:
UPDATE goods SET cnt_view = cnt_view + 1 WHERE id = $1;
  • Либо использовать блокировку строки (SELECT ... FOR UPDATE) перед чтением, чтобы гарантировать последовательный доступ.

  • Либо использовать сериализуемый уровень изоляции транзакций, но это может снизить производительность.

Пример исправленной функции:

func IncrementView(ctx context.Context, db *sql.DB, id int64) error {
    _, err := db.ExecContext(ctx, "UPDATE goods SET cnt_view = cnt_view + 1 WHERE id = $1", id)
    return err
}

Таким образом, счетчик будет корректно увеличиваться при параллельных запросах.