`UPDATE tbl SET col = col + 1` 需要 LOCK TABLE 吗?

is a LOCK TABLE needed for `UPDATE tbl SET col = col + 1`?

假设 col 为 0,并且 100 个客户端同时试图将其增加 1,这里需要 LOCK TABLE 吗?

LOCK TABLE tbl;
UPDATE tbl SET col = col + 1;
UNLOCK TABLE;

或者我可以做吗

UPDATE tbl SET col = col + 1;

并确保 col 变为 100? (例如,mariadb 确保 none 其中两次读取相同的值~)

我基本上是在尝试做相当于这个 php 代码的事情

$fp = fopen("counter.txt", "r+b");
flock($fp, LOCK_EX);
$number = (int) stream_get_contents($fp);
$number += 1;
rewind($fp);
fwrite($fp, (string) $number);
flock($fp, LOCK_UN);
fclose($fp);

但使用数据库而不是文本文件

我怀疑 MariaDB 和 MySQL 在这方面有区别,但如果有的话,我想了解它,所以标记两者(作为轶事,我正在使用 MySQL 上班,MariaDB 在家~)

首先:您的查询似乎缺少 WHERE 子句,该子句指定应更新哪一行。据推测,您会为此使用主键列,比如 id - 例如:

UPDATE tbl SET col = col + 1 WHERE id = 1;

现在,假设您同时有许多并发进程 运行 此查询,那么我不建议手动锁定该行。相反,让数据库为您管理并发。它实际上会在幕后为您锁定行,并按顺序执行并发查询,在提交前一个事务时将行交给下一个事务。如果初始值为 0 并且有 100 个并发进程,则最终值为 100

首先,不要使用MyISAM,使用InnoDB。不要将 LOCK TABLES 与 InnoDB 一起使用。

在任何情况下,您都不需要为单个查询使用任何类型的锁。查询具有隐式锁。 (MyISAM 锁定 table;InnoDB 锁定行。)

当您有 多个 语句需要“原子”地保持在一起时,需要锁/事务。请注意您的示例如何同时进行读取和写入,以及如何使用读取的值进行操作。

InnoDB 中的等价物(伪代码):

START TRANSACTION;
$num = SELECT num FROM tbl WHERE id=1  FOR UPDATE;
$num = $num + 1;
UPDATE tbl SET num = $num;
COMMIT;

是的,UPDATE table SET num = num + 1 以原子方式执行相同的操作,但它不允许您使用 num.

的值执行任何其他操作

MariaDB 在某些情况下有一个 RETURNS 子句。有LAST_INSERT_ID()。但这些是规则的有限例外,如果您希望多个线程处理相同的数据,您应该真正考虑多语句事务。