2015-05-23 64 views
0

我有一个示例数据(插槽= 1) 系统必须为客户端提供1个插槽。如果客户端A获得一个插槽,则样本数据将为插槽= 0.多个客户端同时执行SQL

如果客户端A和客户端B同时执行SQL查询会怎么样? 示例数据将为Slots = -1。

我试图阻止它使用PHP,

if($Slots > 0){ 
execute.... 
} 

但客户仍执行的SQL查询。 我怎样才能让一个人即使在同一时间执行它也能得到这个插槽?

回答

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

+0

如何客户端B知道他/她的行动不成功? –

+0

如果你尝试更新一个锁定的行,MySQL将会抛出一个'Lock超时超时;尝试重新启动交易错误。我会在例外情况下捕捉到这一点,并让用户知道他无法获得插槽。我不认为有另一种方法来检查锁定的行。 –

+0

我已经使用有效的代码示例更新了我的答案。希望有助于澄清。 –