我有一张存储敏感数据的表,需要小心,只有一个会话能够读取/写入特定行。
我的表格有 2 列
id (int) primary
amount (int) index
我想锁定表,但只有一行。
类似的东西
LOCK TABLEROWS `mytable` WRITE WHERE `id` = 1
我正在使用 pdo,并且
startTransaction
不会阻止其他会话在此时进行读/写。
我阅读了 InnoDB 文档,但没有让它运行。
编辑:
$_PDO->exec('START TRANSACTION');
$_PDO->query('SELECT * FROM `currency` WHERE `id` = '.$userid.' FOR UPDATE');
//maybe do update or not check if
$_PDO->exec('COMMIT');
所以这就是我需要做的一切?
您显示的示例将导致其他会话执行
SELECT...FOR UPDATE
等待您的 COMMIT。 SELECT...FOR UPDATE
请求的锁是独占锁,因此一次只有一个会话可以获得该锁。因此,如果您的会话持有锁定,其他会话将等待。
您无法阻止非锁定读取。另一个会话可以在没有锁定子句的情况下运行
SELECT
,并且仍然读取数据。但他们无法更新数据,也无法请求锁定读取。
您也可以使用 LOCK TABLES 让每个会话请求表上的锁定,但您说您想要按行级别锁定。
您可以使用 GET_LOCK() 函数创建自己的自定义锁。这允许您为每个用户 ID 创建一个不同的锁定。如果您对访问该表的所有代码都执行此操作,则不需要使用
FOR UPDATE
。
$lockName = 'currency' . (int) $userid;
$_PDO->beginTransaction();
$stmt = $_PDO->prepare("SELECT GET_LOCK(?, -1)");
$stmt->execute([$lockName]);
$stmt = $_PDO->prepare('SELECT * FROM `currency` WHERE `id` = ?');
$stmt->execute([$userid]);
//maybe do update or not check if
$_PDO->commit();
$stmt = $_PDO->prepare("SELECT RELEASE_LOCK(?)");
$stmt->execute([$lockName]);
这取决于所有客户端代码的协作。它们在处理给定行之前都需要获取锁。您可以使用
SELECT...FOR UPDATE
,也可以使用 GET_LOCK()
。
但是你不能阻止想要使用
SELECT
进行非锁定读取的客户端。