маленькие черные мысли
Эй, я недавно был немного занят, и я плохо готовлюсь к экзамену. Мне завтра нужно переезжать. Это так сложно! !
Я думал о голубях на этой неделе, но я все еще не могу думать об этом, встать и обновить его, и я не могу остановить неделю. Поленитесь, отредактируйте предыдущую историческую статью и переиздайте ее.
Сначала лайкните, затем смотрите, ищите «Program Communication» в WeChat, следите за ним, и все готово.
Авария P0: остаток вычтен!
Это реальное производственное событие со следующими причинами:
Существует существующая система транзакций, каждый раз, когда транзакция генерируется, баланс соответствующего счета обновляется, баланс вычитается из счета, а баланс увеличивается на счет.
В целях обеспечения сохранности средств при списании остатка необходимо сравнить имеющийся остаток с суммой списания.Если сумма списания больше существующего остатка, сальдо списания недостаточно и списание не выполнено.
Таблица счетов (пропустить другие поля) имеет следующую структуру:
CREATE TABLE `account`
(
`id` bigint(20) NOT NULL,
`balance` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_bin;
При вычете остатка порядок слов sql следующий:
ps: Смотрите порядок слов выше, есть ли маленький вопросительный знак? Почему один и тот же запрос так много раз?
Фактически, эти порядок слов SQL не находится в одном и том же методе, а некоторые методы извлекаются и используются повторно, поэтому некоторые из одних и тех же результатов запроса не могут быть переданы вниз, поэтому их необходимо снова запрашивать из базы данных.
Чтобы предотвратить одновременное обновление баланса, в момент t3 используется блокировка записи для блокировки записи строки. Если блокировка прошла успешно, другие потоки будут заблокированы до тех пор, пока транзакция предыдущего потока не будет зафиксирована, если они также выполняются до t3.
В t5 перейдите к следующему методу, снова получите баланс счета, а затем сравните баланс с вычитаемой суммой в методе Java.Если баланс достаточен, выполните операцию обновления в t7.
Кажется, что нет проблем с приведенным выше порядком слов в SQL, но на самом деле он такой же. Но вот предпосылка,Системная база данных Oracle.
Однако из приведенной выше структуры таблицы мы можем узнать, что на этот раз база данных переключена на MySQL, и никакой другой код и конфигурация системы не изменены (есть небольшие изменения в sql).
В этом случае баланс перевычитается параллельным выполнением, то есть фактический баланс заведомо меньше суммы вычета, но выполняется операция обновления баланса, и в итоге баланс становится отрицательным числом.
Давайте воспроизведем ситуацию параллелизма ниже, предположив, что есть две транзакции, выполняющие порядок слов, и порядок выполнения показан на рисунке.
Примечание. База данных использует MySQL,Уровень изоляции транзакций по умолчанию — RR.. Запись в базе данных имеет вид id=1 balance=1000, при условии, что в данный момент выполняются только эти две транзакции.
Читатели могут сначала подумать о балансе в t2, t3, t4, t5, t6, t11.
Вставьте уровень изоляции транзакции нижеRRответ ниже.
Результат запроса транзакции 1:
- t2 (1,1000)
- t4 (1,1000)
- t6 (1,1000)
Результат запроса для транзакции 2:
- t3 (1,1000)
- t5 (1,900)
- t11 (1,1000)
Есть ли такой же результат, как вы ожидали?
Затем измените уровень изоляции транзакции наRC, а так же подумайте о балансе в t2, t3, t4, t5, t6, t11.
Вставьте уровень изоляции транзакции еще разRCответ ниже.
Результат запроса транзакции 1:
- t2 (1,1000)
- t4 (1,1000)
- t6 (1,1000)
Результат запроса для транзакции 2:
- t3 (1,1000)
- t5 (1,900)
- t11 (1,900)
Не должно быть проблем с результатом запроса транзакции 1. Основной вопрос должен быть транзакцией 2. Почему результат отличается после изменения уровня изоляции транзакции?
Давайте сначала рассмотрим соответствующие принципы MySQL с вопросами, и вы все это поймете после прочтения.
- MVCC
- Просмотр согласованности
- Чтение моментального снимка и текущее чтение
MVCC
Сначала рассмотрим простой пример,
Уровень изоляции транзакции — RR ,id=1 balance=1000
Транзакция 1 обновляет баланс записи id=1 до 900, а затем транзакция 2 запрашивает результат записи строки в момент времени t 5. Очевидно, запись строки должна бытьid=1 balance=1000.
Если t5 запрашивает последний результат id=1 balance=900, он считывает незафиксированные данные транзакции 1, что явно несовместимо.Текущий уровень изоляции транзакции.
Из приведенного выше примера видно, что существует две версии записи с id=1, и версия транзакции 1 записывается какbalance=1000, версия транзакции 2 записывается какbalance=900.
Для вышеуказанных функций MySQL использует механизм MVCC для реализации функций.
MVCC: многоверсионный контроль параллелизма, многоверсионный контроль параллелизма. Выдержка из ежемесячного отчета базы данных Taobao:
Многоверсионность: относится к методу, улучшающему параллелизм. В самой ранней системе баз данных одновременно могут быть только чтение и чтение, а чтение и запись, запись и чтение, запись и запись должны быть заблокированы. После введения нескольких версий только записи блокируют друг друга, а остальные три операции могут выполняться параллельно, что значительно улучшает параллелизм InnoDB. Во внутренней реализации, в отличие от Postgres, которая реализует несколько версий строк данных, InnoDB реализуется в андологе, и исторические версии данных могут быть получены через андолог. Извлеченная историческая версия данных может быть предоставлена пользователям для чтения (в соответствии с определением уровня изоляции некоторые запросы на чтение могут видеть только более старые версии данных), либо данные на странице данных могут быть перезаписаны при откате. Внутри InnoDB записывается глобальный массив активных транзакций чтения и записи, который в основном используется для определения видимости транзакций.
Видно, что MVCC в основном используется для улучшения параллелизма, а также может использоваться для чтения данных старой версии.
Прежде чем изучать принцип MVCC, сначала нам нужно понять структуру записи MySQL.
Как показано на фиг.accountРяд записей в таблице, помимо реальных данных, будет иметь три скрытых поля для записи дополнительной информации.
-
DB_TRX_ID: идентификатор транзакции.
-
DB_ROLL_PTR: указатель отката, указывающий на отмену регистрации.
-
ROW_ID: идентификатор строки, неактуальный на данный момент.
Каждая транзакция в MySQL InnoDB будет иметь уникальный идентификатор транзакции, который будет запрашиваться системой транзакций InnoDB в начале транзакции, и он будет увеличиваться строго по порядку.
Каждый раз, когда транзакция обновляет данные, будет генерироваться новая версия данных, а затем идентификатор текущей транзакции будет присвоен текущей записи.DB_TRX_ID. И запись обновления данных (1,1000---->1,900) будет записана в журнал отмены (журнал отката), а затем использовать записанный в данный моментDB_ROLL_PTRУкажите на und olog.
Таким образом, MySQL может найти отмену журнала через DB_ROLL_PTR и вывести содержимое записи предыдущей версии.
Процесс поиска выглядит следующим образом:
Если вам нужно узнать запись версии V1, сначала в соответствии с текущей версией V3DB_ROLL_PTRоказатьсяundolog, то согласноundologсодержание, вычислить предыдущую версию V2. И так далее, и, наконец, найдите запись версии V1.
V1, V2 не являются физическими записями, не имеют реального существования, а имеют только логическое значение.
Может быть несколько версий строки записей данных, но не все записи видны для текущей транзакции. В противном случае последние данные могут быть запрошены в момент t5, указанный выше. Поэтому при поиске версии данных MySQL должен определить версию данных.видна текущая транзакция.
Просмотр согласованности
MySQL создает согласованное представление после начала транзакции (не сразу установил), в этом представлении все активные транзакции сохраняются (незавершенные транзакции).
Предположим, что текущая транзакция сохраняет активный массив транзакций, как показано ниже.
При оценке того, видна ли версия для текущей транзакции, она оценивается на основе следующих правил:
-
Если идентификатор транзакции версии меньше минимального значения текущего активного массива идентификаторов транзакций, например, идентификатор версии равен 40, что меньше минимального значения активного массива 45. Это означает, что текущая версия транзакции зафиксирована и текущая версия видна текущей транзакции.
-
Если идентификатор транзакции версии больше, чем максимальное значение текущего активного массива транзакций, например, идентификатор транзакции версии равен 100, что больше, чем максимальный идентификатор транзакции массива, 90. Он показывает, что эта версия создается после создания текущей транзакции, поэтому эта версия не видна для текущей транзакции.
-
Например, если идентификатор транзакции версии является одной из активных в данный момент транзакций массива, идентификатор транзакции версии равен 56. Указывает, что транзакция, которой принадлежит версия записи, не была зафиксирована, поэтому версия не видна для текущей транзакции.
-
Если идентификатор транзакции версии не является одной из текущих активных транзакций массива, но идентификатор транзакции находится в одном из минимального и максимального значений активного массива, например идентификатор транзакции 57. Указывает, что текущая транзакция записи зафиксирована, поэтому эта версия видна для текущей транзакции.
-
Если идентификатор транзакции версии является идентификатором текущей транзакции, это означает, что данные строки изменены текущей транзакцией, и, конечно, он должен быть видимым.
4 Это правило может сбивать с толку, и его легче понять на картинке выше.
Приведенные выше правила суждения могут быть более абстрактными. Если вы их не понимаете, ничего страшного. Объясним на простом языке:
-
Версия записи, сгенерированная незафиксированной транзакцией, не отображается.
-
Версия записи создания зафиксированной транзакции видна до создания представления.
-
После создания представления новая версия записи создания транзакции не отображается.
-
Обновления самостоятельных транзакций всегда видны.
Представление согласованности будет создано только для RR и RC. Для RR представление согласованности будет создано вПервый оператор запросапри генерации. Для RC представление создается повторно для каждого оператора запроса.
Текущее чтение и чтение моментального снимка
MySQL использует механизм MVCC для чтения данных из предыдущих версий. Эти записи старой версии не являются и не могут быть изменены, как и моментальные снимки. Поэтому мы называем такой запросСнимок прочитан.
Конечно, не все запросы являются чтением моментальных снимков.Заблокированные запросы, такие как select .... for update/ в режиме общего доступа, будут запрашивать только последнюю версию текущей записи. Мы называем этот запрос текущим чтением.
анализ проблемы
Поговорив о принципе, вернемся назад и проанализируем причины приведенных выше результатов запроса.
Здесь мы снова вставим приведенный выше ответ.
Уровень изоляции транзакций — RR, и две транзакции в t2 и t3 устанавливают представления согласованности соответственно благодаря оператору запроса.
В момент времени t4, поскольку транзакция 1 используетselect.. for update
Заблокируйте строку с id=1 и получите последний результат. В момент t5, поскольку строка заблокирована, транзакция 2 должна дождаться, пока транзакция 1 снимет блокировку, прежде чем продолжить.
В момент t6, согласно представлению согласованности, версия, представленная другими транзакциями, не может быть прочитана, поэтому данные не изменились. Баланс вычитается на 100 в t8, и транзакция фиксируется в t9.
Последняя версия на данный момент записана какid=1 balance=900.
Поскольку транзакция транзакции 1 зафиксирована, блокировка строки снимается, и t5 успешно получает блокировку. Поскольку t5 — это текущее чтение, результатом запроса является последняя версия данных (1900).
вот и дело, данные последней версии текущей записи(1900), но идентификатор транзакции последней версии, которая является незафиксированной транзакцией после создания транзакции 2, находится в массиве активных транзакций. Таким образом, последняя версия записи не видна для транзакции 2.
Нет возможности прочитать предыдущую версию записи только по undolog(1,1000), эта запись версии видна только транзакции 2, поэтому запись для t11(1,1000).
и когда мы будемИзмените уровень изоляции транзакций на RC, согласованное представление регенерируется каждый раз. Таким образом, представление согласованности регенерируется в момент времени 11, когда транзакция 1 была зафиксирована, а текущая последняя версия записи видна для транзакции 2, поэтому результат t11 станет(1900).
Суммировать
Уровень изоляции транзакций MySQL по умолчанию — RR.Каждая строка данных (InnoDB) может иметь несколько версий, и каждая версия имеет уникальный идентификатор транзакции.
MySQL обеспечивает видимость версий данных через согласованные представления.Соответствующие правила резюмируются следующим образом:
- Для уровня изоляции транзакции RR обычные запросы могут найти только те данные о версии, которые были отправлены до запуска транзакции.
- Для уровня изоляции транзакций RC обычные запросы могут найти данные версии, которые были отправлены до запуска оператора запроса.
- Текущее чтение всегда считывает последнюю версию данных.
справочная документация
1: Dev.MySQL.com/doc/Furious/…
2 MySQL.taobao.org/monthly/201…
3 MySQL.taobao.org/monthly/201…
4 Dev.MySQL.com/doc/Furious/…
5 Geek Time — столбец MySQL — изолирована ли транзакция или нет?
Добро пожаловать, чтобы обратить внимание на мой официальный аккаунт: программа для общения, ежедневный толчок галантерейных товаров. Если вам интересен мой рекомендуемый контент, вы также можете подписаться на мой блог:studyidea.cn