Как базы данных выполняют атомарный ввод / вывод? - PullRequest
18 голосов
/ 09 февраля 2012

Базы данных, такие как Oracle, SQL Server и т. Д., Очень хороши в целостности данных. Если бы я хотел написать хранилище данных, которое, как я знал, либо сохраняло бы некоторые данные, либо было бы неудачным (т. Е. Быть ACID), то я использовал бы базу данных, подобную MySQL, в качестве хранилища фактического , потому что эти проблемы уже решены.

Тем не менее, как выпускник, не имеющий квалификации, мне интересно, как ACID на самом деле работает на очень низком уровне. Я знаю, что Oracle, например, все время записывает данные в «онлайн-журналы повторов», а затем выполняет «фиксацию» в какой-то момент, когда приложение сообщает, что транзакция должна быть зафиксирована.

Это та стадия «фиксации», которую я хочу увеличить и понять. Это просто запись «еще одного байта» на диск или переключение 0 в 1, чтобы сказать, что данная строка успешно сохранена?

Ответы [ 3 ]

12 голосов
/ 09 февраля 2012

Я помню, как однажды обнаружил, что документы 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.

2 голосов
/ 09 февраля 2012

Общий обзор для Oracle:

Каждый сеанс Oracle уникален, и каждый сеанс может иметь 1 * активную транзакцию. Когда транзакция начинается, Oracle назначает ей монотонно увеличивающийся номер системного изменения (SCN). Когда Oracle обновляет / вставляет / удаляет строки, Oracle блокирует интересующие строки в таблице и поддерживает индекс путем обновления заголовка в записываемых блоках, а также сохранения «оригинальных» блоков в пространство отката (отмены) оракула. Oracle также записывает записи журнала повторов в буфер памяти, описывающий изменения, вносимые как в блоки таблицы и индекса, так и в блоки отмены. Обратите внимание, что сделанные изменения вносятся в память, а не непосредственно на диск.

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

При откате Oracle использует информацию в откате (отменить), чтобы исключить сделанные изменения.

Так, как это реализует КИСЛОТУ:

Атомность: моя сессия, моя транзакция, все идет или ничего не происходит. Когда я фиксирую, я ничего не могу сделать, пока не завершится фиксация

Согласованность: Oracle проверяет, что даты - это даты, символьные данные - символьные данные, числа действительны. То же самое с проверочными ограничениями. Ограничения внешнего ключа полагаются на проверку, чтобы убедиться, что родительский ключ, на который ссылается, действителен - и не был обновлен или удален транзакцией в полете. Если родительский ключ был обновлен или удален, ваш оператор зависает - на самом деле он находится в подвешенном состоянии - в ожидании оператора, влияющего на родительскую запись, для принятия или отката.

Независимость: Помните те номера системных изменений? Если вы не вносите изменения, Oracle знает, что такое SCN, когда вы запускаете оператор или объявляете курсор. Так что, если у вас есть долгосрочный оператор, из-за которого данные изменяются из-под вас, Oracle проверяет, чтобы получить данные, КАК ОН БЫЛ ОБЯЗАН, когда ваш оператор начал выполняться. Это контроль совместимости нескольких версий, и это довольно сложно. Oracle не реализует все уровни изоляции, требуемые различными стандартами SQL - например, Oracle никогда не разрешает грязное или фантомное чтение.

Долговечность. Буфер журналов повторов, сбрасываемый на диск, является корневым уровнем долговечности. Когда файл журнала повторов заполнен, Oracle вызывает контрольную точку. Этот процесс заставляет Oracle записывать все измененные блоки таблиц и индексов из памяти на диск независимо от того, были ли они зафиксированы. Если происходит сбой экземпляра и данные в файлах данных содержат незафиксированные изменения, Oracle использует журналы повторов для отката этих изменений, так как информация об отменах также содержится в журнале повторов.

* На данный момент игнорируем автономные транзакции, поскольку они являются серьезным осложнением.

0 голосов
/ 09 февраля 2012

Ayende предложил мне в Твиттере посмотреть на Munin , механизм хранения данных, который он использует для RavenDB и Raven MQ .

...