Начало работы с MySQL (3): изоляция транзакций

MySQL

Резюме

В этой статье я начну с того, что такое бизнес, и расскажу о необходимости бизнеса.

Затем введите четыре различных уровня изоляции транзакций в InnoDB, какие проблемы можно решить и какие проблемы можно вызвать.

Наконец, я расскажу о том, как InnoDB решает транзакции с высокой степенью параллелизма: управление параллельным выполнением нескольких версий.

1 Что такое сделка

Когда дело доходит до транзакций, типичным примером является банковский перевод: предположим, что у A и B есть баланс в 100 юаней, и A хочет перевести 50 юаней B. Итак, наш рабочий процесс выглядит следующим образом:

  • Запросите баланс A и сохраните его вbalanceв, и судитьbalanceЭто больше 50 юаней?
  • Если да, тоbalanceВычтите 50 юаней, запишите обратно в базу данных, затем добавьте 50 юаней на баланс B, запишите обратно в базу данных.
  • Если нет, верните недостаточный баланс

Затем возникает проблема.После первого запроса, если мы немедленно делаем еще один перевод, а баланс А по-прежнему остается исходным 100 юаней, что больше 50, система решает, что баланс достаточен, и перевод выполнен успешно. Но при обратной записи в базу данных баланс А по-прежнему составляет 50 юаней, а баланс Б становится 200 юаней.

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

Просто потому, что мы хотим, чтобы наша бизнес-логика работала бесперебойно, у нас есть "дела".

Итак, какие условия необходимы для сделки?

Я думаю, вы слышали более или менееACIDэто утверждение.

1.атомарность: В обычной семантике атомарность означает неделимость оператора. Но в транзакции это означает, что все операторы, составляющие транзакцию, должны быть выполнены или отброшены.

2.Последовательность: согласованность здесь несколько отличается от согласованности данных, о которой мы говорили. Когда мы говорим о согласованности данных, мы обычно имеем в виду, что данные в MySQL и Redis непротиворечивы или данные в основной базе данных MySQL и подчиненной базе данных непротиворечивы. Но здесь это обычно относится к тому, вызвала ли транзакция неожиданное промежуточное состояние или результат. Например, в приведенном выше примере банковского перевода общий баланс двух человек перед переводом составляет 200 юаней, а после завершения перевода он становится 250 юаней. Это непоследовательно.

3.Изоляция: Как следует из названия, изоляция означает, что транзакции не должны влиять друг на друга. В MySQL изоляция транзакций разделена на четыре уровня, которые мы подробно опишем позже.

4.Продолжительность: Это легко понять, если транзакция совершена, данные должны быть сохранены и не потеряны.

2 уровень изоляции транзакций

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

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

Возьмем пример. Предположим, у нас есть таблица только с двумя полями, а затем вставляем следующие данные:

CREATE TABLE `t`(
    id int,
    v int,
    PRIMARY KEY (`id`)
)ENGINE=InnoDB;

insert into t(id, v) values(0, 0) 

Уведомление, все следующее содержимое основано только на одной строке данных(0, 0)из表t.

2.1 Чтение незафиксированного

В этот момент уровень изоляции транзакциичитать незафиксированные:

Как видно на рисунке: в Т3 данные, найденные транзакцией А, равны (0, 1), но затем транзакция В откатывается, в результате чего строка данных (0, 1) становитсяошибка, это называетсягрязное чтение.

Корень проблемы в том, что транзакция A считывает незафиксированные данные транзакции B, которая также является уровнем изоляции транзакции.читать незафиксированныесуществующие проблемы. Такой уровень изоляции транзакций может только гарантировать атомарность транзакции, но не гарантирует ее изоляцию, это самый низкий уровень изоляции транзакций.

2.2 Чтение подтверждено

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

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

Другими словами, в момент времени T5 данные, найденные транзакцией A,(0, 1). Но здесь возникает проблема: данные, найденные транзакцией A в T2,(0,0). Такой поиск одной и той же строки данных в одной и той же транзакции, но с разными результатами, называется ""неповторяемое чтение".

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

2.3 Повторяемое чтение

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

Такой уровень изоляции транзакций, называемый "повторяемое чтение".

Обратите внимание, что "Скопируйте все данные в снимок«Заявление являетсяНеточныйДа, потому что это удвоит объем памяти, необходимый для каждой запущенной транзакции, что, очевидно, невозможно. Но вы можете сначала понять это, и я объясню вам, как MySQL реализует функцию «моментального снимка» в следующем содержании.

Затем в "повторяемое чтение«На этом уровне изоляции какие проблемы могут возникнуть?

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

id v
0 0

Стоит отметить, что мы также вставили строку в транзакцию B в момент времени T3.vдля0данные, а поскольку мы используемповторяемое чтениеС этим уровнем изоляции можно сделать вывод, что поиск в момент времени T5 не найдет вновь вставленную строку данных.

То есть в момент времени T5 результат запроса остается таким же, как и в момент времени T2:

id v
0 0

Но тут возникает проблема. Поскольку транзакция A не знает о существовании транзакции B в это время, когда транзакция A узнает, что ее не существуетidдля1,vдля0После того, как транзакция A подготовится к вставке этой строки данных, MySQL вернет эту ошибку:

ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'

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

Этогаллюцинации.

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

2.4 Сериализация

сериализовать, как следует из названия, транзакция должна выполняться последовательно.

Чтобы быть более конкретным: все транзакции, включающие операции записи, должны выполняться последовательно.

В отличие от других уровней изоляции, при сериализации операции чтения разделяются с блокировками и следуют строгому двухэтапному протоколу блокировки.

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

Что касается содержания замков, то яСледующийупомянул.

3 Многоверсионный контроль параллелизма

существует2.3 Повторяемое чтениеВ содержании я упомянул термин «моментальный снимок».

Но это недостаточно точно, потому что MySQL действительно не может сделать резервную копию всех данных в момент начала транзакции.

Здесь я собираюсь представить InnoDB Multi-Version Concurrency Control (сокращенно MVCC).

Сначала определите два понятия:

Во-первых, при запуске каждой транзакции присваивается идентификатор, который присваивается InnoDB и увеличивается.

Во-вторых, InnoDB добавляет три поля в каждую строку базы данных.DB_TRX_IDУказывает идентификатор транзакции, вставившей или обновившей эту строку;DB_ROLL_PTRявляется указателем наundo logданные старой версии в ;DB_ROW_IDявляется увеличивающимся идентификатором строки.

Давайте сначала посмотрим на эту картинку:

Или таблица t упомянутая выше, у него два поля,idа такжеv. Затем автоматически добавляется InnoDBполе указателяа такжеполе идентификатора транзакции, поле идентификатора строки опущено.

Строка данных за пределами верхней пунктирной рамки представляет последниеidдля0данные, на данный моментvдля4, эта строка данных была изменена транзакцией с идентификатором 50.

Глядя вниз, в этих последних данных указатель указывает наidдля0,vдля3, и эта строка данных была изменена транзакцией с идентификатором 44.

К этому моменту вы, возможно, поняли, что каждый раз, когда InnoDB обновляет данные, он будет записывать идентификатор транзакции, в которой обновляется строка данных.事务idполе, а затем заполните адрес памяти исходных данных指针поле. Другими словами, InnoDB может найти запись истории изменений этой строки данных и идентификатор транзакции, которая сгенерировала эту запись, на основе адреса указателя здесь.

Так какое это имеет отношение к тому, что мы называем «моментальными снимками»?

Предполагая, что уровень изоляции транзакций «повторяемое чтение» сейчас, тогда, когда транзакция начнется, InnoDB сгенерирует массив внутри, который записывает все текущие активные (то есть все еще выполняющиеся незафиксированные) идентификаторы транзакций и выполняет сортировку.

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

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

Давайте посмотрим на пример:

Предположениеперед этим, в таблице t уже есть такая строка данных, id=0, v=1, которая вставлена ​​транзакцией с id 100.

Затем предположим, что идентификатор транзакции A равен 101, транзакции B — 102, а транзакции C — 103.

В момент времени T4 транзакция C обновляет эту строку данных, и историческая версия данных выглядит следующим образом:

Затем в момент времени T6 транзакция B готова обновить эту строку данных.УведомлениеПри обновлении необходимо обновлять независимо от исторической версии данных.НовейшиеЭта строка данных. Это называется "текущее чтение", что означает, что операции обновления, вставки и удаления InnoDB не связаны со снимками, и последние данные должны быть обновлены. Содержание этой части будет представлено в следующем разделе.

Итак, получается так:

Затем к моменту времени T7, готов к чтению данных.

Когда транзакция B запущена, транзакция C еще не запущена, поэтому массив равен [101, 102], а версия прочитанных данных — 102, что является обновлением транзакции B, поэтому эта строка данных соответствует требованиям, возврат.

В момент времени T8 транзакция A готова к чтению данных. Поскольку при запуске транзакции A массив равен [101], а текущий идентификатор транзакции данных — 102, что больше 100, что не соответствует требованиям, поэтому необходимо найти предыдущие данные.

Однако идентификатор предыдущих данных равен 103, что также больше 101, поэтому он не соответствует требованиям Найдите данные в предыдущей строке.

Наконец, строка данных с идентификатором транзакции 100 найдена и возвращена.

Проще говоря:

  • Незафиксированный Невидимый
  • Отправляется после запуска текущей транзакции, невидимый
  • Изменено текущей транзакцией, видимо
  • Отправлено до начала текущей транзакции, видимо

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

Также необходимо добавить, что исторические версии данных сохраняются вundo log, и InnoDB определит, что, когда эти старые версии данных не нужны, они будут очищены, чтобы освободить место.

Кроме того, все парыundo logобновления будут сохраняться вredo logсередина.

напиши в конце

Прежде всего, спасибо, что вы здесь!

Эту статью давно голубят, извините, слишком много всего в последнее время.

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

Если в этой статье есть что-то, что я неправильно понял или недостаточно ясно, добро пожаловать на обмен и совместное обучение!

Следующая статья будет скоро доставлена, на этот раз точно не голуби (смеется)

PS: Если у вас есть другие вопросы, вы также можете найти автора на официальном аккаунте. Кроме того, все статьи будут обновлены в общедоступном аккаунте как можно скорее, добро пожаловать, чтобы поиграть с автором~