如果我们谈论 InnoDB 中具有唯一索引的 table ,事务会相互等待还是并行工作?
Will transactions wait for each other or work parallel if we talk about a table with a unique index in InnoDB?
我在 InnoDB 引擎中有一个 table 看起来像这样:
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`);
如您所见,此 table 在列 "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 将使用 "insert" 命令卡在第一个字符串上,等待第一个脚本中的事务提交。当第一个脚本从睡眠中醒来并提交事务时,将在 table 中创建两个新字符串('first' 和 'violate_str'),第二个脚本将再次开始工作:第二个脚本会经历两次插入,但在第二次插入 ('violate_str') 时会生成异常。
我的问题是“为什么 PHP 一直在等待第一个事务完成并且这次不工作?MySQL 是否阻止了第二个事务?为什么?”。我认为我对交易工作原理有一些误解。我以为交易会一个一个创建,第二个交易不会被阻塞。
您的第二个脚本正在等待 第二个 插入,而不是第一个插入。
如果我用 echo 语句测试你的代码:
//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()
那么它会释放它的锁并撤消插入。这允许第二个脚本完成 和 自由插入有争议的值。
我在 InnoDB 引擎中有一个 table 看起来像这样:
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`);
如您所见,此 table 在列 "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 将使用 "insert" 命令卡在第一个字符串上,等待第一个脚本中的事务提交。当第一个脚本从睡眠中醒来并提交事务时,将在 table 中创建两个新字符串('first' 和 'violate_str'),第二个脚本将再次开始工作:第二个脚本会经历两次插入,但在第二次插入 ('violate_str') 时会生成异常。
我的问题是“为什么 PHP 一直在等待第一个事务完成并且这次不工作?MySQL 是否阻止了第二个事务?为什么?”。我认为我对交易工作原理有一些误解。我以为交易会一个一个创建,第二个交易不会被阻塞。
您的第二个脚本正在等待 第二个 插入,而不是第一个插入。
如果我用 echo 语句测试你的代码:
//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()
那么它会释放它的锁并撤消插入。这允许第二个脚本完成 和 自由插入有争议的值。