Введение в MySQL MVCC

база данных

Введение

MVCC (управление многоверсионным параллелизмом), многоверсионный контроль параллелизма, в основном за счет добавления трех полей в каждую строку записей, используемых вместе с соответствующими записями в журнале отмены, и добавления алгоритмов видимости, чтобы каждая транзакция могла быть заблокирована без блокировки Одновременно может быть прочитано точное значение в записи строки (это значение может быть разным для разных транзакций). С помощью MVCC можно считывать точные данные без блокировки, что значительно повышает эффективность параллелизма.

дела

Когда упоминается MVCC, должны быть упомянуты транзакции. Что касается транзакций, есть четыре характеристики, которые мы часто называем ACID.

  • Атомарность: указывает, что транзакции либо выполняются все, либо не выполняются вообще, что является неделимой наименьшей единицей.
  • Непротиворечивость: указывает, что транзакции всегда переходят из одного согласованного состояния в другое согласованное состояние.
  • Изоляция: указывает, что каждая транзакция изолирована друг от друга и не влияет друг на друга.
  • Долговечность: как только транзакция зафиксирована, ее изменения в базе данных остаются постоянными, даже если последующая база данных выходит из строя.

Изоляция транзакций делится на четыре уровня: чтение незафиксированного (read uncommitted), чтение зафиксированного (read commit), повторяемое чтение (repeatable read), сериализуемое (serializable).

  • Чтение незафиксированного: относится к транзакции, которая не была зафиксирована, и ее собственные модификации могут быть видны другим транзакциям. В этом случае возникнут проблемы грязных чтений, фантомных чтений и неповторяющихся чтений.
  • Чтение фиксации: после фиксации транзакции сделанные ею изменения могут быть видны другим транзакциям. В этом случае проблема грязного чтения «чтение незафиксированного» решена, но проблемы фантомных чтений и неповторяющихся чтений все еще возникают.
  • Повторяемое чтение: в одной и той же транзакции чтение данных является согласованным. На этом уровне изоляции проблемы грязных чтений и неповторяющихся чтений могут быть решены, но проблема фантомных чтений все еще существует.
  • Сериализация: относится к нескольким транзакциям, если блокировка чтения-записи конфликтует, транзакция, к которой был получен доступ позже, должна дождаться завершения выполнения предыдущей транзакции, прежде чем продолжить выполнение. Этот уровень изоляции является самым высоким, а также решает проблемы грязных чтений, фантомных чтений и неповторяющихся чтений. Но это также сильно ограничивает степень параллелизма.

Что касается различий между этими четырьмя уровнями изоляции, можно использовать следующие примеры (примеры взяты из:Линь Сяобинь: Практика MySQL, 45 лекций) объяснить.

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

время Транзакция А
Транзакция Б
t1 Начать транзакцию, запрос получает значение 1
t2
начать транзакцию
t3
Запрос получает значение 1
t4
изменить 1 на 2
t5 Запрос получает значение V1

t6
совершить транзакцию
t7 Запрос получает значение V2

t8 совершить транзакцию

t9 Запрос получает значение V3

Для разных уровней изоляции V1, V2 и V3 считывают разные значения.

При уровне изоляции «чтение незафиксированного», поскольку транзакция B изменила значение на 2 в момент t4, хотя B не зафиксировала транзакцию, изменение в это время видно другим транзакциям, поэтому V1, V2, V3 запрашивают значение составляет 2.

На уровне изоляции «чтение фиксации» значение изменяется в момент t4, но в момент t5 транзакция B еще не зафиксирована, а значение, прочитанное транзакцией A, все еще является старым значением, поэтому V1 равно 1, а в момент t7 поскольку транзакция B была отправлена ​​​​в момент t6, изменения, сделанные транзакцией B, видны другим транзакциям, поэтому транзакция A может видеть изменение транзакции B в момент t7, в это время значение V2 равно 2, конечно, V3 также равно 2 .

На уровне изоляции «повторяемое чтение» выполняется требование, согласно которому «данные, видимые транзакцией во время выполнения, должны быть непротиворечивыми», поэтому независимо от того, изменяет ли транзакция B значение или фиксирует транзакция B или нет, транзакция A не делает этого. перед фиксацией.Значения чтения одинаковы, то есть значения V1 и V2 равны 1. Когда транзакция A фиксируется и снова запрашивается, модификация транзакции B может быть видна A, поэтому значение V3 равно 2.

При уровне изоляции «сериализации», когда транзакция B выполняет обновление в момент времени t4, поскольку она работает с той же строкой, что и транзакция A, и возникает конфликт чтения-записи, транзакция B будет заблокирована в это время, ожидая завершить транзакцию A. После этого выполнить транзакцию B, поэтому значение V1 и V2 равно 1, а значение V3 равно 2.

MVCC

операция обновления

В записях таблицы базы данных каждая запись добавит три поля:

  • DBTRXID: 6 байт, указывающий ID транзакции, которая в последний раз изменила эту запись.
  • DBROLLPTR: 7 байт, указатель отката, указывающий на запись журнала отмены в сегменте отката, используемый для нахождения данных последней модифицированной версии этой записи.
  • DBROWID: 6 байт, монотонно возрастающий ID, определяющий уникальность записей в таблице.

Когда запись обновляется, текущая запись будет записана в журнал отмены, а БД в текущей записи будет обновлена.ROLLЗначение поля PTR, сделайте так, чтобы оно указывало на запись журнала отмены только что, затем обновите значение поля, связанного с текущей записью, и одновременно обновите БД.TRXПоле идентификатора, которое записывает идентификатор транзакции, выполнившей операцию обновления. Краткий процесс обновления выглядит примерно следующим образом

mysql-mvcc-update

операция запроса

Из приведенной выше операции обновления видно, что в записи таблицы базы данных всегда записываются последние результаты обновления.Для транзакций с уровнем изоляции «повторяемое чтение» и «чтение-фиксация», как это гарантирует, что после открытия этой транзакции другие транзакции Запись обновлена, и транзакция все еще может считать точное значение (не последнее значение записи таблицы, а значение исторической версии)? Как видно из операции обновления, проходя по БДROLLPTR может получить историческую версию текущей записи (конечно, это только активная транзакция. Если в текущей записи нет связанной транзакции, журнал отмен будет очищен, и данные исторической версии не могут быть получены). Но с таким количеством исторических версий данных, какая версия данных необходима для текущей транзакции? В это время необходимо определить, видна ли текущая версия данных для текущей транзакции.

Когда транзакция открыта, идентификатор текущей активной транзакции (транзакция была открыта, но еще не зафиксирована) будет помещен в массив, а наименьший идентификатор транзакции в массиве будет записан как «низкий уровень воды». , и текущая система была создана. Максимальное значение идентификатора транзакции плюс один — это "максимальная отметка". Эти три составляют непротиворечивое представление транзакции (чтение-представление). Когда транзакция хочет запросить данные определенной записи, она фактически сравнивает идентификатор транзакции записи (включая идентификатор транзакции исторической версии) с этим представлением согласованности до тех пор, пока данные определенной версии не станут видимыми. Процесс запроса выглядит следующим образом:

  • Идентификатор транзакции прочитанной записи меньше минимального уровня воды, что указывает на то, что данные этой версии были отправлены до открытия транзакции, которая видна, и данные возвращаются напрямую.
  • Идентификатор транзакции прочитанной записи больше максимального уровня воды, что указывает на то, что данные этой версии были отправлены после открытия транзакции, и ее не видно Вынуть БД из записиROLLЗапись, на которую указывает PTR, и считывание ее идентификатора транзакции, чтобы начать следующий этап оценки.
  • Идентификатор транзакции прочитанной записи находится между уровнем низкой воды и уровнем высокой воды.В это время определите, находится ли идентификатор транзакции в массиве транзакций представления согласованности:
    • Если его нет, значит, данные этой версии были отправлены до открытия сделки, она видна, и данные возвращаются напрямую
    • Если да, то это означает, что данные этой версии передаются другими активными транзакциями после открытия транзакции, что невидимо для этой транзакции, поэтому БД нужно извлекать из записиROLLЗапись, на которую указывает PTR, и считывание ее идентификатора транзакции, чтобы начать следующий этап оценки.

Блок-схема его процесса суждения примерно выглядит следующим образом.file

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

  • Для данных в текущей транзакции видны
  • Для данных в других транзакциях
    • Не отображается, если версия не зафиксирована
    • Не отображается, если версия была зафиксирована и была зафиксирована после создания представления транзакции.
    • Если версия была зафиксирована, и она была зафиксирована до создания представления транзакции, отображается

пример

Теперь с примером (этот пример взят из:Линь Сяобинь: Практика MySQL, 45 лекций) для описания описанного выше процесса поиска. Предположим, что на уровне изоляции «повторяемое чтение» имеется следующая структура таблицы и данные.

mysql> CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `k` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1),(2,2);

Предположим, выполняются следующие операции (транзакция будет автоматически зафиксирована после завершения операции обновления транзакции C).TRXЗначение идентификатора равно 90. Тогда массив представлений транзакции A равен [99, 100], массив представлений транзакции B равен [99, 100, 101], а массив представлений транзакции C равен [99, 100, 101, 102].

Транзакция А (идентификатор транзакции: 100)
Транзакция Б (идентификатор транзакции: 101)
Транзакция C (идентификатор транзакции: 102)
start transaction with consistent snapshot;


start transaction with consistent snapshot;


update t set k = k + 1 where id = 1;

update t set k = k + 1 where id = 1;


select k from t where id = 1;

select k from t where id = 1;


commit;



commit;

Когда транзакция A выполняет оператор запроса, ее логическая диаграмма данных запроса (эта диаграмма взята из:Линь Сяобинь: Практика MySQL, 45 лекций)Следующим образом

mysql-mvcc-query

Процесс поиска выглядит следующим образом: сначала получается идентификатор транзакции (101) записи, который больше верхней отметки и невидим, поэтому извлекается предыдущая историческая версия записи и ее идентификатор транзакции ( 102), которая больше верхней отметки и невидима.Получите предыдущую историческую версию записи, получите ее идентификатор транзакции (90), который меньше нижней отметки и виден, поэтому возвращает значение 1 для поле k в этой записи.

Конечно, для суждения можно использовать и упрощенную версию. Процесс следующий, сначала получаем запись (1, 3), которая еще не была зафиксирована, невидимая, вынимаем предыдущую историческую версию (1, 2), (1, 2) была зафиксирована, но была зафиксирована после создания представления транзакций не видно, что последняя историческая версия (1, 1) продолжает вывозиться.(1, 1) была отправлена, и она была отправлена ​​до создания представления транзакций, который виден, поэтому окончательное значение k равно 1.

Особого внимания здесь требует то, что операция обновления транзакции B обновляется последним значением текущей записи, а не историческими данными, иначе операция обновления транзакции B будет потеряна. Фактически данные обновления сначала считываются, а затем записываются, и это чтение является текущим значением чтения, называемым «текущим чтением».

Если он находится на уровне изоляции «чтение-фиксация», логика обработки аналогична, но ситуация с созданием согласованного представления отличается:

  • При «повторяемом» уровне изоляции вам нужно только создать согласованное представление в начале транзакции, а затем другие запросы в транзакции используют это согласованное представление.
  • На уровне изоляции «чтение-фиксация» новое представление пересчитывается перед выполнением каждого оператора.

Таким образом, в приведенном выше примере, если он находится на уровне изоляции «чтение-фиксация», транзакция A создаст новое представление согласованности при выполнении оператора запроса.В это время активный массив идентификаторов транзакций в представлении согласованности равен [99, 100, 101] , процесс поиска выглядит следующим образом: прочитайте текущий идентификатор транзакции записи (101), который невидим в массиве представлений, извлеките предыдущую запись исторической версии, прочитайте идентификатор транзакции (102), между низкой водой level и высокий уровень воды, а также Not in the view array, visible, поэтому возвращает значение k записи, равное 2.

разное

  • Из четырех уровней изоляции только два уровня изоляции «чтение фиксации» и «повторяемость» могут использовать MVCC, поэтому только эти два уровня изоляции создадут согласованное представление (чтение-представление). Поскольку уровень изоляции «чтение-фиксация» — это последняя запись, считываемая каждый раз, нет необходимости в MVCC и нет необходимости создавать согласованное представление; уровень изоляции «сериализация» использует блокировку для достижения параллелизма, и нет необходимости MVCC, поэтому нет необходимости создавать представление согласованности. Разница между представлениями согласованности на двух уровнях изоляции «повторяемость» и «чтение-фиксация» в основном отражается в следующем: представление согласованности на уровне изоляции «повторяемость» создается при запуске транзакции. согласованное представление на уровне изоляции «Readable Commit» создается при выполнении SQL, каждый SQL создает отдельное представление и не будет использоваться совместно.
  • Текущее чтение, каждое чтение — это последние записанные данные, в основном включая следующие операторы SQL.
    • select ... lock in share mode
    • select ... for update
    • insert
    • update
    • delete
  • Чтение моментального снимка (чтение моментального снимка), которое может считывать исторические данные версии записей, в основном используется для простого выбора в MVCC (за исключением выбора ... блокировки в режиме общего доступа, выбора ... для обновления), чтобы обеспечить согласованность чтения транзакций.

использованная литература

[1] Линь Сяобинь Изоляция транзакций: почему я не вижу ее после того, как вы ее изменили? [J/OL]. https://time.geekbang.org/column/article/68963, 19 ноября 2018 г.

[2] Линь Сяобинь Изоляция транзакций: изолированы транзакции или нет? [J/OL]. https://time.geekbang.org/column/article/70562, 30 ноября 2018 г.

[3] Официальная документация MySQL: https://dev.mysql.com/doc/refman/8.0/en/innodb-multi-versioning.html