简体   繁体   English

多个客户端同时执行SQL

[英]Multiple Client execute SQL at the same time

I have a sample data (Slots = 1) The system must provide 1 slot for a client. 我有一个样本数据(Slots = 1),系统必须为客户端提供1个插槽。 If the client A gets a slot, sample data will be Slots = 0. 如果客户端A获得一个插槽,则样本数据将为Slots = 0。

What if Client A and Client B execute the SQL query at the same time? 如果客户端A和客户端B同时执行SQL查询怎么办? The Sample data will be Slots = -1. 样本数据将为Slots = -1。

I tried to prevent it using PHP, 我试图使用PHP阻止它,

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

But clients still executing the SQL query. 但是客户端仍在执行SQL查询。 How can I make ONLY ONE can get the slot even if they execute it at the same time? 我如何才能使一个插槽即使同时执行也只能获得一个?

To start, make sure your table engine is InnoDB , as MyISAM can only do table level locking instead of row locking. 首先,请确保您的表引擎是InnoDB ,因为MyISAM只能执行表级锁定,而不能进行行锁定。

Then you can use the SELECT .. FOR UPDATE command to get exclusive access to the row. 然后,您可以使用SELECT .. FOR UPDATE命令来获得对该行的独占访问。

This answer on this thread on dba.stackexchange.com explains it perfectly: https://dba.stackexchange.com/a/15864 dba.stackexchange.com上此线程上的此答案完美地说明了这一点: https ://dba.stackexchange.com/a/15864

Once the row is locked, MySQL will throw an error to any other process that tries to lock or update that row. 一旦该行被锁定,MySQL将向尝试锁定或更新该行的任何其他进程抛出错误。 So to tell a user the slots are already taken, you'll need to catch any errors and alert the user accordingly. 因此,要告诉用户该插槽已被占用,您需要捕获所有错误并相应地警告用户。

Here's a simple example: 这是一个简单的例子:

<?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();
}

It's easy to test this locally. 在本地测试很容易。 Just open up a second MySQL session via command-line and execute these commands to set the lock: 只需通过命令行打开第二个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;

Take a look at the MySQL docs for more info: https://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html 看看MySQL文档以了解更多信息: https : //dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM