Этот блог относится к буклету Nuggets——Как работает MySQL: понимание MySQL в корне
Хотя мы не являемся администраторами баз данных, мы можем мало знать о базе данных, но мы должны иметь относительно простое представление об индексах, транзакциях и блокировках в базе данных.Сегодня я буду говорить с вами о транзакциях.
Зачем иметь дело
Говоря о транзакциях, я не могу не упомянуть о переводах, почти во всех статьях о транзакциях упоминается этот старомодный случай, и я не исключение.
Передачу можно просто разделить на две части на уровне базы данных:
- списать сумму перевода со своего счета;
- Добавьте сумму перевода на счет другой стороны.
Если вы сначала вычтете сумму перевода со своего счета, а затем добавите сумму перевода на счет другой стороны, если вычет будет выполнен успешно, но увеличение не будет успешным, то ваш счет будет на 100 юаней меньше напрасно, и вы будет плакать без слез.
Если вы сначала добавите сумму перевода на счет другой стороны, а затем вычтете сумму перевода со своего собственного счета, если увеличение будет выполнено успешно, но вычет не будет выполнен, то счет другой стороны увеличится на 100 юаней напрасно, и нет деньги будут списаны с вашего личного счета.
То ли от этого хочется плакать без слез, то ли от счастья, банки такого не потерпят, они введут транзакции для решения такого рода проблемы.
характеристики сделки
- Атомарность: все операции, содержащиеся в транзакции, либо завершаются успешно (фиксация), либо терпят неудачу (откат).
- Непротиворечивость: целостность данных до и после выполнения транзакции непротиворечива.
- Изоляция: выполнение транзакции не должно мешать другим транзакциям.
- Долговечность: после завершения транзакции данные сохраняются в базе данных.Даже если база данных выйдет из строя после фиксации, зафиксированные данные не будут потеряны.
Есть четыре характеристики, ACID для краткости. Наиболее трудным для понимания является согласованность. Многие люди думают, что атомарность, изоляция и постоянство должны обеспечивать согласованность. Мы не занимаемся академическими исследованиями. Как объяснить согласованность? Как определить согласованность до судей.
уровень изоляции транзакций
С определенной точки зрения то, что мы можем контролировать или должны изучать, это только особенность изоляции, а для управления изоляцией есть почти одно средство настройки уровня изоляции.Давайте посмотрим на уровень изоляции транзакций.
База данных представляет собой программное обеспечение клиент-серверной архитектуры.После каждого подключения клиента к серверу будет генерироваться сеанс (сеанс).Взаимодействие между клиентом и сервером осуществляется в сеансе.Он может обрабатывать одну транзакцию,и другие транзакции ставятся в очередь и ожидаются.Когда транзакция зафиксирована, сервер обрабатывает следующую транзакцию, которая действительно «изолирована», и проблем нет, но если это так, то производительность слишком низкая, между производительностью и изоляцией, можно сделать только некоторый баланс, поэтому база данных предоставляет нам несколько уровней изоляции на выбор.
Прежде чем говорить об уровнях изоляции, давайте рассмотрим проблемы, возникающие при одновременном выполнении транзакций.
Чтобы гарантировать, что следующее описание может пройти гладко, мы должны сначала создать таблицу:
CREATE TABLE `student` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`grade` int(11) DEFAULT NULL COMMENT '年级',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
грязное письмо
как показано на рисунке:- sessionA и sessionB начинают транзакцию;
- sessionB изменил имя id=2 на «Underground King»;
- sessionA изменил имя id=2 на «Dream King»;
- sessionB откатил транзакцию;
- sessionA фиксирует транзакцию.
Если сеанс B откатывает модификацию сеанса А при откате транзакции, что приводит к потере представления сеанса А, это явление называется «грязной записью». сессия А будет сбита с толку.Я явно модифицировал данные и отправил данные,почему данные не изменились.
грязное чтение
как показано на рисунке:- sessionA и sessionB начинают транзакцию;
- sessionB изменил имя с id=2 на "Underground King", которое еще не отправлено;
- SessionA запрашивает данные с идентификатором = 2. Если имя прочитанных данных — «Underground King», то есть он считывает данные, которые не были отправлены sessionB, это называется «грязным чтением».
неповторяемое чтение
как показано на рисунке:- sessionA и sessionB начинают транзакцию;
- sessionA запрашивает данные с id=2, если имя "Underground King",
- sessionB изменил имя id=2 на «Dream King», а затем отправил транзакцию;
- SessionA еще раз запросил данные с id = 2. Если имя «Dream King», это означает, что в одной и той же транзакции данные, считанные до и после sessionA, несовместимы, что называется «неповторяемым чтением».
галлюцинации
как показано на рисунке:- sessionA и sessionB начинают транзакцию;
- sessionA запрашивает данные name="Underground King", предполагая, что в это время читается запись;
- sessionB вставляет еще один фрагмент данных с name="Underground King", а затем отправляет его;
- sessionA снова запрашивает данные с name="Underground King".Если в это время считываются две записи, а второй запрос считывает данные, которые не были запрошены первым запросом, это называется "фантомное чтение".
Четыре уровня изоляции
Мы знаем, какие проблемы возникнут при одновременном выполнении транзакций. Некоторые проблемы более серьезные, а некоторые проблемы относительно незначительные. В целом, мы считаем, что порядок серьезности следующий:
Грязная запись > Грязное чтение > Неповторяемое чтение > Фантомное чтение
В стандартном определении SQL установлены четыре уровня изоляции для решения вышеуказанных проблем:
- ЧИТАЙТЕ НЕЗАКОННО: Самый низкий уровень изоляции имеет три проблемы: «грязное чтение», «неповторяемое чтение» и «фантомное чтение».
- ПРОЧИТАТЬ СОВЕРШЕНО: Уровень изоляции SQL Server по умолчанию может избежать «грязного чтения», и возникнут две проблемы: «неповторяющееся чтение» и «фантомное чтение».
- ПОВТОРЯЕМОЕ ЧТЕНИЕ: Это может избежать двух проблем «грязного чтения» и «неповторяемого чтения», и возникнет проблема «фантомного чтения». Уровень изоляции MySQL по умолчанию, но в MySQL этот уровень изоляции решает проблему «фантомного чтения».
- Сериализация (SERIALIZABLE): Всех проблем не будет.
Поскольку проблема грязного письма слишком серьезна, при любом уровне изоляции проблемы грязного письма не будет.
MVCC
Все вышеперечисленное - закуска.Я считаю, что большинство мелких партнеров поняли вышеприведенное содержание, поэтому я даже не представил, как изменить уровень изоляции транзакций, и я не проводил различных экспериментов, просто потратил много времени и слова в этой части.
MVCC, полное название — Mutil-Version Concurrency Control, в переводе на китайский язык — многоверсионный контроль параллелизма, MySQL использует MVCC для определения, какие данные можно читать, а какие нельзя читать в транзакции.
несколько версий
Прежде чем рассматривать MVCC, нам нужно знать еще одну точку знаний.База данных хранит построчные данные, которые хранятся в двух частях: одна является дополнительной информацией строки данных (не рассматривается в этом блоге), а другая является реальной записью данных, MySQL добавит два или три скрытых поля к каждой строке реальных записей данных:
- row_id В этом нет необходимости. Если таблица имеет пользовательский первичный ключ или Уникальный ключ, поле row_id не будет добавлено. Если нет ни того, ни другого, MySQL добавит поле row_id по собственной инициативе.
- номер транзакции Обязательный идентификатор транзакции, представляющий идентификатор транзакции, создавшей эту строку данных.
- roll_pointer Должен, указатель отката, указывающий на предыдущую версию этой строки данных.
Как показано ниже:
Здесь нам нужно сосредоточиться на идентификаторе транзакции.Когда мы начинаем транзакцию, мы не получим идентификатор транзакции сразу.Даже если мы выполним оператор select в транзакции, идентификатора транзакции нет (идентификатор транзакции равен 0), только выполнение вставки/обновления/ Это особенно важно для оператора удаления, чтобы получить идентификатор транзакции.
Среди них два поля transaction_id и roll_pointer тесно связаны с MVCC, В процессе разработки нам не нужно заботиться, но чтобы изучить MVCC, мы должны заботиться.
Если есть такая строка данных:
Это означает, что эта строка данных создана транзакцией, у которой transaction_id равен 9, а roll_pointer пуст, потому что это новая запись.На самом деле roll_pointer не пустой, если очень хочется объяснить, то нужно много обойти и понять, что он пустой, что не является большой проблемой.
Когда мы запустим транзакцию и изменим эти данные, она станет такой:
Это немного похоже на односвязный список, называемый «цепочкой версий», верхние данные — это последняя версия этих данных, roll_pointer указывает на старую версию этих данных, создается впечатление, что в версии строки есть несколько данных. , соответствует ли он концепции «многоверсионности» в «управлении многоверсионностью»? Так как же это делает «управление параллелизмом», не волнуйтесь, продолжайте читать.
ReadView
Эй, следующее приведет к новой концепции: ReadView.
Для READ UNCOMMITTED вы можете читать данные, которые не были отправлены другими транзакциями, поэтому вы можете напрямую читать последнюю версию этих данных.Для SERIALIZABLE вы можете получить доступ к записям, заблокировав их.
Остальные - READ COMMITTED и REPEATABLE READ.Эти два уровня изоляции транзакций должны гарантировать, что прочитанные данные были зафиксированы другими транзакциями, то есть последняя версия строки данных не может быть считана без мозгов, но эти два Там все еще есть некоторые различия.Основной вопрос: «Какую версию этих данных я могу прочитать?»
Чтобы решить эту проблему, появилась концепция ReadView, которая содержит четыре важных элемента:
- m_ids: указывает активную коллекцию идентификаторов транзакций в системе при создании ReadView.
- min_trx_id: указывает минимальный идентификатор транзакции, активный в системе при создании ReadView, то есть минимальное значение в m_ids.
- max_trx_id: указывает идентификатор, который система должна назначить следующей транзакции при создании ReadView.
- Creator_trx_id: указывает идентификатор транзакции, сгенерировавшей ReadView.
С этим ReadView вечная проблема «какую версию этих данных я могу прочитать» может быть решена, если используется следующий метод оценки:
- Если trx_id доступной версии совпадает с Creator_trx_id в ReadView, это означает, что текущая версия «создана» вами и может быть прочитана.
- Если trx_id доступной версии меньше, чем min_trx_id в ReadView, это означает, что транзакция, сгенерировавшая версию, была зафиксирована при создании ReadView, поэтому версию можно прочитать.
- Если trx_id версии, к которой осуществляется доступ, больше или равен значению max_trx_id в ReadView, это означает, что транзакция, создающая эту версию, открывается после того, как текущая транзакция генерирует ReadView, поэтому эта версия не может быть прочитана.
- Если trx_id, который сгенерировал доступную версию, находится между min_trx_id и max_trx_id, необходимо оценить, находится ли trx_id в m_ids: если это так, это означает, что когда был создан ReadView, транзакция, сгенерировавшая версию, все еще была активной (не commit), и эта версия не может быть считана, если это не так, то это означает, что при создании ReadView транзакция, сгенерировавшая версию, была зафиксирована, и версию можно считать.
Если последняя версия определенных данных не может быть прочитана, следуйте по указателю roll_pointer, чтобы найти предыдущую версию данных, продолжайте делать вышеупомянутое суждение и т. д., если первая версия также не видна, это означает, что данные в настоящее время транзакция полностью невидима, и результат запроса не содержит этой записи.
Прочитав вышеприведенное описание, чувствуете ли вы себя «в тумане», «непонятном» или даже «мозговой боли, всему человеку нездоровится».
Давайте объясним это по-другому, чтобы увидеть, легче ли это понять:
В момент запуска транзакции (выполнение операции CURD) будет создан ReadView.Для версии данных trx_id возможны следующие три ситуации:- Если он падает на низкий уровень воды, это означает, что транзакция, сгенерировавшая эту версию, была зафиксирована, или текущая транзакция сгенерировала ее сама, и эта версия видна.
- Если он попадает на верхнюю отметку, это означает, что транзакция, сгенерировавшая эту версию, была создана в будущем, и эта версия не видна.
- Если он попадает в средний уровень воды, возможны две ситуации: а) если trx_id текущей версии находится в списке активных транзакций, это означает, что эта версия сгенерирована еще не зафиксированной транзакцией, и эта версия не видна; б) Если trx_id текущей версии отсутствует в списке активных транзакций, это означает, что эта версия сгенерирована зафиксированной транзакцией, и эта версия видна.
Выше я кратко объяснил ReadView и использовал два метода, чтобы проиллюстрировать, как определить, видна ли текущая версия данных. Интересно, есть ли у вас расплывчатая концепция. С базовой концепцией ReadView мы можем В частности, давайте посмотрим, почему две транзакции уровни изоляции READ COMMITTED и REPEATABLE READ считывают разные данные, а также то, как применяются приведенные выше правила.
READ COMMITTED — ReadView создается каждый раз при чтении данных
Предположим сейчас, есть только одна активная транзакция T, ID транзакции 100, и данные изменены, но не отправлены, цепочка формирования такая:
Теперь, когда транзакция A запущена и оператор select выполнен, будет создан ReadView, m_ids — [100], min_trx_id — 100, max_trx_id — 101, а создатель_trx_id — 0.
Почему m_ids только один, а создатель_trx_id равен 0? Здесь снова идентификатор транзакции можно получить, только выполнив оператор вставки/обновления/удаления в транзакции.
Итак, какие данные будет читать оператор select, выполняемый транзакцией A?
- Судя по последней версии данных, имя "Dream King", соответствующий trx_id равен 100, а trx_id находится в m_ids, что указывает на то, что текущая транзакция является активной транзакцией. Эта версия данных создана транзакцией, которая еще не была отправлено, поэтому эта версия невидима. .
- Следуйте по указателю roll_pointer, чтобы найти предыдущую версию этих данных, имя — «Подземный король», соответствующий trx_id — 99, а min_trx_id в ReadView — 100, trx_id
Таким образом, имя прочитанных данных - «Подземный король».
Мы отправляем транзакцию T, и транзакция A снова выполняет оператор select.В это время транзакция A снова создает ReadView, m_ids — [], min_trx_id — 0, max_trx_id — 101, а Creator_trx_id — 0.
Поскольку транзакция T зафиксирована, активных транзакций нет.
Тогда какие данные будет считывать транзакция A, когда она выполнит оператор select во второй раз?
- Судя по последней версии данных, имя "Dream Underground King", соответствующий trx_id равен 100, а в m_ids его нет, что указывает на то, что эта версия данных создана отправленной транзакцией, и эта версия видна.
Таким образом, имя прочитанных данных - «Король мечты».
ПОВТОРЯЕМОЕ ЧТЕНИЕ — первое чтение данных создает ReadView
Допустим, в системе сейчас только одна активная транзакция T, id транзакции 100, данные в транзакции изменены, но не отправлены, результирующая цепочка версий выглядит следующим образом:
Теперь, когда транзакция A запущена и оператор select выполнен, будет создан ReadView, m_ids — [100], min_trx_id — 100, max_trx_id — 101, а создатель_trx_id — 0.
Итак, какие данные будет читать оператор select, выполняемый транзакцией A?
- Судя по последней версии данных, имя "Dream King", соответствующий trx_id равен 100, а trx_id находится в m_ids, что указывает на то, что текущая транзакция является активной транзакцией. Эта версия данных создана транзакцией, которая еще не была отправлено, поэтому эта версия невидима. .
- Следуйте по roll_ponit, чтобы найти предыдущую версию этих данных, имя — «Подземный король», соответствующий trx_id — 99, а min_trx_id в ReadView — 100, trx_id
Таким образом, имя прочитанных данных - «Подземный король».
Если вы внимательны, вы, должно быть, заметили, что я копирую и вставляю здесь, потому что при уровне изоляции транзакции REPEATABLE READ ReadView, созданный транзакцией A, выполняет оператор выбора в первый раз, а при уровне изоляции транзакции READ COMMITTED, транзакция A впервые выполняет оператор select.ReadView тот же, поэтому процесс оценки такой же, поэтому я поленился и начал копировать.
Впоследствии транзакция T фиксирует транзакцию.Поскольку REPEATABLE READ является первым чтением данных, будет создан ReadView, поэтому транзакция A снова выполнит оператор select и не будет снова создавать ReadView.Используется последний ReadView, поэтому Процесс суждения такой же, как и выше Да, так что имя, которое я прочитал, все еще «Подземный король».
Этот блог закончился здесь.