InnoDB Select 更新
InnoDB Select For Update
如果我有一个 InnoDB 的简单语句 Table
UPDATE <table> SET locked=1, col2=<Val2> WHERE locked=0
真的需要SELECT更新吗?
既然 InnoDB 支持行级锁定,即使成千上万的客户端同时执行同一个脚本,是否有可能发生冲突?
更新:
像这样可以防止锁定
$dbh = new PDO(DSN, DB_USER, DB_PASS);
$dbh->beginTransaction();
$selQuery = $dbh->prepare("SELECT <col> FROM <table> WHERE status=0 LIMIT 1 FOR UPDATE");
$selQuery->bindColumn(<col1>, $col1);
$selQuery->execute();
$selQuery->fetch(PDO::FETCH_BOUND);
$dbh->query("UPDATE <table> SET status=1 WHERE status=0 LIMIT 1");
$dbh->commit();
是的,碰撞总是会发生。所以使用设计模式:
SELECT FOR UPDATE
在给定的事务中首先获取所有资源,可以防止或缩短死锁情况。
假设以下场景:
- 进程 A 按 1,2
的顺序更新 table 1 & 2
- 进程 B 按照 2,1
的顺序更新 table 1 & 2
现在进程A更新,table1,进程B同时更新table2,造成死锁(假设是命中了同一个“records/pages”通过这次更新)。
但是,如果 SELECT FOR UPDATE
将在事务开始时使用,事务将在开始时阻塞,因为 table 2(或 1,以更快者为准)不能被锁定(还).这里的关键部分是“在交易开始时”,如果您稍后再做,那么 运行 和 UPDATE
一样有效。
关键始终是保持事务的原子性和快速性:对 SQL 逻辑进行分组,以便它可以在中间执行最少数量的其他代码,并尽可能缩短锁定时间。
如果您只需要
BEGIN;
UPDATE ...
COMMIT;
那就只做那个。
如果您需要这样做:
BEGIN;
SELECT stuff from table T; -- needs FOR UPDATE
work with the stuff, and eventually
UPDATE T ...;
COMMIT;
然后你做需要FOR UPDATE
来防止另一个连接改变T。
如果没有 FOR UPDATE
如果在您处理这些内容时更改了 T,那么您要么逐步执行他们所做的更改。或者他们可以更改使您的 UPDATE
不正确的内容。
UPDATE...WHERE
语句自动获取正在更新的行的独占锁。您不需要显式启动事务或使用 SELECT..FOR UDPATE
.
如果您选择行然后稍后更新它们,那么这就是使用 SELECT..FOR UPDATE
的地方。这可以防止任何其他客户端在您更新行之前看到这些行的旧数据。
如果有数千个客户端,每个客户端都必须获得独占锁,因此它们可能会被另一个先获得它的客户端阻止,然后才能进行自己的更改。
如果我有一个 InnoDB 的简单语句 Table
UPDATE <table> SET locked=1, col2=<Val2> WHERE locked=0
真的需要SELECT更新吗?
既然 InnoDB 支持行级锁定,即使成千上万的客户端同时执行同一个脚本,是否有可能发生冲突?
更新:
像这样可以防止锁定
$dbh = new PDO(DSN, DB_USER, DB_PASS);
$dbh->beginTransaction();
$selQuery = $dbh->prepare("SELECT <col> FROM <table> WHERE status=0 LIMIT 1 FOR UPDATE");
$selQuery->bindColumn(<col1>, $col1);
$selQuery->execute();
$selQuery->fetch(PDO::FETCH_BOUND);
$dbh->query("UPDATE <table> SET status=1 WHERE status=0 LIMIT 1");
$dbh->commit();
是的,碰撞总是会发生。所以使用设计模式:
SELECT FOR UPDATE
在给定的事务中首先获取所有资源,可以防止或缩短死锁情况。
假设以下场景:
- 进程 A 按 1,2 的顺序更新 table 1 & 2
- 进程 B 按照 2,1 的顺序更新 table 1 & 2
现在进程A更新,table1,进程B同时更新table2,造成死锁(假设是命中了同一个“records/pages”通过这次更新)。
但是,如果 SELECT FOR UPDATE
将在事务开始时使用,事务将在开始时阻塞,因为 table 2(或 1,以更快者为准)不能被锁定(还).这里的关键部分是“在交易开始时”,如果您稍后再做,那么 运行 和 UPDATE
一样有效。
关键始终是保持事务的原子性和快速性:对 SQL 逻辑进行分组,以便它可以在中间执行最少数量的其他代码,并尽可能缩短锁定时间。
如果您只需要
BEGIN;
UPDATE ...
COMMIT;
那就只做那个。
如果您需要这样做:
BEGIN;
SELECT stuff from table T; -- needs FOR UPDATE
work with the stuff, and eventually
UPDATE T ...;
COMMIT;
然后你做需要FOR UPDATE
来防止另一个连接改变T。
如果没有 FOR UPDATE
如果在您处理这些内容时更改了 T,那么您要么逐步执行他们所做的更改。或者他们可以更改使您的 UPDATE
不正确的内容。
UPDATE...WHERE
语句自动获取正在更新的行的独占锁。您不需要显式启动事务或使用 SELECT..FOR UDPATE
.
如果您选择行然后稍后更新它们,那么这就是使用 SELECT..FOR UPDATE
的地方。这可以防止任何其他客户端在您更新行之前看到这些行的旧数据。
如果有数千个客户端,每个客户端都必须获得独占锁,因此它们可能会被另一个先获得它的客户端阻止,然后才能进行自己的更改。