Метка: статья в публичном аккаунте "Мы все маленькие лягушки"
а
Для плавного развития истории нам необходимо создать таблицу:
CREATE TABLE t (
id INT PRIMARY KEY,
c VARCHAR(100)
) Engine=InnoDB CHARSET=utf8;
Затем вставьте часть данных в эту таблицу:
INSERT INTO t VALUES(1, '刘备');
Данные в таблице теперь выглядят так:
mysql> SELECT * FROM t;
+----+--------+
| id | c |
+----+--------+
| 1 | 刘备 |
+----+--------+
1 row in set (0.01 sec)
уровень изоляции
MySQL
Это программное обеспечение с архитектурой сервер/клиент.К одному и тому же серверу может быть подключено несколько клиентов.После того, как каждый клиент подключится к серверу, это можно назвать сеансом (Session
). Мы можем одновременно вводить различные операторы в разных сеансах, и эти операторы могут обрабатываться как часть транзакции. Разные сеансы могут отправлять запросы одновременно, а это означает, что сервер может одновременно обрабатывать несколько транзакций, что приведет к тому, что разные транзакции будут обращаться к одной и той же записи в одно и то же время. Ранее мы говорили, что у транзакций есть функция, называемая隔离性
, теоретически, когда транзакция обращается к определенным данным, другие транзакции должны быть поставлены в очередь, и после того, как транзакция зафиксирована, другие транзакции могут продолжать получать доступ к данным. Но это слишком сильно влияет на производительность, поэтому дядя, разработавший базу данных, предложил различные隔离级别
, чтобы максимизировать способность системы одновременно обрабатывать транзакции, но это также за счет определенных隔离性
достигать.
ЧИТАТЬ БЕЗ ЗАЯВЛЕНИЙ
еслиТранзакция прочитала данные, измененные другой незафиксированной транзакцией., то это隔离级别
просто позвони未提交读
(Английское имя:READ UNCOMMITTED
), схема выглядит следующим образом:
Как показано выше,Session A
иSession B
Каждый открыл транзакцию,Session B
лидеры бизнеса вid
за1
столбец записейc
обновить до'关羽'
,ПотомSession A
В транзакции перейдите к запросу этогоid
за1
записи, затем в未提交读
На уровне изоляции результат запроса'关羽'
, то есть транзакция прочитала запись, измененную другой незафиксированной транзакцией. но еслиSession B
Транзакция откатывается позже, затемSession A
Транзакция эквивалентна чтению несуществующих данных, это явление называется脏读
, нравится:
脏读
идет вразрез с последствиями для бизнеса в реальном мире, так что этоREAD UNCOMMITTED
это очень небезопасно隔离级别
.
ПРОЧИТАТЬ СОВЕРШЕНО
еслиТранзакция может только читать данные, измененные другой транзакцией, которая была зафиксирована, и каждый раз, когда другие транзакции изменяют и фиксируют данные, транзакция может запрашивать последнее значение., то это隔离级别
просто позвони已提交读
(Английское имя:READ COMMITTED
), как показано на рисунке:
Как видно из рисунка, на шаге 4 за счетSession B
Транзакция не была совершена, поэтомуSession A
Результат запроса транзакции в'刘备'
, а на шаге 6, посколькуSession B
Транзакция уже зафиксирована, поэтомуSession B
Результат запроса транзакции в'关羽'
.
где-то已提交读
Для транзакций на уровне изоляции, пока другие транзакции изменяют значение определенных данных, а затем отправляют их, транзакция будет считывать последнее значение данных, например:
мы вSession B
совершает несколько неявных транзакций вid
за1
Значение столбца c записи после каждой фиксации транзакции,Session A
Все транзакции в могут просматривать последнее значение. Это явление также называют不可重复读
.
ПОВТОРЯЕМОЕ ЧТЕНИЕ
В некоторых бизнес-сценарияхТранзакция может только читать данные, измененные другой зафиксированной транзакцией, но после первого чтения записи, даже если другие транзакции изменяют значение записи и фиксируют, когда транзакция читает запись позже, значение по-прежнему читается первым время, вместо того, чтобы каждый раз читать разные данные. тогда это隔离级别
просто позвони可重复读
(Английское имя:REPEATABLE READ
), как показано на рисунке:
Как видно из рисунка,Session A
Транзакция в первом чтенииid
за1
записей, столбецc
значение'刘备'
, хотя послеSession B
неявно фиксирует несколько транзакций, каждая из которых изменяет эту запись, ноSession A
Столбцы, прочитанные транзакциями вc
Значение по-прежнему'刘备'
, что является тем же значением, что и первое чтение.
Сериализация (SERIALIZABLE)
Все три вышеуказанных уровня изоляции допускают одну и ту же запись.读-读
,读-写
,写-读
параллельных операций, если мы не допускаем读-写
,写-读
для параллельных операций вы можете использоватьSERIALIZABLE
Уровень изоляции, принципиальная схема выглядит следующим образом:
Как показано на рисунке, когдаSession B
Транзакции в были обновленыid
за1
после записи, послеSession A
Когда сделка вSession B
После транзакции в коммитахSession A
Только транзакции в результате запроса могут быть получены.
цепочка версий
Для использованияInnoDB
Для таблицы подсистемы хранения ее записи кластеризованного индекса содержат два необходимых скрытых столбца (row_id
Это не обязательно.Когда таблица, которую мы создаем, имеет первичный ключ или уникальный ключ, отличный от NULL, она не будет содержатьrow_id
Список):
-
trx_id
: каждый раз, когда изменяется запись кластеризованного индекса, соответствующий идентификатор транзакции будет назначен дляtrx_id
Скрыть столбцы. -
roll_pointer
: при каждом изменении записи кластеризованного индекса старая версия будет записываться вundo日志
, то этот скрытый столбец эквивалентен указателю, по которому можно найти информацию до модификации записи.
Скажем, наш столt
Теперь содержит только одну запись:
mysql> SELECT * FROM t;
+----+--------+
| id | c |
+----+--------+
| 1 | 刘备 |
+----+--------+
1 row in set (0.01 sec)
Предположим, что идентификатор транзакции, которая вставила запись,80
, то принципиальная схема записи в этот момент такова:
Предположим, следующие дваid
соответственно100
,200
транзакция по этой записиUPDATE
Эксплуатация, процесс работы выглядит следующим образом:
Советы: Можно ли перекрестно обновить одну и ту же запись в двух транзакциях? Ха-ха, это не разрешено. После того, как первая транзакция обновит определенную запись, она заблокирует запись. Когда другая транзакция снова обновится, ей нужно дождаться фиксации первой транзакции, а затем блокировку можно снять. Продолжайте обновлять . Эта статья не о блокировках, подробнее о блокировках будет рассказано позже.
Каждый раз, когда в запись вносятся изменения, будет сделана записьundo日志
, каждыйundo日志
также есть одинroll_pointer
Атрибуты(INSERT
операция, соответствующаяundo日志
не имеет этого атрибута, так как запись не имеет более ранней версии), они могут бытьundo日志
Все они соединены в связанный список, поэтому текущая ситуация похожа на следующий рисунок:
После каждого обновления записи старое значение будет помещаться вundo日志
, даже если это старая версия записи, по мере увеличения количества обновлений все версии будутroll_pointer
Атрибуты связаны в связанный список, мы называем этот связанный список版本链
, головной узел цепочки версий — это последнее значение текущей записи. Кроме того, каждая версия также содержит соответствующий идентификатор транзакции при создании версии.Эта информация очень важна, и мы будем использовать ее позже.
ReadView
Для использованияREAD UNCOMMITTED
Для транзакций на уровне изоляции достаточно напрямую прочитать последнюю версию записи.SERIALIZABLE
Для транзакций уровня изоляции блокировки используются для доступа к записям. Для использованияREAD COMMITTED
иREPEATABLE READ
Для транзакций уровня изоляции нам нужно использовать то, что мы сказали выше.版本链
Теперь главный вопрос:Необходимо определить, какая версия в цепочке версий видна для текущей транзакции.. так дизайнInnoDB
Дядя предложилReadView
концепция, этоReadView
В основном он включает в себя, какие активные транзакции чтения и записи все еще находятся в текущей системе, и помещает их идентификаторы транзакций в список, мы называем этот список какm_ids
. Таким образом, при доступе к записи вам нужно только выполнить следующие шаги, чтобы определить, видна ли версия записи:
-
Если доступная версия
trx_id
стоимость имущества меньшеm_ids
Наименьший идентификатор транзакции в списке, указывающий, что транзакция, сгенерировавшая эту версию, генерируетReadView
была зафиксирована ранее, поэтому эта версия может быть доступна для текущей транзакции. -
Если доступная версия
trx_id
стоимость имущества больше, чемm_ids
Самый большой идентификатор транзакции в списке, указывающий, что транзакция, сгенерировавшая эту версию, генерируетReadView
создается позже, поэтому текущая транзакция не может получить доступ к этой версии. -
Если доступная версия
trx_id
стоимость недвижимости вm_ids
Между наибольшим идентификатором транзакции и наименьшим идентификатором транзакции в списке вам нужно судитьtrx_id
Значение атрибута вm_ids
список, если есть, описание для созданияReadView
Когда транзакция, сгенерировавшая версию, все еще активна, доступ к версии невозможен; если нет, значит, версия созданаReadView
Когда транзакция, создавшая версию, зафиксирована, к версии можно получить доступ.
Если данные определенной версии не видны для текущей транзакции, то следуйте по цепочке версий, чтобы найти данные следующей версии, продолжайте оценивать видимость в соответствии с вышеописанными шагами и так далее, пока не появится последняя версия в транзакциях. цепочка версий, если последняя версия Если версия также не видна, это означает, что запись не видна транзакции, и результат запроса не содержит записи.
существуетMySQL
середина,READ COMMITTED
иREPEATABLE READ
Очень большая разница между уровнями изоляции заключается в том, что они генерируютReadView
Сроки другие, давайте посмотрим.
READ COMMITTED --- генерировать ReadView перед каждым чтением данных
Например, есть дваid
соответственно100
,200
Транзакция выполняется:
# Transaction 100
BEGIN;
UPDATE t SET c = '关羽' WHERE id = 1;
UPDATE t SET c = '张飞' WHERE id = 1;
# Transaction 200
BEGIN;
# 更新了一些别的表的记录
...
Советы: Во время выполнения транзакции, только когда запись действительно изменяется в первый раз (например, с помощью операторов INSERT, DELETE, UPDATE), будет назначен отдельный идентификатор транзакции, и идентификатор транзакции будет увеличен.
В этот момент столt
серединаid
за1
Список версий, полученный из записей, выглядит следующим образом:
Предположим, теперь есть применениеREAD COMMITTED
Транзакция на уровне изоляции начинает выполняться:
# 使用READ COMMITTED隔离级别的事务
BEGIN;
# SELECT1:Transaction 100、200未提交
SELECT * FROM t WHERE id = 1; # 得到的列c的值为'刘备'
этоSELECT1
Процесс выполнения следующий:
-
в исполнении
SELECT
оператор сначала создастReadView
,ReadView
изm_ids
Содержание списка[100, 200]
. -
Затем выберите видимые записи из цепочки версий, как видно из рисунка, столбец для последней версии
c
Содержание'张飞'
, эта версияtrx_id
значение100
,существуетm_ids
список, поэтому не соответствует требованиям видимости, согласноroll_pointer
Перейти к следующей версии. -
следующая версия столбцов
c
Содержание'关羽'
, эта версияtrx_id
значение также100
, такжеm_ids
В списке, значит, не соответствует требованиям, продолжайте переходить к следующей версии. -
следующая версия столбцов
c
Содержание'刘备'
, эта версияtrx_id
значение80
, меньше, чемm_ids
наименьший идентификатор транзакции в списке100
, так что эта версия соответствует требованиям, и окончательная версия, возвращаемая пользователю, — это столбецc
за'刘备'
запись о.
После этого мы помещаем идентификатор транзакции как100
Транзакция фиксируется, например:
# Transaction 100
BEGIN;
UPDATE t SET c = '关羽' WHERE id = 1;
UPDATE t SET c = '张飞' WHERE id = 1;
COMMIT;
Затем перейдите к идентификатору транзакции как200
Обновить таблицу в транзакцииt
серединаid
Запись 1:
# Transaction 200
BEGIN;
# 更新了一些别的表的记录
...
UPDATE t SET c = '赵云' WHERE id = 1;
UPDATE t SET c = '诸葛亮' WHERE id = 1;
В этот момент столt
серединаid
за1
Цепочка версий записей выглядит так:
а затем просто используйтеREAD COMMITTED
В транзакции уровня изоляции продолжайте находить этот идентификатор как1
записи следующим образом:
# 使用READ COMMITTED隔离级别的事务
BEGIN;
# SELECT1:Transaction 100、200均未提交
SELECT * FROM t WHERE id = 1; # 得到的列c的值为'刘备'
# SELECT2:Transaction 100提交,Transaction 200未提交
SELECT * FROM t WHERE id = 1; # 得到的列c的值为'张飞'
этоSELECT2
Процесс выполнения следующий:
-
в исполнении
SELECT
оператор сначала создастReadView
,ReadView
изm_ids
Содержание списка[200]
(идентификатор транзакции100
эта транзакция уже зафиксирована, поэтому снимок был сделан без нее). -
Затем выберите видимые записи из цепочки версий, как видно из рисунка, столбец для последней версии
c
Содержание'诸葛亮'
, эта версияtrx_id
значение200
,существуетm_ids
список, поэтому не соответствует требованиям видимости, согласноroll_pointer
Перейти к следующей версии. -
следующая версия столбцов
c
Содержание'赵云'
, эта версияtrx_id
значение200
, такжеm_ids
В списке, значит, не соответствует требованиям, продолжайте переходить к следующей версии. -
следующая версия столбцов
c
Содержание'张飞'
, эта версияtrx_id
значение100
,Сравниватьm_ids
наименьший идентификатор транзакции в списке200
Он еще меньше, поэтому эта версия соответствует требованиям, а окончательная версия, возвращаемая пользователю, — это столбец.c
за'张飞'
запись о.
И так далее, если идентификатор транзакции200
Запись также отправлена и снова используется здесьREAD COMMITTED
Запрос таблицы в транзакции уровня изоляцииt
серединаid
значение1
, полученный результат'诸葛亮'
Сейчас мы не будем анализировать конкретный процесс. Подвести итог:Транзакции, использующие уровень изоляции READ COMMITTED, создают отдельный ReadView в начале каждого запроса..
REPEATABLE READ
--- Создать ReadView при первом чтении данных
Для использованияREPEATABLE READ
Для транзакций на уровне изоляции создается только один запрос при первом выполнении оператора запроса.ReadView
, и последующие запросы не будут генерироваться повторно. Давайте используем пример, чтобы увидеть, каков эффект.
Например, есть дваid
соответственно100
,200
Транзакция выполняется:
# Transaction 100
BEGIN;
UPDATE t SET c = '关羽' WHERE id = 1;
UPDATE t SET c = '张飞' WHERE id = 1;
# Transaction 200
BEGIN;
# 更新了一些别的表的记录
...
В этот момент столt
серединаid
за1
Список версий, полученный из записей, выглядит следующим образом:
Предположим, теперь есть применениеREPEATABLE READ
Транзакция на уровне изоляции начинает выполняться:
# 使用REPEATABLE READ隔离级别的事务
BEGIN;
# SELECT1:Transaction 100、200未提交
SELECT * FROM t WHERE id = 1; # 得到的列c的值为'刘备'
этоSELECT1
Процесс выполнения следующий:
-
в исполнении
SELECT
оператор сначала создастReadView
,ReadView
изm_ids
Содержание списка[100, 200]
. -
Затем выберите видимые записи из цепочки версий, как видно из рисунка, столбец для последней версии
c
Содержание'张飞'
, эта версияtrx_id
значение100
,существуетm_ids
список, поэтому не соответствует требованиям видимости, согласноroll_pointer
Перейти к следующей версии. -
следующая версия столбцов
c
Содержание'关羽'
, эта версияtrx_id
значение также100
, такжеm_ids
В списке, значит, не соответствует требованиям, продолжайте переходить к следующей версии. -
следующая версия столбцов
c
Содержание'刘备'
, эта версияtrx_id
значение80
, меньше, чемm_ids
наименьший идентификатор транзакции в списке100
, так что эта версия соответствует требованиям, и окончательная версия, возвращаемая пользователю, — это столбецc
за'刘备'
запись о.
После этого мы помещаем идентификатор транзакции как100
Транзакция фиксируется, например:
# Transaction 100
BEGIN;
UPDATE t SET c = '关羽' WHERE id = 1;
UPDATE t SET c = '张飞' WHERE id = 1;
COMMIT;
Затем перейдите к идентификатору транзакции как200
Обновить таблицу в транзакцииt
серединаid
Запись 1:
# Transaction 200
BEGIN;
# 更新了一些别的表的记录
...
UPDATE t SET c = '赵云' WHERE id = 1;
UPDATE t SET c = '诸葛亮' WHERE id = 1;
В этот момент столt
серединаid
за1
Цепочка версий записей выглядит так:
а затем просто используйтеREPEATABLE READ
В транзакции уровня изоляции продолжайте находить этот идентификатор как1
записи следующим образом:
# 使用REPEATABLE READ隔离级别的事务
BEGIN;
# SELECT1:Transaction 100、200均未提交
SELECT * FROM t WHERE id = 1; # 得到的列c的值为'刘备'
# SELECT2:Transaction 100提交,Transaction 200未提交
SELECT * FROM t WHERE id = 1; # 得到的列c的值仍为'刘备'
этоSELECT2
Процесс выполнения следующий:
-
потому что он был сгенерирован раньше
ReadView
, поэтому повторно используйте предыдущийReadView
,предыдущийReadView
серединаm_ids
список[100, 200]
. -
Затем выберите видимые записи из цепочки версий, как видно из рисунка, столбец для последней версии
c
Содержание'诸葛亮'
, эта версияtrx_id
значение200
,существуетm_ids
список, поэтому не соответствует требованиям видимости, согласноroll_pointer
Перейти к следующей версии. -
следующая версия столбцов
c
Содержание'赵云'
, эта версияtrx_id
значение200
, такжеm_ids
В списке, значит, не соответствует требованиям, продолжайте переходить к следующей версии. -
следующая версия столбцов
c
Содержание'张飞'
, эта версияtrx_id
значение100
,иm_ids
Список содержит значения как100
идентификатора транзакции, поэтому эта версия не соответствует требованиям, и то же самое верно для следующего столбцаc
Содержание'关羽'
версия также не соответствует требованиям. Продолжайте переходить к следующей версии. -
следующая версия столбцов
c
Содержание'刘备'
, эта версияtrx_id
значение80
,80
меньше, чемm_ids
наименьший идентификатор транзакции в списке100
, так что эта версия соответствует требованиям, и окончательная версия, возвращаемая пользователю, — это столбецc
за'刘备'
запись о.
то есть дваждыSELECT
Результаты, полученные запросом, повторяются, столбцы записейc
значения'刘备'
,Это可重复读
значение. Если мы затем поместим идентификатор транзакции как200
Запись отправлена, а затем использовать ее только сейчасREPEATABLE READ
В транзакции уровня изоляции продолжайте находить этот идентификатор как1
запись, результат еще'刘备'
, вы можете сами проанализировать конкретный процесс внедрения.
Резюме MVCC
Из вышеприведенного описания видно, что так называемый MVCC (Multi-Version Concurrency Control, многоверсионный контроль параллелизма) относится к использованиюREAD COMMITTD
,REPEATABLE READ
Транзакции этих двух уровней изоляции выполняются обычным образом.SEELCT
Процесс доступа к цепочке записей версий во время работы, чтобы можно было выполнять различные транзакции.读-写
,写-读
Операции выполняются одновременно, что повышает производительность системы.READ COMMITTD
,REPEATABLE READ
Большая разница между двумя уровнями изоляции заключается в том, что генерацияReadView
разные тайминги,READ COMMITTD
общее в каждомSELECT
Прежде чем операция создастReadView
,иREPEATABLE READ
Нормально только первый разSELECT
генерироватьReadView
, и последующие операции запроса повторяют этоReadView
Достаточно.
буклет
Если вы хотите увидеть более продвинутые знания MySQL, вы можете проверить это в буклете:Ссылка на статью «Как работает MySQL: понимание MySQL у истоков». Содержание буклета в основном с точки зрения Xiaobai, с использованием относительно популярного языка для объяснения некоторых основных концепций расширенного MySQL, таких как записи, индексы, страницы, табличные пространства, оптимизация запросов, транзакции и блокировки и т. д. количество слов составляет от 300 000 до 400 000 слов, с сотнями оригинальных иллюстраций. Основная цель состоит в том, чтобы облегчить обычным программистам изучение MySQL для продвинутых пользователей и сделать кривую обучения более плавной.