Я помню, как однажды обнаружил, что документы BerkeleyDB действительно очень полезны, чтобы получить представление о том, как эти реализации могут работать, потому что это была / была база данных довольно низкого уровня, которая реализовывала транзакции без всей инфраструктуры реляционного планирования / планирования запросов.
Не все базы данных (даже те, которые вы упомянули) работают одинаково.Реализация нижнего уровня в PostgreSQL сильно отличается от реализации моментальных снимков как в Oracle, так и в SQL Server, хотя все они основаны на одном и том же подходе (MVCC: многоуровневое управление параллелизмом).
Один из способов реализации свойств ACIDсостоит в том, чтобы записать все изменения, которые вы («вы» здесь вносите некоторые изменения в транзакции) в базу данных, в «журнал транзакций», а также заблокировать каждую строку (единицу атомарности), чтобы убедиться, что никакая другая транзакция не может изменить ее впока вы не совершите или не откатитесь.В конце транзакции при фиксации вы просто записываете в журнал запись о том, что вы зафиксировали, и снимаете блокировки .Если вы выполняете откат, вам нужно пройтись назад по журналу транзакций, чтобы отменить все ваши изменения, поэтому каждое изменение, записанное в файл журнала, содержит «изображение перед» того, как данные выглядели изначально.(На практике он также будет содержать "after image", потому что журналы транзакций также воспроизводятся для восстановления после сбоя).Блокируя каждую изменяемую строку, параллельные транзакции не видят ваши изменения, пока вы не снимите блокировки после завершения транзакции.
MVCC - это метод, с помощью которого параллельные транзакции, которые хотят читать строки, вместо того, чтобы блокироватьсяВы обновляете, можете получить доступ к «до изображения» вместо.Каждая транзакция имеет идентичность и способ определить, какие данные транзакции она может «увидеть», а какие нет: разные правила для создания этого набора используются для реализации разных уровней изоляции.Таким образом, чтобы получить семантику «повторяемого чтения», транзакция должна найти «изображение перед» для любой строки, которая была обновлена, например, транзакцией, которая была запущена после нее.Вы могли бы наивно реализовать это, если бы транзакции просматривали журнал транзакций для изображений до, но на практике они хранятся где-то еще: следовательно, Oracle имеет отдельные повторы и отмена пробелов - повтор - это журнал транзакций, отмена - перед изображениями блоков дляодновременные транзакции для использования;SQL Server хранит предыдущие изображения в базе данных tempdb.В отличие от этого, PostgreSQL всегда создает новую копию строки всякий раз, когда она обновляется, поэтому изображения перед этим размещаются в самих блоках данных: у этого есть некоторые преимущества (операции фиксации и отката являются очень простыми операциями, без дополнительного пространства для управления) с компромиссами(эти устаревшие версии строк следует пылесосить в фоновом режиме).
В случае PostgreSQL (и это БД, с которой я больше всего знакома), каждая версия строки на диске имеет некоторые дополнительные свойства:транзакции должны проверить, чтобы решить, является ли эта версия строки "видимой" для них.Для простоты рассмотрим, что у них есть «xmin» и «xmax» - «xmin» указывает идентификатор транзакции, которая создала версию строки, «xmax» (необязательный) идентификатор транзакции, которая его удалила (что может включать создание новой версии строки дляпредставлять обновление в строке).Итак, вы начинаете со строки, созданной txn # 20:
xmin xmax id value
20 - 1 FOO
, а затем txn # 25 выполняет update t set value = 'BAR' where id = 1
20 25 1 FOO
25 - 1 BAR
До завершения txn # 25 новые транзакции будут знатьрассматривать его изменения как невидимые.Таким образом, транзакция, сканирующая эту таблицу, примет версию «FOO», поскольку ее xmax является невидимой транзакцией.
Если откат txn # 25 откатится, новые транзакции не будут сразу пропущены, но примут во внимание,txn # 25 зафиксировано или откатано.(PostgreSQL управляет таблицей поиска «commit status», чтобы обслужить это, pg_clog
) Поскольку txn # 25 откатывается, его изменения не видны, поэтому снова берется версия «FOO».(И версия "BAR" пропускается, поскольку ее транзакция xmin невидима)
Если txn # 25 зафиксирован, то версия строки "FOO" теперь не берется, так как ее транзакция xmax видима (то есть изменения, сделанные этой транзакцией, теперь видны).Напротив, версия строки "BAR" взята , поскольку ее транзакция xmin видна (и у нее нет xmax)
Пока txn # 25 все еще выполняется (опять же, это может бытьчтение из pg_clog
) любая другая транзакция, которая хочет обновить строку, будет ожидать завершения txn # 25, пытаясь получить общую блокировку для идентификатора транзакции .Я подчеркиваю этот момент, поэтому PostgreSQL обычно не имеет «блокировок строк» как таковых, а только блокировок транзакций: в каждой измененной строке нет блокировки в памяти.(Блокировка с помощью select ... for update
выполняется путем установки xmax, а флаг, указывающий, что xmax указывает только на блокировку, а не на удаление)
Oracle ... делает что-то похожее, но мои знания деталей намного опаснее.В Oracle каждой транзакции выдается номер изменения системы, который записывается в верхней части каждого блока.Когда блок изменяется, его исходное содержимое помещается в область отмены, а новый блок указывает на старый блок: таким образом, у вас по существу есть связанный список версий блока N - самая последняя версия в файле данных, с постепенно устаревшими версиями.в табличном пространстве отмены.И в верхней части блока находится список «заинтересованных транзакций», которые каким-то образом реализуют блокировку (опять же, не имея блокировки в памяти для каждой измененной строки), и я не могу вспомнить подробности этого.
Механизм изоляции моментальных снимков SQL Server, я полагаю, во многом похож на механизм Oracle, использующий базу данных tempdb для хранения изменяемых блоков, а не выделенного файла.
Надеюсь, этот бессвязный ответ был полезен.Это все из памяти, поэтому возможны большие объемы дезинформации, особенно для реализаций, не относящихся к postgresql.