Будут ли транзакции ждать друг друга или работать параллельно, если мы поговорим о таблице с уникальным индексом в InnoDB? - PullRequest
1 голос
/ 04 мая 2019

У меня одна таблица в движке InnoDB выглядит так:

CREATE TABLE `sample` (
   `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
   `unique_str` varchar(255) NOT NULL
);
ALTER TABLE `sample`
   ADD UNIQUE `unique_str_index` (`unique_str`);

Как видите, эта таблица имеет уникальный индекс для столбца "unique_str".

Например, я хочу запустить два сценария с транзакциями по одной.

//First script, i use pdo for connection
$pdo->beginTransaction();
$pdo->exec("INSERT INTO sample (unique_str) VALUES('first')");
$pdo->exec("INSERT INTO sample (unique_str) VALUES('violate_str')");
sleep(50); //I'll start second script here
$pdo->commit();

Я начну второй сценарий, когда первый сценарий спит в течение 50 секунд.

//Second script
$pdo->beginTransaction();
$pdo->exec("INSERT INTO sample (unique_str) VALUES('third')");
$pdo->exec("INSERT INTO sample (unique_str) VALUES('violate_str')"); 
$pdo->commit();

Во втором скрипте PHP будет привязан к самой первой строке с помощью команды «вставка» и будет ждать, пока транзакция в первом скрипте будет зафиксирована. Когда первый скрипт выйдет из спящего режима и завершит транзакцию, в таблице будут созданы две новые строки ('first' и 'violate_str'), и второй скрипт снова начнет работать: второй скрипт будет проходить через две вставки, но на второй вставке ('violate_str') будет сгенерировано исключение.

У меня вопрос ", почему PHP ждет, пока первая транзакция будет завершена, и не работает на этот раз? MySQL блокирует вторую транзакцию? Почему? ". Я думаю, что у меня есть некоторое недопонимание принципов работы транзакций. Я думал, что транзакции будут создаваться одна за другой, а вторая транзакция не будет блокироваться.

1 Ответ

1 голос
/ 05 мая 2019

Ваш второй скрипт ожидает вставки second , а не первой вставки.

Если я проверю ваш код с помощью эхо-операторов:

//First script, i use pdo for connection
$pdo->beginTransaction();
echo "begin done\n";
$pdo->exec("INSERT INTO sample (unique_str) VALUES('first')");
echo "insert('first') done\n";
$pdo->exec("INSERT INTO sample (unique_str) VALUES('violate_str')");
echo "insert('violate_str') done\n";
sleep(50); //I'll start second script here
echo "sleep done\n";
$pdo->commit();
echo "commit done\n";

//Second script
$pdo->beginTransaction();
echo "begin done\n";
$pdo->exec("INSERT INTO sample (unique_str) VALUES('third')");
echo "insert('third') done\n";
$pdo->exec("INSERT INTO sample (unique_str) VALUES('violate_str')");
echo "insert('violate_str') done\n";
$pdo->commit();
echo "commit done\n";

Я вижу вывод из первого скрипта:

begin done
insert('first') done
insert('violate_str') done

Теперь он перешел в спящий режим, поэтому я запускаю второй скрипт и сразу же вижу вывод:

begin done
insert('third') done

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

Как только сон заканчивается, первый скрипт фиксируется и выдает:

sleep done
commit done

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

Fatal error: Uncaught PDOException: SQLSTATE[23000]: Integrity constraint violation: 
1062 Duplicate entry 'violate_str' for key 'unique_str'

Если первый сценарий должен был сделать rollback() вместо commit(), тогда он снимает блокировку и отменяет вставку. Это позволяет второй сценарий, чтобы закончить и вставить спорное значение свободно.

...