предисловие
Принцип реализации MVCC — очень часто задаваемый вопрос в интервью. Недавно его обсуждали мелкие партнеры в технической дискуссионной группе. Давайте поговорим об этом, пока Национальный день свободен.
- публика:маленький мальчик собирает улиток
1. Обзор соответствующих точек знаний базы данных
1.1 Что такое транзакция базы данных и почему существует транзакция
Транзакция, состоящая из ограниченной последовательности операций с базой данных, все из которых выполняются или не выполняются ни разу, является неделимой единицей работы.
Если A переводит 100 юаней B, сначала вычтите 100 юаней со счета A, а затем добавьте 100 юаней на счет B. Если после вычета 100 юаней А, прежде чем добавить их к Б, банковская система ненормальна, и, наконец, баланс А уменьшается, но баланс Б не увеличивается. Итак, транзакция необходима, чтобы откатить деньги А, это так просто.
Зачем иметь дело?Это необходимо для обеспечения возможной согласованности данных.
1.2 Какие функции включает в себя транзакция?
Существует четыре типичных характеристики транзакций, а именно ACID, атомарность, непротиворечивость, изоляция и долговечность.
- Атомарность: транзакция выполняется как единое целое, и либо все операции над базой данных, содержащиеся в ней, либо не выполняются.
- Согласованность: это означает, что данные не будут уничтожены до начала транзакции и после ее завершения.Если счет A переводит 10 юаней на счет B, общая сумма A и B остается неизменной независимо от того, успешна она или нет.
- Изоляция: когда несколько транзакций имеют одновременный доступ, транзакции изолированы друг от друга между транзакциями, которые не должны мешать другим вопросам, для изоляции между несколькими одновременными транзакциями. .
- Постоянство: указывает, что после фиксации транзакции операционные изменения, внесенные транзакцией в базу данных, будут сохраняться в базе данных.
1.3 Проблемы с параллелизмом транзакций
Параллелизм транзакций вызоветГрязные чтения, неповторяемые чтения, фантомные чтенияпроблема.
1.3.1 Грязные чтения
Если транзакция считывает другие данные, которые не были представлены, мы сказали, что существует грязное чтение.
Предположим, что теперь есть две транзакции A, B:
- Теперь предположим, что баланс А равен 100, и транзакция А готовится запросить баланс Джея.
- Транзакция B сначала вычитает баланс Джея, вычитая 10, но еще не отправлена
- Последний баланс, прочитанный А, равен 90, что является балансом после вычета
Поскольку транзакция A читает транзакцию BНезафиксированные данные, что является грязным чтением.
1.3.2 Неповторяемое чтение
В одной и той же транзакции читать несколько раз до и после, и содержимое прочитанных данных несовместимо
Предположим, что теперь есть две транзакции A и B:
- Транзакция A сначала запрашивает баланс Джея, и результат равен 100.
- В это время транзакция B вычитает баланс счета Джея, вычитает 10 и отправляет транзакцию.
- Транзакция А пошла проверить баланс счета Джея и обнаружила, что он стал равным 90.
В транзакцию А вмешивается транзакция Б! В рамках транзакции A два идентичных запроса считывают одну и ту же запись, но возвращают разные данные, т.е.неповторяемое чтение.
1.3.3 Фантомное чтение
Если транзакция сначала запрашивает некоторые записи в соответствии с некоторыми условиями поиска, а когда транзакция не зафиксирована, другая транзакция записывает некоторые записи, соответствующие этим условиям поиска (например, вставка, удаление, обновление), это означает, что что-то произошло.галлюцинации.
Предположим, что теперь есть две транзакции A, B:
- Транзакция A сначала запрашивает запись учетной записи с идентификатором больше 2 и получает две записи с идентификатором записи = 2 и идентификатором = 3.
- В это время транзакция B открывается, вставляет запись с id=4 и фиксирует
- Транзакция A снова выполняет тот же запрос, но получает 3 записи с id=2,3,4.
Транзакция A запрашивает диапазон наборов результатов, другая параллельная транзакция B вставляет новые данные в этот диапазон и фиксирует транзакцию, а затем транзакция A снова запрашивает тот же диапазон, но наборы результатов, прочитанные дважды, отличаются.Это фантомное чтение.
1.4 Четыре основных уровня изоляции
Чтобы решить проблему существования параллельных транзакцийГрязные чтения, неповторяемые чтения, фантомные чтенияИ другие проблемы, дядя базы данных разработал четыре уровня изоляции. соответственноЧтение незафиксированного, Чтение зафиксированного, Повторяемое чтение, Сериализуемый.
1.4.1 Незафиксированное чтение
Чтение недолгого уровня изоляции, только два данныхнельзя изменить одновременно, но при изменении данных, даже если транзакцияне отправлен, могут быть прочитаны другими транзакциями, этот уровень изоляции транзакцийГрязные чтения, повторные чтения, фантомные чтенияПроблема;
1.4.2 Прочитано
Читать преданный уровень изоляции, текущая транзакция может только прочитать в другие транзакцииОтправитьданных, поэтому уровень изоляции этой транзакцииГрязные чтения решеныпроблема, но все жеповторное чтение, фантомное чтениепроблема;
1.4 3 Повторяемое чтение
Уровень изоляции повторяющегося чтения ограничивает модификацию при чтении данных, поэтомуРазрешенные повторяющиеся чтенияОднако при чтении данных диапазона данные могут быть вставлены, поэтому все равно будетгаллюцинациипроблема;
1.4.4 Сериализация
Самый высокий уровень изоляции для транзакций, при котором все транзакциипорядок сериализацииреализовано. Можно избежать всех проблем параллелизма грязных чтений, неповторяемых чтений и фантомных чтений. Однако при таком уровне изоляции транзакций выполнение транзакций потребляет много ресурсов.
1.4.5 Какие проблемы параллелизма существуют на четырех основных уровнях изоляции?
уровень изоляции | грязное чтение | неповторяемое чтение | галлюцинации |
---|---|---|---|
читать незафиксированные | √ | √ | √ |
чтение зафиксировано | × | √ | √ |
повторяемое чтение | × | × | √ |
сериализовать | × | × | × |
1.5 Как база данных обеспечивает изоляцию транзакций?
база данных проходитзамок, чтобы добиться изоляции транзакций. Это похоже на то, что если вы хотите побыть в одиночестве и чтобы вас не беспокоили другие, вы можете поставить замок на дверь.
Блокировка действительно проста в использовании и может обеспечить изоляцию. НапримерУровень изоляции сериализации реализуется блокировкой. Однако частая блокировка делает невозможным изменение данных при их чтении, и нет возможности прочитать их при изменении данных.Снижение производительности базы данных.
Итак, как решить проблему с производительностью после блокировки?
Ответ,Многоверсионный контроль параллелизма MVCC! Он понимает, что чтение данных не нужно блокировать, и позволяет одновременно изменять данные чтения. Его также можно прочитать при изменении данных.
2. Что такое MVCC?
MVCC, т.е.Многоверсионный контроль параллелизма. Это метод управления параллелизмом, обычно в системе управления базами данных, для обеспечения одновременного доступа к базе данных, на языке программирования для достижения транзакционной памяти.
С точки зрения непрофессионала, в базе данных одновременно существует несколько версий данных, а не несколько версий всей базы данных, но несколько версий записи существуют одновременно.Когда транзакция работает с ней, вам необходимо проверить это 1. Запишите идентификатор версии транзакции скрытого столбца, сравните идентификатор транзакции и оцените, какую версию данных следует читать в соответствии с уровнем изоляции транзакции.
Чтение уровня изоляции базы данныхсовершено, повторяемое чтениеВсе они реализованы на основе MVCC.По сравнению с простым и грубым способом блокировки, он использует лучший способ обработки конфликтов чтения и записи, что может эффективно улучшить параллельную производительность базы данных.
3. Ключевые моменты реализации MVCC
3.1 Номер версии транзакции
Перед запуском каждой транзакции она получаетавтоматическое приращениеПри длинном идентификаторе транзакции о порядке выполнения транзакций можно судить по идентификатору транзакции. Это номер версии транзакции.
3.2 Неявные поля
Для механизма хранения InnoDB каждая строка имеет два скрытых столбца.trx_id,roll_pointer, если в таблице нет первичного ключа и уникального ключа, отличного от NULL, также будет третий скрытый столбец первичного ключаrow_id.
имя столбца | Тебе обязательно | описывать |
---|---|---|
row_id | нет | Монотонно увеличивающийся идентификатор строки, не требуется, занимает 6 байт. |
trx_id | да | Запишите идентификатор транзакции транзакции, которая оперирует данными. |
roll_pointer | да | Этот скрытый столбец эквивалентен указателю на журнал отмены сегмента отката. |
3.3 undo log
журнал отмены,журнал отката, который используется для записи информации перед изменением данных. Перед изменением записи таблицы данные будут скопированы в журнал отмен.Если транзакция откатывается, данные можно восстановить через журнал отмен.
Можно считать, что при удалении записи в журнал отмен будет записана соответствующая запись вставки, а при обновлении записи будет записана соответствующая противоположная запись обновления.
что такое журнал отменыиспользоватьШерстяная ткань?
- При откате транзакции гарантируют атомарность и согласованность.
- для MVCCСнимок прочитан.
3.4 Цепочка версий
Когда несколько транзакций обрабатывают строку данных параллельно, модификация строки данных разными транзакциями будет генерировать несколько версий, а затем с помощью указателя отката (roll_pointer) они соединяются для формирования связанного списка, который называется связанным списком. список.цепочка версий. следующее:
На самом деле вариант с цепочкой мы видимНомер версии транзакции, скрытые столбцы таблицы и журнал отменотношения между ними. Проведем небольшой анализ.
- Допустим сейчас есть таблица core_user, в таблице есть кусок данных, id равен 1, а имя Sun Quan:
- Теперь запустите транзакцию A: выполните в таблице core_user
update core_user set name ="曹操" where id=1
, будет выполняться следующий процесс
- Сначала получите идентификатор транзакции = 100
- Скопируйте данные таблицы core_user перед внесением изменений в журнал отмены.
- Измените данные с id=1 в таблице core_user и измените имя на Cao Cao.
- Измените измененный идентификатор транзакции данных = 101 на текущий номер версии транзакции и поместитеroll_pointerУказывает на адрес данных журнала отмены.
3.5 Чтение моментального снимка и текущее чтение
Снимок читается:Читается видимая версия данных записи (есть более старая версия). Без блокировки обычные операторы select представляют собой чтение моментальных снимков, например:
select * from core_user where id > 2;
текущее чтение: чтение последней версии записанных данных, а явная блокировка — это текущая версия чтения.
select * from core_user where id > 2 for update;
select * from account where id>2 lock in share mode;
3.6 Read View
- Что такое просмотр для чтения?Это представление для чтения создается, когда транзакция выполняет оператор SQL. На самом деле, в innodb каждый оператор SQL получает представление для чтения перед выполнением.
- В чем польза режима чтения?Он в основном используется для оценки видимости, то есть для определения того, какая версия данных видна для текущей транзакции~
Как в режиме чтения обеспечивается оценка видимости? Давайте рассмотрим несколько важных свойств режима чтения.
- M_IDS: идентификатор транзакции чтения и записи активных (неотправленных) в текущей системе, его структура данных представляет собой список.
- min_limit_id: указывает наименьший идентификатор транзакции в активных транзакциях чтения и записи в текущей системе при создании ReadView, то есть наименьшее значение в m_ids.
- max_limit_id: указывает значение идентификатора, которое должно быть присвоено следующей транзакции в системе при создании ReadView.
- Creator_trx_id: идентификатор транзакции, которая создала текущее представление для чтения.
Просмотр для чтения Сопоставление правил условияследующее:
- если идентификатор транзакции данных
trx_id < min_limit_id
, указывающее, что транзакция, сгенерировавшая эту версию, была зафиксирована до создания представления для чтения (поскольку идентификатор транзакции увеличивается), поэтому текущая транзакция может получить доступ к этой версии. - если
trx_id>= max_limit_id
, указывающий, что транзакция, создающая эту версию, создается после создания ReadView, поэтому текущая транзакция не может получить доступ к этой версии. - если
min_limit_id =<trx_id< max_limit_id
, точки талии необходимо обсудить в 3-х ситуациях
- (1) Если
m_ids
Включаютtrx_id
, это означает, что транзакция не была отправлена на момент генерации Read View, но если данныеtrx_id
равныйcreator_trx_id
, что указывает на то, что данные генерируются сами по себе, поэтомувидимыйиз.- (2) если
m_ids
Включаютtrx_id
,иtrx_id
не равноcreator_trx_id
, при создании Read View транзакция не фиксируется и не создается сама по себе, поэтому текущая транзакция такженевидимыйиз;- (3) Если
m_ids
не содержитtrx_id
, это означает, что ваша транзакция была отправлена до создания представления для чтения, и результат модификации можно увидеть в текущей транзакции.
4. Анализ принципа реализации MVCC
4.1 Что представляет собой процесс запроса записи на основе MVCC
- Получить номер версии самой транзакции, то есть ID транзакции
- Получить представление для чтения
- Запросите полученные данные, а затем сравните номера версий транзакций в представлении для чтения.
- Если он не соответствует правилам видимости Read View, то есть требуются исторические снимки в журнале отмены;
- Наконец, верните данные, соответствующие правилам
InnoDB реализует MVCC через Read View+ Undo Log
Реализация, отменить журнал сохранил исторический снимок, прочитайте правила видимости, чтобы помочь определить, отображается ли текущая версия данных.
4.2 Чтение уровня изоляции представления (RC), существует процесс анализа, который не повторяется, проблемы с чтением
- Создайте таблицу core_user и вставьте данные инициализации следующим образом:
- Уровень изоляции установлен на чтение зафиксированных (RC), транзакция A и транзакция B запрашивают и изменяют таблицу core_user одновременно.
事务A: select * fom core_user where id=1
事务B: update core_user set name =”曹操”
Последовательность выполнения следующая:
Наконец, результат запроса транзакции A:имя = Цао Цаозаписи, мыНа основе MVCC, чтобы проанализировать процесс выполнения:
(1) A запускает транзакцию и сначала получает идентификатор транзакции 100.
(2).B открывает транзакцию и получает идентификатор транзакции 101.
(3) Транзакция A создает представление для чтения, и значение, соответствующее представлению для чтения, выглядит следующим образом.
Переменная | ценность |
---|---|
m_ids | 100, 101 |
max_limit_id | 102 |
min_limit_id | 100 |
creator_trx_id | 100 |
Затем вернитесь к цепочке версий: начните выбирать видимые записи из цепочки версий:
Как видно из рисунка, содержимое столбца имя последней версии孙权
, эта версияtrx_id
Значение равно 100. Запустите проверку правила видимости для чтения:
min_limit_id(100)=<trx_id(100)<102;
creator_trx_id = trx_id =100;
Отсюда запись trx_id=100 видна для текущей транзакции. Итак, выяснилось, что имя孙权
запись о.
(4) Транзакция B выполняет операцию модификации и меняет имя на Cao Cao. Скопируйте исходные данные в журнал отмен, затем измените данные, отметьте идентификатор транзакции и адрес предыдущей версии данных в журнале отмен.
(5) Зафиксировать транзакцию
(6) Транзакция A снова выполняет операцию запроса,Создать новое представление для чтения, соответствующее значение Read View выглядит следующим образом
Переменная | ценность |
---|---|
m_ids | 100 |
max_limit_id | 102 |
min_limit_id | 100 |
creator_trx_id | 100 |
Затем снова вернитесь к цепочке версий: выберите видимые записи из цепочки версий:
Как видно из рисунка, содержимое столбца имя последней версии曹操
, эта версияtrx_id
Значение равно 101. Запустите проверку правила видимости Read View:
min_limit_id(100)=<trx_id(101)<max_limit_id(102);
但是,trx_id=101,不属于m_ids集合
следовательно,trx_id=101
Эта запись видна для текущей транзакции. Таким образом, запрос SQL состоит в том, что имя曹操
запись о.
Ввиду изложенного, вЧитайте преданный (RC) уровень изоляцииДалее в той же транзакции два одинаковых запроса читают одну и ту же запись (id=1), но возвращают разные данные (В первый раз я узнал, что это был Сунь Цюань, а во второй раз я узнал, что это был рекорд Цао Цао.), поэтому уровень изоляции RC существуетнеповторяемое чтениеПроблемы параллелизма.
4.3 Уровень изоляции Repeatable Read (RR), анализ решения проблемы неповторяющегося чтения
Как решить проблему неповторяющегося чтения на уровне изоляции RR? Давайте еще раз посмотрим,
Продолжайте процесс в разделе 4.2 или эту транзакцию A и транзакцию B следующим образом:
4.3.1 Режим чтения работает по-разному на разных уровнях изоляции
На самом деле методы работы Read view при разных уровнях изоляции транзакций различаются, RR может решить проблему неповторяющегося чтения, что аналогичноКак работает режим чтения.
- На уровне изоляции read commited (RC) в рамках одной и той же транзакцииКаждый запрос создает новую копию представления чтения., что может привести к тому, что данные, считанные до и после одной и той же транзакции, могут быть несогласованными (проблема неповторяющегося параллелизма чтения).
begin | |
---|---|
select * from core_user where id =1 | Создать представление для чтения |
/ | / |
/ | / |
select * from core_user where id =1 | Создать представление для чтения |
- На уровне изоляции Repeatable Read (RR)В транзакции получается только одно прочитанное представление, которые совместно используются копиями, чтобы гарантировать, что данные каждого запроса одинаковы.
begin | |
---|---|
select * from core_user where id =1 | Создать представление для чтения |
/ | |
/ | |
select * from core_user where id =1 | Прочитать Посмотреть поделиться копией |
4.3.2 Анализ случая
Давай пересечемся, вернемсяВсего 4.2 пример, а затем при выполнении второго запроса:
Транзакция A снова выполняет операцию запроса и повторно использует старую копию Read View, Значения, соответствующие Read View, следующие
Переменная | ценность |
---|---|
m_ids | 100, 101 |
max_limit_id | 102 |
min_limit_id | 100 |
creator_trx_id | 100 |
Затем снова вернитесь к цепочке версий: выберите видимые записи из цепочки версий:
Как видно из рисунка, содержимое столбца имя последней версии曹操
, эта версияtrx_id
Значение равно 101. Запустите проверку правила видимости для чтения:
min_limit_id(100)=<trx_id(101)<max_limit_id(102);
因为m_ids{100,101}包含trx_id(101),
并且creator_trx_id (100) 不等于trx_id(101)
так,trx_id=101
Эта запись для текущей транзакцииНевидимыйиз. В настоящее время цепочка версийroll_pointer
перейти к следующей версии,trx_id=100
Эта запись, проверьте еще раз, видна ли она:
min_limit_id(100)=<trx_id(100)< max_limit_id(102);
因为m_ids{100,101}包含trx_id(100),
并且creator_trx_id (100) 等于trx_id(100)
так,trx_id=100
Эта запись для текущей транзакциивидимыйиз. То есть на уровне изоляции Repeatable Read (RR) повторно используется старая копия Read View, что решает проблему.неповторяемое чтениеПроблема.
4.4 По легенде интернета, решил ли MVCC проблему фантомного чтения?
В интернете Jianghu ходит легенда, что уровень изоляции RR MVCC решает проблему фантомного чтения, давайте разберем ее вместе.
4.4.1 На уровне RR, пример чтения моментального снимка, проблемы с фантомным чтением нет
Как видно из рисунка, набор результатов запроса на шагах 2 и 6 не изменился,Кажется, уровень RR решил проблему фантомного чтения.~
4.4.2 Пример текущего чтения на уровне RR
Предположим, что сейчасaccount表
, в таблице 4 фрагмента данных, уровень RR.
- Откройте транзакцию A и выполнитетекущее чтение, запросите все записи с id>2.
- Затем откройте транзакцию B и вставьте часть данных с id=5.
Процесс выглядит следующим образом:
Очевидно, что когда транзакция B выполняет операцию вставки, она блокируется, потому что выполняется транзакция A.select ... lock in share mode
(текущее чтение) добавляются блокировки не только на 2 записи с id=3,4, но и вid > 2
Добавлено в этот диапазонгэп замок.
Следовательно, мы можем обнаружить, что на уровне изоляции RR заблокированные операторы select, update, delete и другие будут использовать блокировку промежутка + блокировку смежной клавиши, чтобы заблокировать диапазон между записями индекса и избежать вставки записей между диапазонами.Избегайте записей призрачной строкито есть уровень изоляции RR решает проблему фантомного чтения? ? ?
4.4.3 Похоже, что в этом особом сценарии есть проблема с галлюцинациями.
Фактически, в транзакции A на приведенном выше рисунке большеupdate account set balance=200 where id=5;
На этом шаге одна и та же транзакция и один и тот же SQL приводят к разным наборам результатов.Этот результат соответствует определению фантомного чтения~
Этот вопрос, дорогой друг, вы думаете, что это проблема фантомного чтения? Поэтому, по сути, проблема фантомного чтения все же может быть на уровне изоляции RR. Добро пожаловать всем, чтобы оставить сообщение в области комментариев.