Что вы должны знать о MVCC в MySQL

MySQL
Что вы должны знать о MVCC в MySQL

предисловие

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

Такого рода бездушная операция может быть уже привычной для многих новичков. Но для некоторых людей, уже имеющих опыт разработки, этого далеко не достаточно. Вы должны изучить много знаний, связанных с базами данных, и эта статья предназначена для тщательного анализа того, как реализована MVCC в MySQL. Прочитав эту статью, вы сможете узнать, какова роль MVCC при различных уровнях изоляции? Когда будет использоваться MVCC? как пользоваться?

Пример таблицы

CREATE TABLE `test`.`Untitled`  (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `phone` char(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  `age` int(3) NOT NULL,
  `country` varchar(255) NOT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `uk_phone`(`phone`) USING BTREE,
  INDEX `idx_name`(`name`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
insert into person values (null, '1351111111', 'any', 20, '蜀');
insert into person values (null, '1351111112', 'bat', 21, '吴');
id phone name age country
1 1351111111 any 20 Шу
2 1351111112 bat 21 Ву

MVCC

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

Каждая запись в базе данных фактически имеет три скрытых столбца:

  • DB_TRX_ID: этот столбец представляет идентификатор транзакции для этой записи.
  • DB_ROLL_PTR: этот столбец представляет собой указатель на сегмент отката, который на самом деле представляет собой цепочку версий, указывающую на запись.
  • DB_ROW_ID: идентификатор записи, если указан первичный ключ, то значение является первичным ключом. Если первичный ключ отсутствует, используется первый определенный уникальный индекс. Если уникального индекса нет, то значение генерируется по умолчанию.
start transaction;
update person set age = 22 where id = 1;
update person set name = 'out' where id = 1;
commit;

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

image-20200616210548918

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

В транзакции идентификатор транзакции назначается только при выполнении операций вставки, обновления и удаления. Если транзакция является транзакцией чистого чтения, то ее идентификатор транзакции по умолчанию равен 0.

Структура режима чтения выглядит следующим образом:

  • rw_trx_ids: указывает массив текущих активных транзакций чтения и записи при создании представления чтения.

  • min_trx_id: Указывает, что при создании представления для чтения текущий отправленный номер транзакции + 1, т. е. вrw_trx_idsНаименьшее количество транзакций в .

  • max_trx_id: Указывает текущий номер транзакции + 1 при создании представления чтения, то есть номер транзакции, который будет назначен следующей транзакции.

  • curr_trx_id: идентификатор текущей транзакции, которая создала представление для чтения.

image-20200616223956391
MySQL определяет, какая версия (запись) в цепочке версий видна в транзакции в соответствии со следующими правилами:

  • trx_id < min_trx_id, то запись находится в текущей транзакциивидимый, так как транзакция, изменившая запись версии, была зафиксирована до того, как текущая транзакция создала представление для чтения.
  • trx_id in (rw_trx_ids), то запись находится в текущей транзакцииНевидимый, потому что транзакция, которая должна изменить запись версии, не была зафиксирована до того, как текущая транзакция сгенерирует представление для чтения.
  • trx_id > max_trx_id, то запись находится в текущей транзакцииНевидимый, так как транзакция, изменяющая запись версии, не открывается до того, как текущая транзакция создаст представление для чтения.
  • trx_id = curr_trx_id, то запись находится в текущей транзакциивидимый, потому что транзакция, изменяющая запись версии, является текущей транзакцией.

Например:

image-20200617204951244

Сначала мы открыли транзакцию чтения на шаге 1. Поскольку это транзакция только для чтения, ее идентификатор транзакции равен 0 (далее именуемый транзакцией 0). Затем мы запрашиваем запись с идентификатором 1 в транзакции 0. На данный момент цепочка версий выглядит следующим образом:

image-20200617204030286

Примечание. Записи, связанные с красным заголовком, находятся в дереве B+, а все записи, связанные указателем roll_ptr, находятся в журнале отмены. Все цепочки версий ниже имеют эту форму.

READ UNCOMMITTED

Этот уровень изоляции не использует MVCC. он просто выполняетselect, то он получит последнюю запись в дереве B+. Независимо от того, была ли зафиксирована записанная транзакция.

READ COMMITTED

На уровне изоляции READ COMMITTED используется MVCC. После запуска транзакции чтения будетselectПеред операцией создается представление для чтения.

потому что на шаге 2selectПри чтении активных транзакций нет, значит, все транзакции были зафиксированы. Таким образом, он может прочитать первую запись.

Выполните шаг 3, чтобы открыть новую транзакцию с идентификатором транзакции 101 (следующее обнаруживает транзакцию 101).

Выполните шаг 4, который изменяет запись с идентификатором 1, и цепочка версий станет следующей:

image-20200617205904335

Шаг 5 выполняется, транзакция 0 выполняетselectоперация, транзакция 0 создаст представление для чтения. Значения Read View следующие:

image-20200617210657965

Мы следуем правилам видимости записи в цепочке версий выше:

  1. Первая запись в цепочке версий, ееtrx_idне меньше чемmin_trx_id, поэтому запись не видна.
  2. Вторая запись в цепочке версий, ееtrx_idменьше, чемmin_trx_id, поэтому запись видна.

Таким образом, для этого запроса записи, которые он может получить, следующие:

image-20200617211137066

Транзакция 101 выполняет операцию обновления на шаге 5, выполняет шаг 6 и фиксирует транзакцию, цепочка версий выглядит следующим образом:

image-20200617212138940

Выполняем шаг 7, мы делаем это один раз в транзакции 0selectQuery, так как наш уровень изоляции READ COMMITTED, этот запрос также создаст представление для чтения. Значения этого Read View следующие:

image-20200617214141045

Затем в соответствии с правилами видимости цепочки версий:

  • Поскольку активных транзакций нет, видно, что все транзакции были зафиксированы, поэтомуrw_trx_idsПусто.
  • Первая запись цепочки версий, ееtrx_idменьше, чемmin_trx_id, поэтому эта запись видна.

Тогда записи, которые можно получить по этому запросу, следующие:

image-20200617214546910

После того, как транзакция 0 завершает выполнение запроса, мы открываем новую транзакцию с идентификатором транзакции 102 (далее — транзакция 102), которая также обновляет запись с идентификатором 1.

Запрос на шаге 9 анализирует сам себя. Мы напрямую даем окончательную цепочку версий двух операторов обновления, выполняемых транзакцией 102:

Выполните шаг 11, в соответствии с правилами видимости цепочки версий записи, которые он может получить:

image-20200617215736644

REPEATABLE READ

На самом деле разница между REPEATABLE READ и READ COMMITTED заключается только во времени генерации Read View.

READ COMMITTED выполняется каждый разselectПри работе будет создан новый вид чтения. в то время как REPEATABLE READ будет выполняться только в первый разselectПредставление для чтения создается во время операции, пока транзакция не будет зафиксирована, всеselectОперация заключается в использовании представления чтения, созданного в первый раз.

Повторим действия в таблице.

Сначала переходим к шагу 2, после транзакции 0 запускаем транзакцию и выполняем ее один разselectЗапрос. Создается представление для чтения. Представление чтения устроено следующим образом:

Snipaste_2020-06-18_13-17-05

Результирующее представление для чтения будет использоваться до тех пор, пока транзакция 0 не будет зафиксирована.

Таким образом, несмотря на то, что последние две транзакции открываются, а записи изменяются, итоговая цепочка версий становится следующей:

Но транзакция 0 по-прежнему может читать только первую запись, а именно:

Snipaste_2020-06-18_13-22-51

Независимо от того, когда выполняется транзакция 0select * from person where id = 1;Прочитать записи, тогда он будет использовать только представление чтения, созданное в первый раз, для выбора записей, которые можно прочитать в цепочке версий.

SERIALIZABLE

Этот уровень изоляции не использует MVCC. Если использовать обычныйselectоператор, он добавляет операторlock in share mode, что становится согласованным чтением блокировки. Предположим, транзакция читает запись, а другие транзакции блокируются при изменении этой записи. Если предположить, что транзакция изменяет запись, прочтение записи другими транзакциями будет заблокировано.

На этом уровне изоляции операции чтения и записи становятся последовательными операциями.

Суммировать

Из приведенной выше статьи мы можем узнать, что MVCC используется только на уровнях изоляции READ COMMITTED и REPEATABLE READ.

Но READ COMMITTED и REPEATABLE READ используют MVCC по-разному:

  • READ COMMITTED выполняется каждый разselectКаждая операция будет генерировать представление для чтения.
  • REPEATABLE READ выполняется только в первый разselectRead View будет генерироваться только во время работы, а последующиеselectОперации будут использовать первое сгенерированное представление чтения.

Уровни изоляции READ UNCOMMITTED и SERIALIZABLE не используют MVCC.

Их операции чтения также отличаются:

  • READ UNCOMMITTED при каждом выполненииselectБуду читать последние записи.
  • SERIALIZABLE выполняется каждый разselectоперации будут сопровождаться операторомlock in share mode,сделатьselectСтановится согласованным блокирующим чтением, сериализующим чтением и записью.