Когда дело доходит до транзакций, вы обязательно вспомните ACID (Atomicity, Consistency, Isolation, Durability, то есть атомарность, непротиворечивость, изолированность, долговечность).
Когда несколько транзакций выполняются в базе данных одновременно, могут возникнуть проблемы грязного чтения, неповторяемого чтения и фантомного чтения, поэтому давайте поговорим об уровне изоляции.
Уровни изоляции транзакций стандарта SQL включают в себя: незафиксированное чтение, зафиксированное чтение, повторяемое чтение и сериализуемое.
- Read uncommitted означает, что когда транзакция не зафиксирована, ее изменения могут быть видны другим транзакциям.
- Чтение фиксации означает, что после фиксации транзакции ее изменения будут видны другим транзакциям.
- Повторяющееся чтение означает, что данные, видимые во время выполнения транзакции, всегда совпадают с данными, видимыми транзакцией при ее запуске. Конечно, при уровне изоляции повторяющегося чтения незафиксированные изменения также невидимы для других транзакций.
- Сериализация, как следует из названия, предназначена для одной и той же строки записей: «запись» добавит «блокировку записи», «чтение» добавит «блокировку чтения». Когда возникает конфликт блокировки чтения-записи, транзакция, к которой был получен доступ позже, должна дождаться завершения выполнения предыдущей транзакции, прежде чем продолжить.
В MySQL поддерживается четыре уровня изоляции.
Хотя MySQL поддерживает четыре уровня изоляции, это несколько отличается от допустимых проблем на всех уровнях изоляции, указанных в стандарте SQL.При уровне изоляции REPEATABLE READ MySQL может запрещать возникновение фантомных проблем чтения.
Мы можем пройти:SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level;
установить уровень изоляции.
Есть 4 необязательных значения для уровня:
level: {
REPEATABLE READ
| READ COMMITTED
| READ UNCOMMITTED
| SERIALIZABLE
}
Принцип MVCC
Для таблицы, использующей механизм хранения InnoDB, ее записи кластеризованного индекса содержат необходимые скрытые столбцы:
- trx_id: каждый раз, когда транзакция изменяет запись кластеризованного индекса, идентификатор транзакции назначается скрытому столбцу trx_id.
ReadView
Проблема, которую решает ReadView, заключается в том, что в транзакциях, использующих уровни изоляции READ COMMITTED и REPEATABLE READ, незафиксированные записи не могут быть прочитаны, что требует оценки того, какая версия в цепочке версий видна для текущей транзакции.
ReadView в основном содержит еще 4 важных содержимого:
- 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 ReadView, необходимо судить о том, находится ли значение атрибута trx_id в списке m_ids. версия, сгенерированная при создании ReadView, зафиксирована, и к этой версии можно получить доступ.
Если данные определенной версии не видны для текущей транзакции, то следуйте по цепочке версий, чтобы найти данные следующей версии, продолжайте оценивать видимость в соответствии с указанными выше шагами и так далее, пока не появится последняя версия в транзакциях. цепочка версий. Если последняя версия также не видна, значит, запись полностью невидима для транзакции, и результат запроса не содержит записи.
Очень большая разница между уровнями изоляции READ COMMITTED и REPEATABLE READ в MySQL заключается в том, что ониКогда создавать ReadViewразные.
Мы используем пример здесь, чтобы объяснить:
mysql> CREATE TABLE `t` (
`id` int(11) NOT NULL,
`k` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1) ;
Транзакция А | Транзакция Б |
---|---|
begin | |
begin | |
update t set k= k+1 where id=1; | |
совершить; | |
update t set k = k+1 where id=1; | |
select k from t where id =1; | |
commit; |
В этом примере мы делаем следующие предположения:
- Номера версий транзакций A и B равны 100 и 200 соответственно, и в текущей системе есть только эти 3 транзакции;
- До запуска трех транзакций строка trx_id этой строки данных (1,1) была равна 90.
READ COMMITTED — генерировать ReadView перед каждым чтением данных
Продолжая приведенный выше пример, предположим, что теперь есть транзакция с запущенным уровнем изоляции READ COMMITTED:
# 使用READ COMMITTED隔离级别的事务
BEGIN;
# SELECT1:Transaction 100、200未提交
select k from t where id=1 ; # 得到值为1
Процесс выполнения этого SELECT1 выглядит следующим образом:
- При выполнении оператора SELECT сначала будет сгенерирован ReadView.Содержимое списка m_ids ReadView — [100, 200], min_trx_id — 100, max_trx_id — 201, а создатель_trx_id — 0.
- Затем выберите видимые записи из цепочки версий.Последняя версия имеет значение trx_id 200, которое находится в списке m_ids, поэтому она не соответствует требованиям видимости.
- Значение trx_id следующей версии тоже равно 100, что тоже есть в списке m_ids, поэтому не соответствует требованиям, и продолжает переходить к следующей версии.
- Значение trx_id следующей версии равно 90, что меньше значения min_trx_id, равного 100, в ReadView, поэтому эта версия соответствует требованиям.
После этого мы фиксируем транзакцию транзакции B, а затем продолжаем поиск в транзакции, которая только что использовала уровень изоляции READ COMMITTED, следующим образом:
# 使用READ COMMITTED隔离级别的事务
BEGIN;
# SELECT1:Transaction 100、200均未提交
SELECT * FROM hero WHERE number = 1; # 得到值为1
# SELECT2:Transaction 200提交,Transaction 100未提交
SELECT * FROM hero WHERE number = 1; # 得到值为2
Процесс выполнения этого SELECT2 выглядит следующим образом:
- При выполнении оператора SELECT будет сгенерирован отдельный ReadView.Содержимое списка m_ids ReadView равно [100] (транзакция с идентификатором транзакции 200 была зафиксирована, поэтому ее не будет в момент создания снимка снова), а min_trx_id равен 100, max_trx_id равен 201, Creator_trx_id равен 0.
- Затем выберите видимые записи из цепочки версий.Как видно из рисунка, значение trx_id последней версии равно 100, которая находится в списке m_ids, поэтому не соответствует требованиям видимости.
- Значение trx_id следующей версии равно 200, что меньше, чем max_trx_id, и его нет в списке m_ids, поэтому оно видимо, а возвращаемое значение равно 2.
ПОВТОРЯЕМОЕ ЧТЕНИЕ — генерировать ReadView при первом чтении данныхПредположим теперь, что транзакция с уровнем изоляции REPEATABLE READ начинает выполняться:
# 使用REPEATABLE READ隔离级别的事务
BEGIN;
# SELECT1:Transaction 100、200未提交
SELECT * FROM hero WHERE number = 1; # 得到值为1
Процесс выполнения этого SELECT1 выглядит следующим образом:
- При выполнении оператора SELECT сначала будет сгенерирован ReadView.Содержимое списка m_ids ReadView — [100, 200], min_trx_id — 100, max_trx_id — 201, а создатель_trx_id — 0.
- Затем выберите видимые записи из цепочки версий.Значение trx_id этой версии равно 100, которое находится в списке m_ids, поэтому оно не соответствует требованиям видимости.
- В следующей версии значение trx_id этой версии равно 200, что также находится в списке m_ids, поэтому не соответствует требованиям, и продолжает переходить к следующей версии.
- Значение trx_id следующей версии равно 90, что меньше значения min_trx_id, равного 100, в ReadView, поэтому эта версия соответствует требованиям.
После этого мы отправляем транзакцию транзакции B Затем перейдите к транзакции, которая только что использовала уровень изоляции REPEATABLE READ, чтобы продолжить поиск:
# 使用REPEATABLE READ隔离级别的事务
BEGIN;
# SELECT1:Transaction 100、200均未提交
SELECT * FROM hero WHERE number = 1; # 得到值为1
# SELECT2:Transaction 200提交,Transaction 100未提交
SELECT * FROM hero WHERE number = 1; # 得到值为1
Процесс выполнения этого SELECT2 выглядит следующим образом:
- Поскольку уровень изоляции текущей транзакции REPEATABLE READ, а ReadView был сгенерирован до выполнения SELECT1, в это время непосредственно повторно используется предыдущий ReadView.Содержимое списка m_ids предыдущего ReadView равно [100, 200] и min_trx_id равен 100, max_trx_id равен 201, Creator_trx_id равен 0.
- Затем выберите видимые записи из цепочки версий.Значение trx_id этой версии равно 100, которое находится в списке m_ids, поэтому оно не соответствует требованиям видимости.
- В следующей версии значение trx_id этой версии равно 200, что тоже есть в списке m_ids, поэтому не соответствует требованиям, и продолжает переходить к следующей версии.
- Значение trx_id следующей версии равно 90, что меньше значения min_trx_id, равного 100, в ReadView, поэтому эта версия соответствует требованиям.
Оригинальная ссылкаСтатья поможет вам освоить согласованное представление mysql (MVCC)