← Назад к вопросам
Что такое оптимистичная блокировка?
3.0 Senior🔥 101 комментариев
#Архитектура и паттерны#Базы данных и SQL
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Оптимистичная блокировка (Optimistic Locking): концепция и примеры
Что такое оптимистичная блокировка
Оптимистичная блокировка это техника для управления конфликтами в многопользовательских системах без использования database locks. Предполагается что конфликты редки и проверяются только при сохранении.
Как это работает
1. Пользователь А читает данные (version = 1)
2. Пользователь Б читает те же данные (version = 1)
3. Пользователь А обновляет и сохраняет (version -> 2)
4. Пользователь Б пытается обновить
- Проверка: текущая версия 2, а он хочет обновить с версии 1
- КОНФЛИКТ! Не позволяем обновление
5. Пользователь Б должен перечитать и повторить
Пример с version column
-- Таблица users с version column
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100),
version INTEGER DEFAULT 1
);
-- Исходные данные
INSERT INTO users (id, name, email, version)
VALUES (1, 'John', 'john@example.com', 1);
-- Пользователь А: читает
SELECT * FROM users WHERE id = 1;
-- Получает: id=1, name=John, email=john@example.com, version=1
-- Пользователь Б: читает то же
SELECT * FROM users WHERE id = 1;
-- Получает: id=1, name=John, email=john@example.com, version=1
-- Пользователь А: обновляет
UPDATE users
SET name = 'John Updated', version = version + 1
WHERE id = 1 AND version = 1;
-- Успешно! version стал 2
-- Пользователь Б: пытается обновить с версией 1
UPDATE users
SET email = 'newemail@example.com', version = version + 1
WHERE id = 1 AND version = 1;
-- ОШИБКА! Ноль строк обновлено (version = 2, а он хочет 1)
Реализация в Node.js
// Модель
class User {
id: number;
name: string;
email: string;
version: number;
}
// Сервис с оптимистичной блокировкой
class UserService {
async updateUser(
id: number,
data: Partial<User>,
expectedVersion: number
): Promise<User> {
// UPDATE ... WHERE id = ? AND version = ?
const result = await db.query(
`UPDATE users
SET name = ?, email = ?, version = version + 1
WHERE id = ? AND version = ?`,
[data.name, data.email, id, expectedVersion]
);
// Если не обновлено ни одной строки - конфликт версии
if (result.affectedRows === 0) {
throw new OptimisticLockException(
`Version mismatch for user ${id}`
);
}
return this.getUser(id);
}
async getUser(id: number): Promise<User> {
const [user] = await db.query(
'SELECT * FROM users WHERE id = ?',
[id]
);
return user;
}
}
// Использование
const service = new UserService();
try {
// 1. Читаем текущие данные
const user = await service.getUser(1);
// { id: 1, name: 'John', email: 'john@example.com', version: 1 }
// 2. Обновляем (передаём ожидаемую версию)
const updated = await service.updateUser(
user.id,
{ email: 'newemail@example.com' },
user.version // Ожидаем версию 1
);
console.log('Успешно обновлено');
console.log(updated.version); // 2
} catch (error) {
if (error instanceof OptimisticLockException) {
console.log('Конфликт версии! Перечитайте данные и попробуйте снова');
}
}
Оптимистичная блокировка в ORM (Sequelize/TypeORM)
// TypeORM с версионированием
import { Entity, Column, Version } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
email: string;
@Version()
version: number; // Автоматически управляется
}
// Использование
const user = await userRepository.findOne(1);
user.email = 'newemail@example.com';
try {
await userRepository.save(user);
console.log('Успешно');
} catch (error) {
if (error.code === 'OPTIMISTIC_LOCK_FAILED') {
console.log('Конфликт версии!');
}
}
Оптимистичная vs Пессимистичная блокировка
ОПТИМИСТИЧНА (Optimistic Locking):
✅ Нет блокировок в БД
✅ Высокая производительность
✅ Хорошо для редких конфликтов
❌ Нужно обрабатывать конфликты
❌ Нужен столбец версии
ПЕССИМИСТИЧНА (Pessimistic Locking):
✅ Гарантирует отсутствие конфликтов
✅ Простая в использовании
❌ Блокирует строку (другие ждут)
❌ Медленнее при высокой конкуренции
❌ Риск deadlock'ов
Пессимистичная блокировка пример:
-- SELECT FOR UPDATE - блокирует строку
SELECT * FROM users WHERE id = 1 FOR UPDATE;
-- Другой пользователь ЖДЁТ пока первый не закончит транзакцию
BEGIN;
SELECT * FROM users WHERE id = 1 FOR UPDATE;
-- ... он это прочитает
UPDATE users SET name = 'New' WHERE id = 1;
COMMIT; -- Теперь второй пользователь может продолжить
Когда использовать
✅ ОПТИМИСТИЧНУЮ блокировку когда:
- Конфликты редки
- Много читателей, мало писателей
- Нужна высокая производительность
- Пример: обновление профиля пользователя
✅ ПЕССИМИСТИЧНУЮ блокировку когда:
- Конфликты частые
- Много одновременных обновлений
- Нельзя повторять операции
- Пример: бронирование билетов, перевод денег
Типы версионирования
// 1. Простой счётчик версии (самый частый)
version: 1 -> 2 -> 3 -> 4 ...
// 2. Временная метка
last_updated: '2024-01-01T10:00:00Z'
// 3. Хеш содержимого
data_hash: 'abc123def456'
// 4. ETag (в HTTP)
ETag: 'W/"123abc"'
Обработка конфликтов
// Автоматический retry
async function updateWithRetry(
id: number,
updates: Partial<User>,
maxRetries: number = 3
): Promise<User> {
for (let i = 0; i < maxRetries; i++) {
try {
const user = await getUser(id);
return await updateUser(id, updates, user.version);
} catch (error) {
if (error instanceof OptimisticLockException && i < maxRetries - 1) {
// Retry с задержкой
await delay(100 * (i + 1));
continue;
}
throw error;
}
}
}
// Merge конфликтных изменений
async function mergeUpdates(
id: number,
updates: Partial<User>
): Promise<User> {
const current = await getUser(id);
const merged = { ...current, ...updates };
return updateUser(id, merged, current.version);
}
Выводы
Оптимистичная блокировка:
- Предполагает что конфликты редки
- Использует столбец версии (или временную метку)
- Проверяет версию при обновлении
- Если версия не совпадает выбрасывает ошибку
- Отличная производительность без БД блокировок
- Нужна правильная обработка конфликтов