← Назад к вопросам

Какие знаешь опции для SELECT FOR UPDATE?

2.0 Middle🔥 161 комментариев
#Базы данных

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Опции SELECT FOR UPDATE в Go (PostgreSQL)

В Go при работе с транзакциями и конкурентным доступом к данным часто используется SELECT FOR UPDATE. Это пессимистическая блокировка, которая предотвращает модификацию выбранных строк другими транзакциями до завершения текущей. Рассмотрим основные опции и их применение.

Ключевые опции FOR UPDATE

1. FOR UPDATE

Базовая блокировка строк для обновления. Другие транзакции не могут изменять или блокировать эти строки до коммита.

SELECT * FROM orders WHERE status = 'pending' FOR UPDATE;

2. FOR NO KEY UPDATE

Более мягкая блокировка: разрешает другим транзакциям блокировать строки FOR KEY SHARE (например, при ссылочной целостности). Не блокирует операции, затрагивающие только внешние ключи.

SELECT * FROM products WHERE stock > 0 FOR NO KEY UPDATE;

3. FOR SHARE

Блокировка для чтения: другие транзакции могут тоже заблокировать строки FOR SHARE, но не могут изменить их. Полезна для совместного чтения с гарантией неизменности данных.

SELECT * FROM accounts WHERE balance > 1000 FOR SHARE;

4. FOR KEY SHARE

Самая слабая блокировка: защищает только от изменений значений ключей. Другие транзакции могут менять неключевые столбцы. Используется для поддержания ссылочной целостности.

SELECT * FROM customers WHERE id = 5 FOR KEY SHARE;

Дополнительные модификаторы

NOWAIT

Если строки уже заблокированы другой транзакцией, запрос сразу завершится ошибкой вместо ожидания.

// Пример в Go с github.com/jackc/pgx
_, err := tx.Query(ctx, "SELECT * FROM orders FOR UPDATE NOWAIT")
if err != nil {
    // Обработка ошибки блокировки
}

SKIP LOCKED

Пропускает уже заблокированные строки, возвращая только доступные. Полезно для реализации очередей задач.

SELECT * FROM task_queue WHERE processed = false FOR UPDATE SKIP LOCKED LIMIT 10;

Практическое применение в Go

При работе с поддержкой транзакций в Go (например, с библиотеками pgx, sqlx), важно учитывать:

  1. Уровни изоляции транзакций: SELECT FOR UPDATE обычно используется с Repeatable Read или Serializable.
  2. Время удержания блокировки: блокировка действует до завершения транзакции (коммит или откат).
  3. Индексы и производительность: блокировка работает эффективнее при выборке по индексируемым полям.
// Пример транзакции с FOR UPDATE
func ReserveProduct(ctx context.Context, tx pgx.Tx, productID int) error {
    var stock int
    // Блокировка строки для изменения
    err := tx.QueryRow(ctx, 
        "SELECT stock FROM products WHERE id = $1 FOR UPDATE", 
        productID,
    ).Scan(&stock)
    if err != nil {
        return err
    }
    
    if stock <= 0 {
        return errors.New("товар отсутствует")
    }
    
    // Изменение заблокированной строки
    _, err = tx.Exec(ctx, 
        "UPDATE products SET stock = stock - 1 WHERE id = $1", 
        productID,
    )
    return err
}

Типичные сценарии использования

  • Финансовые операции: блокировка счетов перед списанием/зачислением
  • Управление запасами: резервирование товаров
  • Очереди задач: конкурентная обработка задач с SKIP LOCKED
  • Сложные агрегации: блокировка данных для консистентных отчетов

Важные предостережения

  1. Дедлоки: неправильный порядок блокировок может привести к взаимным блокировкам
  2. Производительность: длительные блокировки снижают параллелизм
  3. Изоляция: в режиме Read Committed возможны фантомные чтения

В Go особенно важно всегда закрывать транзакции (коммит или роллбэк) и использовать контексты с таймаутами для предотвращения вечных блокировок.

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

err := pgx.BeginFunc(ctx, db, func(tx pgx.Tx) error {
    // Использование FOR UPDATE с NOWAIT для быстрого отказа
    return ReserveProductWithTimeout(ctx, tx, productID)
})

Выбор конкретной опции зависит от требований конкурентности, целостности данных и производительности вашего приложения.

Какие знаешь опции для SELECT FOR UPDATE? | PrepBro