Помните одновременный «тупик» Mysql, проблемы и обсуждения

MySQL

В последние несколько дней, когда я просматривал статью, я обнаружил проблему параллелизма Mysql.В начале, когда я просто смотрел на это глазами, я не нашел проблему и решение, поэтому мы выполнили конкретные практические операции и тесты на нем:

1. Проблемы

В транзакции: после вставки записи обновите запись в соответствии с полем p.Однако, когда есть параллельная операция, при обновлении возникнет проблема взаимоблокировки, и обновление будет изменено на id, и это будет отлично.

В той же таблице с высокой параллельной транзакцией сначала вставьте запись в транзакцию, а затем обновите эту запись: (1) Если обновление является уникальным индексом, существует исключение; (2) Если обновление является самоувеличивающимся первичным ключом, исключения нет; Голос за кадром: Не обманывайтесь описанием «тупиковой блокировки», будь то проблема взаимоблокировки, проблема блокировки или какая-то другая аномалия и так далее.

2. Тестовые задачи и воспроизведение

2.1 Подготовка данных

create table t (id int(20) primary key AUTO_INCREMENT,cell varchar(20) unique)engine=innodb;

Новая таблица: (1) механизм хранения — innodb, версия MySQL — 5.6; (2) поле id, самоувеличивающийся первичный ключ; (3) поле ячейки, уникальный индекс;

start transaction;
insert into t(cell) values(11111111111);
insert into t(cell) values(22222222222);
insert into t(cell) values(33333333333);
commit;

Вставьте некоторые тестовые данные.

2.2 настройки параметров сеанса

Установите уровень изоляции транзакций на RR (повторяемое чтение).

--设置手动提交
--设置事务隔离级别为RR
set session autocommit=0;
set session transaction isolation level repeatable read;

2.3 Моделирование параллелизма

Несколько сеансов терминала имитируют параллельные транзакции

start TRANSACTION;
INSERT INTO t(cell) VALUES(44444444);
UPDATE t set cell = 123 WHERE cell = 44444444 ;
ROLLBACK;
start TRANSACTION;
INSERT INTO t(cell) VALUES(5555555);
UPDATE t set cell= 456 WHERE cell = 5555555 ;
ROLLBACK;

Откройте два окна в Navicat

  1. Окно A, сначала запустите транзакцию и вставьте запись;
  2. Окно B, снова запустите транзакцию и вставьте запись;
  3. Окно A, измените вставленную запись;
  4. Окно B, также измените вставленную запись;

2.4 Результаты

Странно появилось!

  • При запуске обновления транзакции 1 произошло ожидание!
  • При переходе к обновлению транзакции 2 возникла взаимоблокировка, и был автоматически выполнен откат.

3. Запрос

Логично, вставить неконфликтующую запись, а потом изменить эту запись, блокировка строки не должна конфликтовать?
Как может быть разница между уникальным индексом и индексом первичного ключа? Актуально ли это? Это тупик или что-то другое?

3.1 Запрос в соответствии с состоянием show engine innodb

Если вы не можете понять это, давайте сначала посмотрим, что находится в статусе innodb, скопируйте и вставьте его и проверьте:

Видно, что Transaction1 и Transaction2 одновременно блокируют одну и ту же часть, и это locate_mode X rec bur not gap Блокировка записи

Это очень странно.Это не тупик, вызванный гэп-локом.Почему первое обновление ждет, а второе зависает?

не знаю, поищите в другом месте

3.2 Просмотр таблицы innodb_locks

Взглянув на таблицу inndb_locks в библиотеке information_schema, вы увидите, что транзакция 1 и транзакция 2 действительно заблокированы в области данных одновременно, что приводит к ожиданию данных и взаимоблокировке, но в чем причина?

Поэтому снова измените метод, чтобы увидеть:

3.3 SQL-оператор объяснения/описания

Какие! Обнаружены основные проблемы
Почему рядов здесь 6!
Почему мое обновление сканирует всю таблицу? ?
я добавил индекс

Нашел проблему:обновление не пошло на индекс, а зачистило всю таблицу!

В-четвертых, решить проблему

Теперь, когда проблема найдена, давайте посмотрим, как ее решить.Почему обновление не идет в индекс? Затем вернемся и посмотрим на два оператора обновления.

UPDATE t set cell = 123 WHERE cell = 44444444 ;
UPDATE t set cell= 456 WHERE cell = 5555555 ;

Выглядит нормально?

Я устал искать, покинутый и несчастный, наконец, когда я проверил часы, я нашел проблему:

Оглянитесь назад на оператор создания таблицы/структуру таблицы.

create table t (id int(20) primary key AUTO_INCREMENT,cell varchar(20) unique)engine=innodb;

Тип данных поля ячейки имеет тип varchar, и наше обновление записывается как cell = 444444;

Данные не указаны! что привело кобновление не пошло на индекс, а просканировало всю таблицу

Итак, давайте рассмотрим процесс с самого начала:

Когда уровень изоляции транзакций — RR (Repeat Read)
Вставка транзакции 1 генерирует блокировку намерения вставки, а вставка транзакции 2 также генерирует блокировку намерения вставки (не будут блокироваться друг другом, поскольку строки данных не конфликтуют).
В это время транзакция 1 снова выполняет оператор обновления. Поскольку индекс не используется, сканируется вся таблица. Когда сканируются данные, вставленные транзакцией 2, блокировка строки конфликтует с блокировкой намерения вставки, поэтому транзакция 1 должна подождите, пока транзакция 2 освободит намерение вставки, заблокируйте и подождите.
Когда транзакция 2 обновляется, ей также необходимо сканировать всю таблицу, но вся таблица заблокирована обновлением транзакции 1. Транзакция 2 должна дождаться, пока транзакция 2 освободит блокировку строки транзакции 1, которая вставила блокировку намерения, поэтому возникает взаимоблокировка.

Решение очень простое, измените оператор на:

UPDATE t set cell = "123" WHERE cell = "44444444" ;
UPDATE t set cell= "456" WHERE cell = "5555555" ;

Может решить проблему взаимоблокировки/ожидания

В-пятых, проблема расширения

5.1 Сравнение RC и RR

5.1.1 Тесты с историческими данными в таблице

На самом деле, во время теста я также задавался вопросом, не связано ли это с проблемой RR, так почему бы не изменить его на RC?

--将事务隔离级别改为RC
SET TRANSACTION ISOLATION LEVEL REPEATABLE COMMITTED;

После модификации проделайте с ним то же самое:

Находить:Вставка транзакции 1, вставка транзакции 2, обновление транзакции 1 вступает в силу, а обновление транзакции 2 ожидает

В соответствии с проблемами, которые мы обнаружили выше, проанализируйте его:

  • Когда транзакция 1 обновляется, она также сканирует всю таблицу, но поскольку RC не имеет блокировок пробелов и не вставляет блокировок намерения, обновление транзакции 1 не будет ждать.
  • Когда транзакция 2 обновляется, ей нужно дождаться фиксации обновления транзакции 1, чтобы снять блокировку, поэтому происходит ожидание.

пришел к выводу:

Там нет блокировки пробелов под RC

5.1.2 Тесты, не содержащие в таблице исторических данных (таблица пуста)

Для сравнения между RC и RR мы продолжили тестировать данные в таблице, удалив данные:

truncate table 

Продолжаем делать то же самое с таблицей, результат:

  • Транзакция 1 все еще ожидает под RR, а транзакция 2 заблокирована.
  • При RC транзакция 1 и транзакция 2 являются нормальными, и ожидание не происходит.

Причина в том, что при RR данные, вставленные транзакцией 1, видны транзакции 2. Следовательно, при RR, даже если данные очищаются, транзакция 1 по-прежнему блокирует данные, вставленные транзакцией 2.
При RC данные, вставленные транзакцией 1, не могут быть видны для транзакции 2, а данные, вставленные транзакцией 2, не могут быть видны для транзакции 1. Они блокируют только данные, вставленные сами по себе, поэтому они могут успешно выполняться.

5.1.3 Заключение

Помимо повторяющихся чтений и зафиксированных чтений, они читали что-то, что может быть прочитано несколько раз в одной и той же транзакции, на этот раз нашли свои другие отличия:

  1. В RC нет блокировки промежутка, а также нет блокировки намерения вставки, принадлежащей замку промежутка.
  2. В RR транзакция данных 2, вставленная транзакцией 1, может быть видна, но транзакция данных 2, вставленная транзакцией 1 в RC, не видна.

5.2 Блокировка промежутка и блокировка вставки намерения в Mysql

В этом процессе мы обнаружили: блокировки строк, блокировки промежутков, блокировки намерения вставки. Среди них разные блокировки генерируются из-за разного поведения, и их значения и использование также различаются:

5.2.1 Гэп-замки

  1. Блокировка диапазона, блокирует только диапазон индекса (открытый диапазон, исключая двусторонние конечные точки).
  2. Блокировка промежутка между записями индекса, до или после записи индекса не включает саму запись индекса. Например, в 1, 2 и 3 возможные значения блокировки гэпа равны (∞, 1), (1, 2), (2, ∞).
  3. Гэп-блокировки можно использовать для предотвращения фантомного чтения и гарантии того, что данные не будут вставлены между индексами.

5.2.2 Вставка замков намерения

  1. Блокировка намерения вставки — это блокировка Gap, а не блокировка намерения, которая генерируется во время операции вставки.
  2. Когда несколько транзакций записываются в разные данные в один и тот же интервал индекса, нет необходимости ждать завершения других транзакций и ожидания блокировки.
  3. Предположим, что есть индекс записи, содержащий значения ключа 4 и 7, и разные транзакции вставляют соответственно 5 и 6. Каждая транзакция будет генерировать блокировку намерения вставки между 4 и 7, чтобы получить эксклюзивную блокировку на вставленную строку, но не будет заблокированы друг с другом, потому что строки данных не конфликтуют.
  4. Блокировки намерения вставки не блокируют никакие блокировки, для вставленной записи удерживается блокировка записи.

5.2.3 Выбор блокировки

Когда мы обрабатываем операторы SQL для выполнения, разные операторы будут выбирать разные блокировки:

  • Если условие обновления не проходит через индекс, например, выполнение «update test set name="hello" where name="world";", в это время будет выполнено полное сканирование таблицы. При сканировании таблицы любые другие операции обновления должны быть заблокированы, поэтому увеличение блокировки таблицы.

  • Если условием обновления является поле индекса, но это не уникальный индекс (включая индекс первичного ключа), например, выполните «update test set name="hello" where code=9;", тогда обновление будет использовать Next -Key Lock в это время. Причины использования Next-Key Lock:

  1. Прежде всего, необходимо убедиться, что эксклюзивный замок добавляется к приемлемому записям, что заблокирует значение текущего неуникального индекса и соответствующий индекс первичного ключа;

  2. Также убедитесь, что заблокированный интервал не может вставлять новые данные.

  3. Если условием обновления является уникальный индекс, используется блокировка записи.