Анализ ошибок | Анализ логики исходного кода вставки блокировки из взаимоблокировки вставки параллелизма

Автор: Ли Сичао

Инженер базы данных Jiangsu Suning Bank, который любит посмеяться, в основном отвечает за повседневную работу и обслуживание базы данных, построение автоматизации, а также эксплуатацию и обслуживание платформы DMP. Хорошо разбираюсь в MySQL, Python, Oracle, люблю езду на велосипеде и исследовательские технологии.

Источник этой статьи: оригинальный вклад

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


Введение

Тупик, как распространенная проблема параллелизма в базах данных. Такие вопросы:

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

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

3. После того, как в базе данных возникнет взаимоблокировка, некоторые транзакции будут немедленно завершены, и состояние ожидания до взаимоблокировки впоследствии не будет видно.

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

Основываясь на трудностях решения проблемы взаимоблокировки, в этой статье в качестве примера рассматривается взаимоблокировка, вызванная параллельной вставкой в ​​базу данных MySQL.Из четырех шагов: поиск проблемы, воспроизведение проблемы, анализ основной причины и решение проблемы. , ожидается, что он предоставит набор взаимоблокировок. Научное и эффективное решение для читателей и друзей, на которое можно ссылаться.

2. Проблемное явление

Во время стресс-теста системы перед запуском было обнаружено, что в журнале приложений есть следующие подсказки журнала, вызывающие проблему взаимоблокировки:

Deadlock found when trying to get lock; try restarting transaction

К счастью, во время стресс-теста проблема была обнаружена, чтобы не влиять на производство после выхода в онлайн.

Затем выполните show engine innodb status, следующее содержимое (после десенсибилизации):

------------------------
LATEST DETECTED DEADLOCK
------------------------
2023-03-24 19:07:50 140736694093568
*** (1) TRANSACTION:
TRANSACTION 56118, ACTIVE 6 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1192, 1 row lock(s), undo log entries 1
MySQL thread id 9, OS thread handle 140736685700864, query id 57 localhost root update
insert into dl_tab(id,name) values(30,10)

*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 11 page no 5 n bits 72 index ua of table `testdb`.`dl_tab` trx id 56118 lock mode S waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;  # 十进制: 10
 1: len 4; hex 8000001a; asc     ;;  # 十进制: 26


*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 11 page no 5 n bits 72 index ua of table `testdb`.`dl_tab` trx id 56118 lock mode S waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;  # 十进制: 10
 1: len 4; hex 8000001a; asc     ;;  # 十进制: 26


*** (2) TRANSACTION:
TRANSACTION 56113, ACTIVE 12 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1192, 2 row lock(s), undo log entries 2
MySQL thread id 8, OS thread handle 140736952903424, query id 58 localhost root update
insert into dl_tab(id,name) values(40,8)

*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 11 page no 5 n bits 72 index ua of table `testdb`.`dl_tab` trx id 56113 lock_mode X locks rec but not gap
Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;; # 十进制: 10
 1: len 4; hex 8000001a; asc     ;; # 十进制: 26


*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 11 page no 5 n bits 72 index ua of table `testdb`.`dl_tab` trx id 56113 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;; # 十进制: 10
 1: len 4; hex 8000001a; asc     ;; # 十进制: 26

*** WE ROLL BACK TRANSACTION (1)
------------

1. Прочесывание тупиковой информации

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

Версия: 8.0.27

Уровень изоляции: Read-Commited

Структура таблицы:

*************************** 1. row ***************************
       Table: dl_tab
Create Table: CREATE TABLE `dl_tab` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` int NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ua` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

Обратите внимание, что приведенный выше вывод состояния innodb зависит от версии базы данных. Есть:

О. В MySQL 8.0.18 и более поздних версиях выходные данные включают блокировки, удерживаемые двумя транзакциями, и блокировки, которых они ожидают, что очень полезно для анализа проблем взаимоблокировки. До версии 8.0.18 включались только блокировки, ожидающие транзакции 1, блокировки, удерживаемые транзакцией 2, и ожидающие блокировки, но информация о блокировках, удерживаемая транзакцией 1, не включалась;

B. Приведенный выше пример также включает определенные значения записи индекса (например, {10,26}: первое поле — это значение записи индекса, а второе поле — соответствующая запись первичного ключа). Если нет значения индексной записи, может быть только куча №. Этот номер очень важен как внутренний механизм блокировки, но он не может быть связан с конкретной индексной записью. Кроме того, я нашел несколько других версий MySQL и обнаружил, что исходная версия ≥ 5.7.21 и ≥ 8.0 имеет эту функцию, но Percona mysql 5.7.21 не имеет этой функции;

C. Блокировка, которую ожидает транзакция T1, является LOCK S из выходных результатов, но на самом деле полученная блокировка является блокировкой следующего ключа, которая будет объяснена позже в результатах анализа исходного кода.

Сортировка журнала состояния Innodb:

дела Транзакция Т1 Транзакция Т2
действовать вставить в dl_tab(id,name) значения(40,8) вставить в dl_tab(id,name) значения(30,10)
связанный объект Уникальный индекс ua для testdb.dl_tab Уникальный индекс ua для testdb.dl_tab
замок удерживается lock_mode X блокирует запись, но не блокирует
кучу пробелов нет 6
10,26
режим блокировки S ожидающая
куча № 6
10 (a в шестнадцатеричном формате), 26 (1a в шестнадцатеричном формате)
ждущий замок lock_mode X блокирует промежуток перед записью, намерение вставки, ожидание
кучи нет 6
10,26
режим блокировки S ожидание
кучи нет 6
10,26

Из приведенного выше вывода состояния innodb. Вы можете видеть, что взаимоблокировка происходит по уникальному индексу ua. Это действительно распространенный сценарий взаимоблокировки в конфигурации уровня изоляции RC. Далее разбираем процесс взаимоблокировки:

A. Во-первых, транзакция T1 получает lock x,rec not not gapблокировку

B. Транзакция T2 пытается получить запись 10 в ua lock s, next key lock, поскольку T1 удерживает эксклюзивную блокировку записи, она заблокирована T1.

C. Транзакция T1 пытается получить запись 10 в ua lock x, gap before rec,insert intention, но блокируется

2. Задавайте вопросы

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

Q1: Почему T1 удерживает блокировку записи 10 в ua?

Q2: Поскольку T1 удерживает блокировку, почему она ждет блокировки?

Q3: T2 удерживает и ждет ту же блокировку, он удерживает или ждет?

Q4: Как именно происходит взаимоблокировка?

С этой целью я пообщался со студентами R&D, чтобы понять бизнес-сценарий, в котором возникла тупиковая ситуация, и снова воспроизвел проблему.

3. Воспроизведите проблему

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

Читателям и друзьям предлагается подумать 1 минуту, как дальше анализировать

1. Попробуйте решить

Основываясь на принципе решения проблем в первую очередь, он генерируется, когда уникальный индекс ua является параллельным.Можно ли уникальный индекс изменить на нормальный индекс? Если нет, можно ли уменьшить параллелизм (или напрямую изменить его на одиночный параллелизм)? Однако студенты, занимающиеся исследованиями и разработками, вскоре подтвердили, что единственный индекс uname зависит от базовой структуры и не может быть изменен! Требования к реальному времени для этой функции очень высоки, а объем бизнеса после выхода в онлайн относительно велик, поэтому параллелизм нельзя уменьшить или уменьшить. Поскольку этого нельзя избежать, мы можем только дополнительно проанализировать причину взаимоблокировки и соответствующим образом подтвердить решение!

После того, как R&D воспроизвел проблему, информации по-прежнему не было, кроме лога приложения и вывода состояния innodb. Кроме того, R&D обращается к тестовой среде, создает пакет данных, а затем моделирует и воспроизводит его. То есть: хотя взаимоблокировка может быть воспроизведена, на исходные вопросы (Q1, Q2, Q3, Q4) нельзя ответить.

2. Отслеживание процесса возникновения взаимоблокировки

Затем я нашел Percona, чтобы предоставить статью (ссылка: Как бороться с взаимоблокировками MySQL), которая примерно дает представление об анализе проблемы взаимоблокировки: на основе данных, предоставленных журналом приложения и статусом innodb, в сочетании с events_statements_history, binlog (предпочтительно в формате оператора), журнал медленных запросов (slow log) и общий журнал (общий журнал) для анализа.

Согласно статье, используйте существующие функции (events_statements_history/медленный журнал/общий журнал), чтобы узнать, какие операторы SQL выполнялись при подключении к базе данных. В дальнейшем сценарий резюмируется следующим образом:

-- 将events_statements_history 中的启动时间转换为标准时间
create database IF NOT EXISTS perf_db;
use perf_db;
DELIMITER //
create function f_convert_timer_to_utc(pi_timer bigint) returns timestamp(6)
DETERMINISTIC
begin
    declare value_utc_time timestamp(6);
    select FROM_UNIXTIME( (unix_timestamp(sysdate()) - variable_value) + pi_timer/1000000000000 )  from performance_schema.global_status where variable_name = 'Uptime' into value_utc_time;
    return value_utc_time;
end;
//
DELIMITER ;

--innodb status 输出中,死锁信息中MySQL thread id,实际表示是PROCESSLIST ID。执行语句找到thread_id 与PROCESSLIST_ID的对应关系
select PROCESSLIST_ID,THREAD_ID,PROCESSLIST_INFO from performance_schema.threads where PROCESSLIST_ID in (8,10);

-- 找到上一步找到的线程ID找到运行过的SQL语句
select THREAD_ID,
 perf_db.f_convert_timer_to_utc(TIMER_START) run_start_time,
 perf_db.f_convert_timer_to_utc(TIMER_END) run_end_time,
 TIMER_WAIT/1000000000000 wait_time_s,
 'False' is_current,
 CURRENT_SCHEMA,
 SQL_TEXT
 from performance_schema.events_statements_history where thread_id=51
union 
select THREAD_ID,
 perf_db.f_convert_timer_to_utc(TIMER_START) run_start_time,
 perf_db.f_convert_timer_to_utc(TIMER_END) run_end_time,
 TIMER_WAIT/1000000000000 wait_time_s,
 'True' is_current,
 CURRENT_SCHEMA,
 SQL_TEXT
 from performance_schema.events_statements_current where thread_id=51
 and (THREAD_ID,EVENT_ID,END_EVENT_ID) not in (select THREAD_ID,EVENT_ID,END_EVENT_ID from performance_schema.events_statements_history )
order by run_start_time;

Примечание. Красный текст в приведенном выше сценарии необходимо заменить в соответствии с реальной ситуацией.

Разобравшись со сценарием, студенты R&D снова попытались воспроизвести тупик. Запрос дает следующие результаты:

select PROCESSLIST_ID,THREAD_ID,PROCESSLIST_INFO from performance_schema.threads where PROCESSLIST_ID in (8,10);

+----------------+-----------+------------------+
| PROCESSLIST_ID | THREAD_ID | PROCESSLIST_INFO |
+----------------+-----------+------------------+
|              8 |        49 | NULL             |  
|             10 |        51 | NULL             | 
+----------------+-----------+------------------+

thread_id=49的sql运行情况:
|        49 | 2023-03-25 02:15:59.173352 | 2023-03-25 02:15:59.173612 |      0.0003 | False      | testdb         | begin                                     |
|        49 | 2023-03-25 02:16:08.349311 | 2023-03-25 02:16:08.350678 |      0.0014 | False      | testdb         | insert into dl_tab(id,name) values(26,10) |
|        49 | 2023-03-25 02:16:26.824176 | 2023-03-25 02:16:26.826121 |      0.0019 | False      | testdb         | insert into dl_tab(id,name) values(40,8)  |
+-----------+----------------------------+----------------------------+-------------+------------+----------------+-------------------------------------------+

thread_id=51 的sql运行情况:
|        51 | 2023-03-25 02:15:58.040749 | 2023-03-25 02:15:58.041057 |      0.0003 | False      | testdb         | begin                                     |
|        51 | 2023-03-25 02:16:17.408110 | 2023-03-25 02:16:26.828374 |      9.4203 | False      | testdb         | insert into dl_tab(id,name) values(30,10) |

Результаты расчесывания следующие:

Транзакция T1 — поток 49 Транзакция T2 — поток 51
начинать; начинать;
вставить в dl_tab(id,name) значения(26,10);
вставить в dl_tab(id,name) значения(30,10);
вставить в dl_tab(id,name) значения(40,8);

Согласно приведенным выше результатам прочесывания, если приведенный выше оператор SQL выполняется вручную в среде разработки, снова возникает взаимоблокировка, и информация о взаимоблокировке в статусе innodb в основном такая же, как и в тестовой среде.

На данный момент получен ответ на первый вопрос Q1:

Q1: Почему T1 удерживает блокировку записи 10 в ua?

Ответ: Поскольку перед транзакцией был выполнен следующий оператор, удерживается блокировка записи (26,10): вставьте в dl_tab(id,name) values(26,10);

3. Дополнительные мысли об отслеживании взаимоблокировок

Из-за возникновения этой взаимоблокировки случилось так, что два сеанса в выводе информации о взаимоблокировке состояния innodb вызвали взаимоблокировку. Однако в тупиковой ситуации может быть задействовано 3, 4 и более транзакций, поэтому возникает несколько дополнительных проблем:

Q5: Как отследить, если 3 или более транзакций участвуют в взаимоблокировке?

Q6: Выполненный оператор SQL должен быть самой непосредственной причиной взаимоблокировки.Суть блокировки - это запись, тип блокировки и отношения блокировки.Как это проверить?

Q7: После возникновения взаимоблокировки механизм обнаружения взаимоблокировки MySQL автоматически обнаружит взаимоблокировку и выберет транзакции для отката. После отката транзакции первая сцена взаимоблокировки уничтожается. За исключением самой последней информации о взаимоблокировках, предоставленной состоянием innodb (в частности, информация о блокировке, удерживаемая транзакцией 1, не была включена до версии 8.0.18), других доступных данных анализа нет.

Основываясь на трех вышеуказанных проблемах, в обобщенном виде представлены следующие дополнительные решения для сбора соответствующих данных о производительности:

  • Для Q7: временно отключите обнаружение взаимоблокировок в тестовой среде, а затем воспроизведите его снова:
innodb_deadlock_detect = off
innodb_lock_wait_timeout = 10
innodb_rollback_on_timeout = on
  • Для Q5 и Q6: В сочетании с существующими блокировками MySQL в реальном времени и данными о производительности ожидания блокировки, следующие сценарии суммированы:
-- 创建工作目录
cd <path-to-dir>
mkdir deadlock_data
cd deadlock_data

-- 创建死锁数据保存表
mysql -uroot -S /tmp/mysql.sock
create database IF NOT EXISTS perf_db;
use perf_db
CREATE TABLE `tab_deadlock_info` (
  `id` int primary key auto_incrment,
  `curr_dt` datetime(6) NOT NULL,
  `thread_id` bigint unsigned DEFAULT NULL,
  `conn_id` bigint unsigned DEFAULT NULL,
  `trx_id` bigint unsigned DEFAULT NULL,
  `object_name` varchar(64) DEFAULT NULL,
  `INDEX_NAME` varchar(64) DEFAULT NULL,
  `lock_type` varchar(32) NOT NULL,
  `lock_mode` varchar(32) NOT NULL,
  `lock_status` varchar(32) NOT NULL,
  `LOCK_DATA` varchar(8192) CHARACTER SET utf8mb4 DEFAULT NULL,
  `blk_trx_id` bigint unsigned DEFAULT NULL,
  `blk_thd_id` bigint unsigned DEFAULT NULL,
  index idx_curr_dt(curr_dt)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 查看当前存在的锁及锁堵塞信息
-- data_locks/data_lock_waits 自MySQL 8.0.1提供,之前版本查询information_schema.innodb_locks/ information_schema.innodb_lock_waits获取类似信息
vi save_lock_data.sql
insert into tab_deadlock_info(curr_dt,thread_id,conn_id,trx_id,object_name,index_name,
     lock_type,lock_mode,lock_status,lock_data,blk_trx_id,blk_thd_id)
select NOW(6) curr_dt,a.thread_id,b.PROCESSLIST_ID conn_id,
  ENGINE_TRANSACTION_ID trx_id, object_name,
  INDEX_NAME,lock_type,lock_mode,lock_status,LOCK_DATA,
  c.BLOCKING_ENGINE_TRANSACTION_ID blk_trx_id,
  c.BLOCKING_THREAD_ID blk_thd_id
from performance_schema.data_locks a left join performance_schema.threads b 
  on a.thread_id=b.thread_id
left join performance_schema.data_lock_waits c 
     on a.ENGINE_TRANSACTION_ID=c.REQUESTING_ENGINE_TRANSACTION_ID and a.thread_id=c.REQUESTING_THREAD_ID
where a.thread_id=b.thread_id order by thread_id,trx_id;

-- 查询脚本
mysql -uroot -S /tmp/mysql.sock perf_db -e "source save_lock_data.sql"

-- 定时查询脚本
vi run_save_lock.sh
while true
do
sleep 0.1 # 指定查询间隔时间,结合实际需求调整
mysql -uroot -S /tmp/mysql.sock perf_db -e "source save_lock_data.sql" 2>>run_save_lock.err
done

-- 执行查询
sh run_save_lock.sh

проиллюстрировать:

A. Настройте вышеуказанный параметр innodb_deadlock_detect, чтобы отключить обнаружение взаимоблокировок, состояние innodb больше не будет продолжать выводить последнюю информацию LATEST DETECTED DEADLOCK;

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

После повторного появления используйте tab_deadlock_info для запроса данных блокировки следующим образом:

В то же время информация о выписке, запрашиваемая с помощью шага 2), выглядит следующим образом:

На основании приведенных выше результатов запроса получены следующие результаты:

время Транзакция Т1 Транзакция Т2
2023-03-27 14:53:49 начинать;
2023-03-27 14:53:50 начинать;
2023-03-27 14:53:54 вставить в dl_tab(id,name) значения(26,10);
2023-03-27 14:53:59 Удержание: Lock X, Rec_not_gap записи ua (10,26) вставить в dl_tab(id,name) значения(30,10);
ждать: блокировка S записи ua (10,26)
2023-03-27 14:54:04 вставить в dl_tab(id,name) значения(40,8);
удержание: блокировка X записи ua (10,26), Rec_not_gap
ожидание: блокировка X, пробел, insert_intention записи ua (10,26)

Процесс возникновения взаимоблокировки снова разбирается.

4. Анализ первопричин

С помощью описанного выше процесса вы можете увидеть процесс взаимоблокировки, полученную блокировку и информацию о ее атрибутах. Но чтобы проанализировать, почему возникает взаимоблокировка, также необходимо объединить механизм реализации блокировки MySQL. Из-за описанного выше сценария взаимоблокировки логика реализации вставки, включающая уникальный индекс, будет интерпретироваться вместе с исходным кодом.

1. Логика вставки уникального индекса в один столбец

Рисунок ниже:

Синяя линия представляет логику, выполняемую T1 для первой вставки;

Фиолетовая линия представляет логику, выполняемую первой вставкой T2;

Черная линия представляет логику, выполняемую второй вставкой T1;

Операция блокировки, связанная с записью вставки уникального индекса, отмечена красной короткой стрелкой;

Вертикальная линия, пересекающая красную короткую заостренную голову, означает, что функция выполняется, в противном случае — что она не выполняется.

2. Окончательный тупиковый процесс

На основе измерения времени в сочетании с приведенной выше логикой блокировки mysql для анализа:

A. T1 и T2 начинают транзакцию, а затем T1 выполняет оператор вставки (26,10)

B. T2 выполняет оператор вставки для вставки (30,10). Проверьте наличие конфликтов уникальности, попробуйте получить LOCK_S | LOCK_ORDINARY [строка 15]. Тогда почему, когда вы видите LOCK S, это потому, что число, соответствующее LOCK_ORDINARY, выражено как 0, и любая операция «И» с ним равна самой себе, поэтому вы не можете его увидеть:

C. Затем соединение, в котором находится T2, преобразует неявную блокировку в T1 в явную блокировку [строка 17].В это время T1 получит LOCK_REC | LOCK_X | LOCK_REC_NOT_GAP, то есть Lock X, Rec_not_gap виден. Поскольку поток, в котором находится T2, создает блокировку для T1, thread_id, соответствующий блокировке, является идентификатором потока T2, а trx_id — идентификатором транзакции T1.

D. Но поскольку LOCK X|LOCK_REC_NOT_GAP T1 несовместим с LOCK S|LOCK_ORDINARY T2 [строка 23], T2 заблокирован.

E. Когда T2 заблокирован, внутренняя функция add_to_waitq также запишет создание блокировки при обработке и установит атрибуты LOCK_S | LOCK_ORDINARY | LOCK_WAIT | LOCK_REC, чтобы указать, что блокировка находится в ожидании [строка 24]. Затем T2 возвращается к функции верхнего уровня, чтобы дождаться блокировки ресурса [строка 38].

F. Затем T1 выполняет оператор вставки (40,8). Поскольку вставленное значение уникального индекса равно 8 (обратите внимание, что это не 10), конфликта первичных ключей нет, и оптимистичная операция вставки выполняется напрямую [строка 43].

G. При выполнении оптимистической вставки необходимо проверить, не блокируют ли другие транзакции операцию вставки. Суть его в том, чтобы получить следующее значение вставляемой записи [строки 46-47] (здесь ровно 10), и получить все блокировки на запись, и определить, есть ли конфликт с блокировкой, которую необходимо добавлено (LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION) [строка 51].

H. На шаге E T2 удерживает LOCK_S | LOCK_ORDINARY | LOCK_WAIT | LOC записи 10.

Блокировка K_REC несовместима с LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION T1, поэтому T1 заблокирован T2 [строка 51]

I. Формирование тупика.

Если вам нужно узнать больше деталей реализации, вы можете дополнительно подтвердить исходный код.

На данный момент получены ответы на первые вопросы Q2, Q3 и Q4:

Q2: Поскольку T1 удерживает блокировку, почему она ждет блокировки?

Ответ: В удержании блокировки сомнений быть не должно, при анализе подобных проблем обратите внимание на механизм преобразования неявных блокировок в явные (lock_rec_convert_impl_to_expl). Ожидание блокировки в основном связано с тем, что после блокировки T2 будут созданы блокировки (LOCK_S | LOCK_ORDINARY | LOCK_WAIT | LOCK_REC). Затем, когда T1 выполняет оптимистическую вставку, ему необходимо пройти через все > блокировки, существующие в записи, чтобы судить о конфликтах блокировок.Поскольку режим блокировки несовместим, он заблокирован

Q3: T2 удерживает и ждет ту же блокировку, он удерживает или ждет?

Ответ: Хотя по статусу innodb видно, что T2 держит и ждет блокировки s, следующей блокировки ключа. Но на самом деле дождитесь блокировки s, следующей блокировки ключа; из-за конфликтов блокировок блокировки будут удерживаться при присоединении к очереди ожидания LOCK_S | LOCK_ORDINARY | LOCK_WAIT | LOCK_REC

Q4: Как именно происходит взаимоблокировка?

Ответ: См. приведенный выше анализ анализа тупиковых процессов.

5. Решить проблему

Исходя из процесса возникновения и причин вышеуказанных взаимоблокировок, причины резюмируются следующим образом:

Причина 1: основные исследования и разработки чрезмерно зависят от уникального индекса, а вставленные данные не соответствуют требованиям уникального индекса, и требуется проверка уникальности на конфликт.

Причина 2: Данные, вставленные пакетами, не по порядку. Оба условия существуют одновременно, что приводит к взаимоблокировке.

Причина 2 сложнее контролировать в параллельном сценарии. Причина 1. Этот сценарий представляет собой логику параллельной пакетной вставки, которая позволяет избежать вставки повторяющихся имен при выполнении вставок. Впоследствии, после того как студенты R&D оптимизировали логику, проблема больше не возникала.

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

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

Supongo que te gusta

Origin blog.csdn.net/ActionTech/article/details/130323924
Recomendado
Clasificación