Допустим, мы выполняем...
SELECT * FROM MY_TABLE FOR UPDATE
... и в MY _TABLE более одной строки.
Теоретически, если две одновременных транзакции выполняют этот оператор, но происходит обход (и, следовательно, блокировка )строк в другом порядке, может возникнуть взаимоблокировка. Например:
Чтобы решить эту проблему, используйте ORDER BY, чтобы гарантировать, что строки всегда будут заблокированы в одном и том же порядке.
Итак, мой вопрос: :возникнет ли когда-нибудь этот теоретический тупик на практике? Я знаю, что есть способы искусственно вызвать его , но могло ли это когда-нибудь произойти при нормальной работе? Должны ли мы просто всегда использовать ORDER BY, или на самом деле безопасно его опустить?
Меня в первую очередь интересует поведение Oracle и MySQL/InnoDB, но комментарии по другим СУБД также будут полезны.
Вот как воспроизвести взаимоблокировку в Oracle, когда порядок блокировки не тот же:
Создайте тестовую таблицу и заполните ее тестовыми данными...
CREATE TABLE DEADLOCK_TEST (
ID INT PRIMARY KEY,
A INT
);
INSERT INTO DEADLOCK_TEST SELECT LEVEL, 1 FROM DUAL CONNECT BY LEVEL <= 10000;
COMMIT;
...из одного клиентского сеанса (я использовал SQL Developer ), запустил следующий блок:
DECLARE
CURSOR CUR IS
SELECT * FROM DEADLOCK_TEST
WHERE ID BETWEEN 1000 AND 2000
ORDER BY ID
FOR UPDATE;
BEGIN
WHILE TRUE LOOP
FOR LOCKED_ROW IN CUR LOOP
UPDATE DEADLOCK_TEST
SET A = -99999999999999999999
WHERE CURRENT OF CUR;
END LOOP;
ROLLBACK;
END LOOP;
END;
/
Из другого клиентского сеанса (я просто запустил еще один экземпляр SQL Developer ), запустив тот же блок, но с DESC
в ORDER BY
. Через несколько секунд вы получите:
ORA-00060: deadlock detected while waiting for resource
Кстати, вы, скорее всего, добьетесь того же результата, полностью удаливORDER BY
(так что оба блока идентичны ), и добавление...
ALTER SESSION SET OPTIMIZER_INDEX_COST_ADJ = 1;
...перед одним кварталом, но...
ALTER SESSION SET OPTIMIZER_INDEX_COST_ADJ = 10000;
... перед другим (, поэтому Oracle выбирает разные планы выполнения и, вероятно, извлекает строки в другом порядке ).
Это показывает, что блокировка действительно выполняется, когда строки извлекаются из курсора (, а не для всего набора результатов -сразу при открытии курсора ).