СУБД - Управление параллелизмом: Пособие для начинающих

Здравствуйте, будущие маги баз данных! Сегодня мы отправимся в увлекательное путешествие в мир управления параллелизмом в системах управления базами данных (СУБД). Не волнуйтесь, если вы новички; я буду вашим доброжелательным гидом, и мы шаг за шагом рассмотрим эту тему. Так что налейте себе чашечку кофе и погружайтесь с нами!

DBMS - Concurrency Control

Что такое управление параллелизмом?

Прежде чем мы углубимся в детали, давайте поймем, что такое управление параллелизмом. Представьте себе忙碌ный ресторан, где несколько официантов пытаются одновременно принимать заказы и подавать еду. Без properной координации наступит хаос! Точно так же, в базе данных, несколько пользователей или процессов могут пытаться одновременно доступ к данным и модифицировать их. Управление параллелизмом — это как старший официант, который обеспечивает безаварийную работу без конфликтов.

Теперь давайте рассмотрим основные методы управления параллелизмом в СУБД.

Протоколы на основе блокировок

Понимание блокировок

Блокировки — это как "Не беспокоить" на дверях отелей. Когда транзакция хочет получить доступ к данным, она устанавливает блокировку, говоря другим: "Эй, я здесь работаю!"

Типы блокировок

Тип блокировки Описание Сценарий использования
Shared Lock (S) Позволяет нескольким транзакциям читать данные Чтение данных без изменений
Exclusive Lock (X) Только одна транзакция может удерживать эту блокировку Запись или обновление данных

Протокол двухфазной блокировки (2PL)

Этот протокол похож на танец с двумя основными движениями:

  1. Фаза роста: Получение блокировок, не释放 ни одной.
  2. Фаза сужения: Освобождение блокировок, не получать новые.

Давайте рассмотрим простой пример:

BEGIN TRANSACTION;
-- Фаза роста
LOCK TABLE users IN EXCLUSIVE MODE;
UPDATE users SET balance = balance - 100 WHERE id = 1;
LOCK TABLE transactions IN EXCLUSIVE MODE;
INSERT INTO transactions (user_id, amount) VALUES (1, -100);
-- Фаза сужения
UNLOCK TABLE users;
UNLOCK TABLE transactions;
COMMIT;

В этом примере мы сначала блокируем необходимые таблицы, выполняем наши операции и затем освобождаем блокировки перед固定ом транзакции.

Зависания: Танец gone wrong

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

Чтобы предотвратить зависания, мы используем методы, такие как:

  1. Таймаут: Если транзакция ждет слишком долго, она откатывается.
  2. Обнаружение зависаний: Система активно ищет зависания и решает их.

Протоколы на основе меток времени

Теперь давайте изменим направление и поговорим о протоколах на основе меток времени. Это как присвоить каждой транзакции уникальный билет с меткой времени при входе в систему.

Основной протокол упорядочения по меткам времени (TO)

В этом протоколе мы используем метки времени для определения порядка конфликтующих операций. Это как обслуживание клиентов в зависимости от времени их arrival в ресторан.

Вот как это работает:

  1. Каждый элемент данных X имеет две метки времени:
  • W-timestamp(X): Самая большая метка времени любой транзакции, успешно записавшей X.
  • R-timestamp(X): Самая большая метка времени любой транзакции, успешно прочитавшей X.
  1. Для транзакции T, пытающейся прочитать X:
  • Если TS(T) < W-timestamp(X), T слишком поздно и должна быть отменена и перезапущена.
  • В противном случае, разрешить T читать X и установить R-timestamp(X) в max(R-timestamp(X), TS(T)).
  1. Для транзакции T, пытающейся записать X:
  • Если TS(T) < R-timestamp(X) или TS(T) < W-timestamp(X), T слишком поздно и должна быть отменена и перезапущена.
  • В противном случае, разрешить T записать X и установить W-timestamp(X) в TS(T).

Давайте рассмотрим пример:

class DataItem:
def __init__(self):
self.value = None
self.r_timestamp = 0
self.w_timestamp = 0

def read(transaction, data_item):
if transaction.timestamp < data_item.w_timestamp:
print(f"Транзакция {transaction.id} слишком поздно для чтения. Отмена...")
abort(transaction)
else:
print(f"Транзакция {transaction.id} читает значение: {data_item.value}")
data_item.r_timestamp = max(data_item.r_timestamp, transaction.timestamp)

def write(transaction, data_item, new_value):
if (transaction.timestamp < data_item.r_timestamp or
transaction.timestamp < data_item.w_timestamp):
print(f"Транзакция {transaction.id} слишком поздно для записи. Отмена...")
abort(transaction)
else:
print(f"Транзакция {transaction.id} пишет значение: {new_value}")
data_item.value = new_value
data_item.w_timestamp = transaction.timestamp

def abort(transaction):
print(f"Транзакция {transaction.id} отменена и будет перезапущена.")

В этом примере мы реализовали основные операции чтения и записи в соответствии с протоколом упорядочения по меткам времени. Система проверяет метки времени перед выполнением операций и обновляет их соответствующим образом.

Правило Write Rule Thomas: Умное оптимизация

Правило Write Rule Thomas позволяет пропустить некоторые "слишком поздние" записи без отмены транзакции. Это как позволить быстрому бегуну обогнать медленного в гонке.

Вот как это работает:

Если TS(T) < W-timestamp(X), вместо отмены T, мы просто игнорируем эту операцию записи. Это безопасно, так как записываемое значение уже устарело.

Давайте изменим нашу функцию записи, чтобы включить правило Write Rule Thomas:

def write_with_thomas_rule(transaction, data_item, new_value):
if transaction.timestamp < data_item.r_timestamp:
print(f"Транзакция {transaction.id} слишком поздно для записи. Отмена...")
abort(transaction)
elif transaction.timestamp < data_item.w_timestamp:
print(f"Запись транзакции {transaction.id} ignored из-за правила Write Rule Thomas.")
else:
print(f"Транзакция {transaction.id} пишет значение: {new_value}")
data_item.value = new_value
data_item.w_timestamp = transaction.timestamp

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

Заключение

Уф! Мы covered много сегодня, от протоколов на основе блокировок до протоколов на основе меток времени. Помните, управление параллелизмом — это все о поддержании порядка в хаотическом мире одновременных операций с базой данных. Это как быть регулировщиком на оживленном перекрестке, обеспечивая, чтобы все добирались до места назначения без столкновений.

Пока вы продолжаете свое путешествие в мире баз данных, вы встретите более сложные концепции и методы. Но пока, похлопайте себя по спине за овладение этими основными концепциями управления параллелизмом!

Продолжайте практиковаться, оставайтесь любопытными и счастливого кодирования!

Credits: Image by storyset