Тайна MySQL (6): последовательное неблокирующее чтение InnoDB

задняя часть база данных MySQL SQL
Тайна MySQL (6): последовательное неблокирующее чтение InnoDB

Согласованная неткая редакция относится к тому, как двигатель хранения InnoDB считывает данные строки в текущей базе данных через Multi-Version Control (MVVC). Если ряд чтения выполняет операцию удаления или обновления, то операция чтения не будет ждать выпуска блокировки строки. Вместо этого InnoDB будет читать снимок строки.

一致性非锁定读示意图

 На приведенном выше рисунке наглядно показан механизм последовательного неблокирующего чтения InnoDB. Это называется неблокирующим чтением, потому что нет необходимости ждать освобождения монопольной блокировки строки. Данные моментального снимка относятся к данным предыдущей версии строки, и каждая запись строки может иметь несколько версий, что обычно называется технологией многоверсионности строк. Результирующий контроль параллелизма называется управлением параллелизмом нескольких версий (MVVC). InnoDB реализует MVVC через журнал отмены. Сам журнал отката используется для отката данных в транзакции, поэтому сами данные моментального снимка не имеют дополнительных издержек. Кроме того, чтение моментальных снимков не требует блокировки, поскольку ни одна транзакция не требует изменения исторических данных.

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

  При уровнях изоляции транзакций READ COMMITTED и REPEATABLE READ InnoDB использует последовательное чтение без блокировки. Однако определение данных моментального снимка отличается. На уровне изоляции транзакций READ COMMITTED последовательное чтение без блокировки всегда считывает последний моментальный снимок заблокированной строки. При уровне изоляции транзакции REPEATABLE READ считывается версия данных строки в начале транзакции.

  Давайте рассмотрим пример, чтобы подробно проиллюстрировать описанную выше ситуацию.

# session A
mysql> BEGIN;
mysql> SELECT * FROM test WHERE id = 1;

  Сначала мы открываем транзакцию явно в сессии A, а затем читаем данные с id 1 в тестовой таблице, но транзакция не завершается. В то же время пользователь открывает другой сеанс B, который может имитировать параллельные операции, а затем выполняет следующие операции над сеансом B:

# session B
mysql> BEGIN;
mysql> UPDATE test SET id = 3 WHERE id = 1;

 В транзакции сеанса B запись с id 1 в тестовой таблице меняется на id=3, но транзакция также не фиксируется, поэтому строка с id=1 фактически добавляет монопольную блокировку. Поскольку InnoDB использует согласованные неблокирующие операции чтения на уровнях изоляции транзакций READ COMMITTED и REPEATABLE READ, если сеанс A снова считывает запись с идентификатором 1, он все еще может читать те же данные. На данный момент нет разницы между уровнями изоляции транзакций READ COMMITTED и REPEATABLE READ.

会话A和会话B示意图

  Как показано выше, когда сеанс B фиксирует транзакцию, сеанс A запускается снова.SELECT * FROM test WHERE id = 1При выполнении оператора SQL результаты, полученные на двух уровнях изоляции транзакций, различаются.

  Для уровня изоляции транзакций READ COMMITTED всегда считывается последняя версия строки, а если строка заблокирована, считывается последний снимок версии строки. Поскольку транзакция сеанса B была зафиксирована, результирующий набор приведенного выше оператора SQL на этом уровне изоляции пуст.

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

MVVC

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

Multiversion concurrency control (MCC or MVCC), is a concurrency control method commonly used by database management systems to provide concurrent access to the database and in programming languages to implement transactional memory.

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

 Рассмотрите реалистичный сценарий. Администратор хочет запросить общую сумму депозитов всех пользователей.Предполагается, что за исключением пользователя A и пользователя B, общая сумма депозитов других пользователей равна 0, а пользователи A и B имеют депозиты по 1000, поэтому общая сумма депозитов всех пользователей 2000. Однако в процессе запроса пользователь А переведет деньги пользователю Б. Диаграмма последовательности операции передачи и общей операции запроса показана на рисунке ниже.

转账和查询的时序图

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

 Использование механизма блокировки может решить вышеуказанные проблемы. Общая транзакция запроса блокирует прочитанные строки и снимает блокировки со всех строк после завершения операции. Поскольку депозит пользователя А заблокирован, операция перевода блокируется до тех пор, пока не будет зафиксирована общая транзакция запроса и не будут сняты все блокировки.

使用锁机制
  Однако в это время может возникнуть новая проблема: когда операция передачи выполняется от пользователя Б к пользователю А, это приводит к тупиковой ситуации. Транзакция передачи сначала заблокирует данные пользователя B и будет ожидать блокировки данных пользователя A, но транзакция, которая запрашивает общую сумму, сначала блокирует данные пользователя A и ожидает блокировки данных пользователя B.

 Использование механизма MVVC также может решить эту проблему. Общая транзакция запроса сначала считывает депозит на счете пользователя A, а затем транзакция перевода изменяет депозиты на счетах пользователя A и пользователя B. Когда транзакция общего запроса считывает депозит пользователя B, она не считывает измененные данные транзакции перевода, но читает Копию данных в начале этой транзакции (под уровнем изоляции REPEATABLE READ).

使用MVVC机制

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

MVVC-реализация InnoDB

 Контроль параллелизма в нескольких версиях — это только техническая концепция, единого стандарта реализации не существует. Его основная концепция — моментальные снимки данных. Различные транзакции получают доступ к разным версиям моментальных снимков данных для достижения разных уровней изоляции транзакций. Хотя буквально это означает, что существует несколько версий моментальных снимков данных, это не означает, что база данных должна копировать данные и сохранять несколько файлов данных, что потребует много места для хранения. InnoDB умело реализует моментальные снимки данных с несколькими версиями с помощью журналов отмены транзакций.

  Иногда необходимо откатить транзакцию базы данных, а затем отменить предыдущую операцию. Поэтому InnoDB генерирует журнал отмены при изменении данных. Когда транзакцию необходимо откатить, InnoDB может использовать эти журналы отмены, чтобы откатить данные до того состояния, в котором они были до модификации.

 В зависимости от поведения журнал отмены делится на два типа: вставка журнала отмены и обновление журнала отмены.

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

Журнал отмены обновления — это журнал отмены, созданный при операции обновления или удаления, поскольку он повлияет на существующие записи.Чтобы обеспечить механизм MVCC, журнал отмены обновления не может быть удален при фиксации транзакции, но помещается в фиксация транзакции.Войдите в список истории и подождите, пока поток очистки выполнит окончательную операцию удаления.

  Чтобы гарантировать отсутствие конфликтов при записи соответствующих журналов отмены, когда транзакции выполняются одновременно, InnoDB использует сегменты отката для обеспечения одновременной записи и сохранения журналов отмены. Сегмент отката на самом деле является файловой организацией Undo.

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

初始状态

Когда транзакция 2 использует оператор UPDATE для изменения данных строки, она сначала заблокирует строку монопольной блокировкой, скопирует текущее значение строки в журнал отмены, а затем фактически изменит значение текущей строки и наконец, введите идентификатор транзакции и используйте откат. Указатель указывает на неизмененную строку в журнале отмены. Как показано ниже.

第一次修改

 При изменении транзакции 3 процесс обработки транзакции 2 аналогичен, как показано на следующем рисунке.

第二次修改

 Уровень изоляции REPEATABLE READ после того, как транзакция начнет читать с использованием механизма MVVC, идентификатор транзакции, который был активен в это время, будет записан и записан в Read View. При уровне изоляции READ COMMITTED новое представление для чтения создается каждый раз при чтении.

 Read View — это структура данных в InnoDB для оценки видимости записей и записи некоторых атрибутов для оценки видимости.

  • low_limit_id: db_trx_id записи строки
  • up_limit_id: db_trx_id >= это значение записано в строке, тогда строка должна быть невидима для текущего представления чтения
  • low_limit_no: Оценка операции очистки
  • rw_trx_ids: массив транзакций чтения и записи

 После создания представления чтения, когда транзакция снова выполняет операцию чтения, она сравнивает записанный db_trx_id с low_limit_id и up_limit_id в представлении чтения и массивом транзакций чтения-записи, чтобы определить видимость.

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

 Если db_trx_id больше или равен low_limit_id, запись должна быть невидимой для этого представления чтения. Если db_trx_id находится в диапазоне [up_limit_id, low_limit_id), вам необходимо проверить, существует ли db_trx_id в активном массиве транзакций чтения/записи (rw_trx_ids).Если он существует, запись невидима для текущего представления чтения.

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

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

постскриптум

  Мы узнаем о блокировках InnoDB в будущем, пожалуйста, продолжайте обращать внимание.

Справочная статья