我有一个示例数据(Slots = 1) 系统必须为客户端提供1个插槽。如果客户端A获得一个槽,则样本数据将为Slots = 0。
如果客户端A和客户端B同时执行SQL查询,该怎么办? Sample数据将为Slots = -1。
我试图用PHP防止它,
if($Slots > 0){
execute....
}
但客户端仍在执行SQL查询。 即使他们同时执行它,我怎么能只有一个可以获得插槽?
答案 0 :(得分:1)
首先,确保您的表引擎为InnoDB
,因为MyISAM
只能执行表级锁定而不是行锁定。
然后,您可以使用SELECT .. FOR UPDATE
命令获取对该行的独占访问权。
dba.stackexchange.com上这个帖子的答案完美地解释了它:https://dba.stackexchange.com/a/15864
一旦该行被锁定,MySQL将向尝试锁定或更新该行的任何其他进程抛出错误。因此,要告诉用户已经使用了插槽,您需要捕获任何错误并相应地提醒用户。
这是一个简单的例子:
<?php
$db = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', 'password');
// Ask PDO to throw Exceptions on error
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try
{
$db->beginTransaction();
// If the row is locked, mysql will wait for a time-out. By default the timeout is 50 seconds
// so to make sure the flow of the script works, we set the time-out to 1
$db->query('SET innodb_lock_wait_timeout = 1;');
// Select the row we want to update and tell MySQL to lock it until we are done updating
// by adding the "FOR UPDATE" command to the query.
// Note that if the row is already locked by another process, this query will throw an error
// which we'll catch below. It means that someone else already has the slot and is updating it.
$result = $db->query('SELECT slots FROM table WHERE id = 1 FOR UPDATE');
if ($result !== false)
{
$object = $result->fetchObject();
if ($object->slots > 0)
{
$db->query('UPDATE table SET slots = slots - 1 WHERE id = 1');
echo "You got the slot!";
}
else echo "Sorry, no more slots available.";
}
// Commit the transaction and release the lock, and move on.
$db->commit();
}
catch (Exception $e)
{
// An error was thrown, so rollback the transaction
$db->rollback();
// and tell the user he couldn't get the slot
echo "Failed to reserve a slot for you: (" . $e->getMessage() . ")";
exit();
}
在本地测试这个很容易。只需通过命令行打开第二个MySQL会话并执行这些命令来设置锁定:
{10:34}[5.4.36]/tmp ➭ mysql -uroot -p
Welcome to the MySQL monitor. Commands end with ; or \g.
..
mysql> USE test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT slots FROM table WHERE id = 1 FOR UPDATE;
+-------+
| slots |
+-------+
| 87 |
+-------+
1 row in set (0.00 sec)
..
the lock is set, if you now run the example script you should not be able to update the slots and get an error
..
mysql> COMMIT;
请查看MySQL文档以获取更多信息:https://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html