Специалист базы данных

Принципы работы СУБД. MVCC

Многие из нас сталкивались в своей работе с СУБД. На текущий момент базы данных в том или ином виде окружают нас повсюду, начиная с мобильных телефонов и заканчивая социальными сетями, в число которых входит и любимый нами хабр. Реляционные СУБД являются наиболее распространенными представителями семейства СУБД, и большинство из них являются транзакционными.
В институте нас заставляли заучивать определение ACID и стоящие за ним свойства, но почему-то стороной обходились подробности реализации этой парадигмы. В данной статье я постараюсь частично заполнить этот пробел, рассказав о MVCC, которая используется в таких СУБД как Oracle, Postgres, MySQL, etc. и является весьма простой и наглядной.
Итак, стоит начать с определения ACID:

  • Atomicity — транзакции атомарны, то есть либо все изменения транзакции фиксируются (commit), либо все откатываются (rollback);
  • Consistency — транзакции не нарушают согласованность данных, то есть они переводят базу данных из одного корректного состояния в другое. Тут можно упомянуть допустимые значения полей, внешние ключи и более сложные ограничения целостности;
  • Isolation — работающие одновременно транзакции не влияют друг на друга, то есть многопоточная обработка транзакций производится таким образом, чтобы результат их параллельного исполнения соответствовал результату их последовательного исполнения;
  • Durability — если транзакция была успешно завершена, никакое внешнее событие не должно привести к потере совершенных ей изменений.

Каждое из этих требований выгладит более чем рациональным, особенно если оно затрагивает такие важные сферы, как банковские операции и другие операции с валютой: согласитесь, будет очень неприятно, если с вашего счета деньги спишутся, а на счет магазина они не придут (нарушение «atomicity»), или в результате сбоя АБС будет потеряна информация о том, что вам на счет зачислили зарплату (нарушение «durability»).
Если начать рассуждать о том, как же работают СУБД, поддерживающие ACID транзакции, больше всего вопросов вызовет свойство Isolation: современные СУБД поддерживают сотни и тысячи одновременных транзакций, и все они обращаются к одним и тем же таблицам. Как же сделать так, чтобы они друг другу не мешали? Здесь на помощь приходит MVCC (MultiVersion Concurrency Control), то есть контроль конкурентного доступа к данным через создание множества “версий” изменяемых данных. В упрощенном виде этот механизм можно представить следующим образом: все операции с данными можно условно разделить на чтение (select), вставку (insert), удаление (delete), обновление (update). Вот что происходит при этих операциях:

  • Select — считываются валидные записи таблицы. Запись считается валидной, если она создана транзакцией, которая была зафиксирована (commit) до начала текущей транзакции;
  • Insert — новая запись просто добавляется в свободное место таблицы;
  • Delete — запись в таблице помечается как не валидная, при этом сама запись не удаляется;
  • Update — комбинация delete и insert. Сначала старая версия записи помечается как не валидная, затем добавляется новая запись с обновленными данными.

В целом, указанный подход можно реализовать при помощи всего одного дополнительного бита с флагом is_valid = 1 для валидных записей и 0 для не валидных. Но есть проблема с многопоточностью: при таком подходе будет возможен только последовательный доступ к данным (писатели будут блокировать как читателей, так и других писателей).
Допустим, у нас есть “Таблица А” со следующими данными:

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

  • Транзакция 1 прочитала валидное значение Value для строки с ID=1: “Hello”;
  • Транзакция 2 прочитала валидные значение Value для строки с ID=1: “Hello”;
  • Транзакция 1 посчитала новое значение для поля Value: “Hello transaction 1”;
  • Транзакция 2 посчитала новое значение для поля Value: “Hello transaction 2”;
  • Транзакция 1 обновила данные таблицы;
  • Транзакция 2 обновила данные таблицы.

Вот результат такого сценария:

А вот результат их последовательного выполнения:

Очевидно, что требование изоляции нарушено: работающие одновременно транзакции повлияли друг на друга. Подобные ошибки можно условно разделить на Dirty Reads, Non-repeatable Reads и Phantom Reads.
Таким образом можно заключить, что:

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

Рассмотрим как это работает в Postgres:

  1. Все транзакции в системе имеют последовательные номера (условно, по факту номер транзакции для каждой из таблиц свой и в рамках vacuum он сбрасывается во избежании переполнения 4-байтового целого, которое используется для хранения ID транзакции);
  2. Существует глобальный реестр транзакций, содержащий информацию о том, какие транзакции находятся в процессе выполнения, а какие были подвергнуты откату;
  3. Для каждой записи таблицы существуют технические поля Xmin и Xmax, хранящие информацию о транзакциях, модифицировавших эту запись. Xmin — идентификатор транзакции, которая добавила запись в таблицу. Xmax — идентификатор транзакции, удалившей запись из таблицы;
  4. Для каждой записи таблицы существуют технические поля Cmin и Cmax, использующиеся для обеспечения работы транзакций в несколько команд.

Допустим, транзакция сначала добавляет в таблицу строку, затем её обновляет, затем удаляет. Благодаря Xmin и Xmax до момента фиксации транзакции (commit) все эти операции будут невидимы для внешних транзакций. Но как быть с самой транзакцией, которая свои изменения должна видеть, даже если они противоречат друг другу (сначала запись добавили, затем удалили)? Для этого созданы Сmin и Сmax, работающие во многом аналогично Xmin и Xmax, но в рамках одной конкретной транзакции.
С таким набором метаданных мы намного ближе к реализации ACID, чем с одним флагом валидности. Теперь рассмотрим описанные выше операции в контексте MVCC:

  • Select — считываются валидные записи таблицы. Для каждой из записей мы имеем Xmin, Xmax, Cmin, Cmax. Если Xmin больше ID текущей транзакции или находится в списке работающих или отмененных транзакций, запись не валидна. Если Xmax задан и меньше ID текущей транзакции и транзакция с этим ID не находится в списке работающих или отмененных транзакций, запись не валидна. Если Xmin равен ID текущей транзакции, то для проверки валидности записи смотрим на Cmin и Cmax (Cmax должно быть задано, чтобы запись была не валидна). Если Xmax равен ID текущей транзакции, всё с точностью до наоборот. Если указанные выше проверки не сработали, запись валидна. На эту тему есть весьма хороший кусок кода из src/backend/utils/time/tqual.c с комментарием автора:
  • Insert — новая запись просто добавляется в свободное место таблицы, ей проставляется Xmin в ID текущей транзакции и Cmin в ID текущей операции в рамках транзакции;
  • Delete — запись в таблице помечается как не валидная, при этом сама запись не удаляется. Выполняется это следующим образом: Xmax ставится в ID текущей транзакции, Cmax в ID текущей операции;
  • Update — комбинация delete и insert. Сначала старая версия записи помечается как не валидная, затем добавляется новая запись с обновленными данными;
  • Commit транзакции выполняется через удаление идентификатора транзакции из списка работающих;
  • Rollback транзакции выполняется через пометку идентификатора транзакции как транзакция подверженная откату.

Данная модель работы с транзакциями является упрощенной, так как не охватывает механизм блокировок, а также случаи с распределенными транзакциями. Тем не менее этот подход является общим для многих современных СУБД и его понимание позволит лучше разобраться в том, что же происходит внутри движка СУБД.
Более подробный разбор примеров для Postgres можно посмотретьздесь

Расписание по курсам администратор баз данных

— Программа повышения квалификации — Записаться

Курсы обучения Ак.ч Ближайшая группа Преподаватели Цена от*
Очно Онлайн
Частные
лица
Органи-
зации
Частные
лица
Органи-
зации
Разработчик приложений и баз данных на Java и Oracle SQL (Дипломная программа — 5 курсов) 260 23.03.2019

Кораблин Александр Игоревич

200 940 160 690 216 940 173 490 200 940 160 690 216 940 173 490
Oracle 12c — Курс по администрированию, установке, обновлению, резервированию, восстановлению и управлению производительностью. (Программа повышения квалификации — 3 курса) Вы экономите 60% стоимости третьего курса! 120 31.03.2019

Фокин Михаил Владимирович

152 970 122 290 173 970 139 090 152 970 122 290 173 970 139 090
Администрирование Oracle12с (Программа повышения квалификации — 4 курса) Вы экономите 80% стоимости четвёртого курса! 160 31.03.2019

Фокин Михаил Владимирович

203 960 163 090 229 960 183 890 203 960 163 090 229 960 183 890

ХИТ!

Курс 10774 Создание запросов в Microsoft SQL Server 2012 40 01.04.2019

Аверин Владимир Анатольевич

32 990 34 990 32 990 34 990

ХИТ!

Курс 10778 Реализация модели данных и отчетов с помощью Microsoft SQL Server 2012 40 01.04.2019

Самородов Федор Анатольевич

32 990 35 990 32 990 35 990

ХИТ!

Курс 10775AB Администрирование баз данных в Microsoft SQL Server 2012 40 01.04.2019

Самородов Федор Анатольевич

32 990 34 990 32 990 34 990

ХИТ!

Курс 10776AB Разработка баз данных в Microsoft SQL Server 2012 40 01.04.2019

Самородов Федор Анатольевич

34 990 36 990 34 990 36 990

ХИТ!

Курс 6231 Поддержка баз данных в Microsoft SQL Server 2008 R2 40 01.04.2019

Самородов Федор Анатольевич

32 990 38 990 32 990 38 990
Курс 10777 Внедрение хранилищ данных в Microsoft SQL Server 2012 40 01.04.2019

Самородов Федор Анатольевич

32 990 35 990 32 990 35 990
Oracle 12с: Основы SQL 40 31.03.2019

Фокин Михаил Владимирович

50 990 55 990 50 990 55 990
Oracle12с: Основы PL/SQL 40 25.03.2019 52 990 55 990 52 990 55 990
Oracle Database 12c: Передовые методы PL/SQL 24 22.04.2019

Тимаков Алексей Анатольевич

28 990 31 990 28 990 31 990
Oracle 12c. Ускоренный курс по администрированию, установке и обновлению 40 31.03.2019 — 20%

Фокин Михаил Владимирович

40 750 57 990 40 750 57 990
Oracle 12c. Практикум по резервированию и восстановлению 40 31.03.2019 — 20%

Фокин Михаил Владимирович

40 750 57 990 40 750 57 990
Oracle 12c. Управление и настройка производительности 40 31.03.2019 — 20%

Фокин Михаил Владимирович

40 750 57 990 40 750 57 990
Microsoft Access 2019/2016. Уровень 1. Обработка и анализ информации в базах данных 20 01.04.2019

Завьялов Андрей Николаевич

10 990 12 990 10 990 12 990
Курс 55073A: Управление нормативными данными при помощи Master Data Services и Data Quality Services 2012 — 2014 24 31.03.2019

Самородов Федор Анатольевич

21 990 24 990 21 990 24 990
Oracle 12c — Новые возможности для администраторов баз данных (Программа повышения квалификации — 2 курса) Вы экономите 53% стоимости второго курса! 64 Звоните по тел. +7 (495) 232-32-16 84 980 67 890 96 980 77 490 84 980 67 890 96 980 77 490

ХИТ!

Курс 6232В Реализация баз данных в Microsoft SQL Server 2008 R2 40 Звоните по тел. +7 (495) 232-32-16 34 990 38 990 34 990 38 990

NEW!

Практикум. Проектирование и разработка базы данных на примере логистики 16 Звоните по тел. +7 (495) 232-32-16 10 990 12 990
Oracle 12с: Настройка команд SQL 24 Звоните по тел. +7 (495) 232-32-16 35 990 40 990 35 990 40 990
Oracle Data Integrator 12c (ODI 12c) — Интеграция и Администрирование 40 Звоните по тел. +7 (495) 232-32-16 50 990 53 990
Oracle 12c. Новые возможности для администраторов баз данных. 40 Звоните по тел. +7 (495) 232-32-16 52 990 60 990 52 990 60 990
Oracle 12с. Новые возможности высокой доступности. 24 Звоните по тел. +7 (495) 232-32-16 31 990 35 990 31 990 35 990
IBM DB2. Уровень 1. Основы IBM DB2 24 Звоните по тел. +7 (495) 232-32-16 21 990 23 990 21 990 23 990
IBM DB2. Уровень 2. Язык SQL для DB2 40 Звоните по тел. +7 (495) 232-32-16 37 990 41 990 37 990 41 990
IBM DB2. Уровень 3. Администрирование DB2 v10/v9 для Windows, UNIX и Linux 40 Звоните по тел. +7 (495) 232-32-16 38 990 42 990 38 990 42 990

*»Цена от» — минимальная возможная цена на данный курс, зависит от формата, вида обучения, выбранного времени занятий и указана с учетом действующих акций и специальных предложений. Точную цену на конкретную группу Вы можете узнать по тел. +7 (495) 232-3216.

Для юридических лиц (организаций) указана минимальная цена, действующая при полной предоплате.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *