Ответ
Оптимистичная блокировка (Optimistic Concurrency Control) — это стратегия для работы с параллельным доступом к данным в БД, которая предполагает, что конфликты при одновременном изменении одной записи маловероятны. Вместо явной блокировки записи на время транзакции, конфликт обнаруживается в момент фиксации изменений и должен быть обработан приложением.
Принцип работы:
- Каждая запись в таблице имеет поле-версию (обычно
versionилиupdated_at). - Клиент, читая запись, также получает текущее значение версии.
- При обновлении запись модифицируется только если её версия в БД совпадает с той, что была получена клиентом. Версия увеличивается.
- Если версии не совпали (запись уже обновил кто-то другой), операция обновления не затрагивает ни одной строки, и приложение получает сигнал о конфликте.
Пример реализации в Node.js с PostgreSQL и node-postgres:
async function updateProduct(productId, newPrice, clientVersion) {
const query = `
UPDATE products
SET price = $1, version = version + 1, updated_at = NOW()
WHERE id = $2 AND version = $3
RETURNING id, price, version;
`;
const values = [newPrice, productId, clientVersion];
const result = await pool.query(query, values);
if (result.rowCount === 0) {
// Конфликт версий: запись не найдена с указанной версией
// Здесь можно либо выбросить ошибку, либо реализовать повторную попытку
const currentRecord = await pool.query('SELECT * FROM products WHERE id = $1', [productId]);
throw new Error(`Конфликт оптимистичной блокировки. Текущая версия: ${currentRecord.rows[0].version}. Ваша версия: ${clientVersion}.`);
}
return result.rows[0]; // Возвращаем обновленную запись
}
// Использование
// 1. Сначала читаем продукт: const product = await pool.query('SELECT id, price, version FROM products WHERE id=123');
// 2. Пытаемся обновить: try { await updateProduct(123, 99.99, product.rows[0].version); } catch(err) { /* Обработка конфликта */ }
Плюсы:
- Высокая производительность, так как нет долгих блокировок, ожидающих завершения транзакций.
- Хорошо подходит для сценариев с высокой частотой чтения и низкой частотой конфликтующих записей (например, редактирование профиля пользователя).
Минусы:
- Требует дополнительного поля в таблице.
- Приложение должно быть готово обрабатывать ошибки конфликтов (например, предлагать пользователю обновить данные и повторить действие).
- Не подходит для сценариев, где конфликты записи происходят очень часто.
В экосистеме Node.js эта стратегия часто реализуется на уровне ORM (например, в Sequelize есть опция version).