简体   繁体   English

如何检测2位管理员是否同时在php中更新表行

[英]How to detect if 2 admins update a table row at the same time in php

The website i'm developing has an admin page to add products to the database or edit existing products in the database. 我正在开发的网站上有一个管理页面,可将产品添加到数据库或编辑数据库中的现有产品。 If 2 admins want to edit the same product the second admin should get an alert that the product has already been/is now being updated. 如果2位管理员想要编辑同一产品,则第二位管理员应收到有关该产品已经/正在更新的警报。 How could I detect in php/sql when 2 admins try to edit the same product? 当2位管理员试图编辑同一产品时,如何检测php / sql?

I haven't really tried anything because I have no idea what to try or where to start. 我真的没有尝试过任何东西,因为我不知道尝试什么或从哪里开始。

here's how my function looks for updating a product in the database: 这是我的函数在数据库中更新产品的外观:

//I know this is a VERY unsafe function, this website will never go online!
FUNCTION updateProduct($data) {
  include_once 'dbconn.php';
  try {
    $conn = connectToDb();
    if ($data['imageChanged'] == false) {
      $query = "SELECT img_url FROM products WHERE product_id=".$data['productId'];
      $result = mysqli_query($conn, $query);
      if ($result == false) {
        throw new Exception('Failed to execute: '. $query . 'Error: '. mysqli_error($conn));
      }
      $imgUrl = mysqli_fetch_row($result)[0];
    } else {
      $imgUrl = $data['image'];
    }
    $query2 = "img_url='".$imgUrl."'";
    $query = "UPDATE products
              SET product_name='".$data['productName']."', product_desc='".$data['productDesc']."', price=".$data['price'].", ". $query2 . " 
              WHERE product_id=".$data['productId'];
    $result = mysqli_query($conn, $query);
    if ($result == false) {
      throw new Exception('Failed to execute: '. $query . 'Error: '. mysqli_error($conn));
    }
  } finally {
    mysqli_close($conn);
  }
}

edit: storing the old data after an update is made is not needed nor is storing the updates being made(regarding the question this might be a duplicate of). 编辑:不需要在进行更新后存储旧数据,也不需要存储正在进行的更新(关于这个问题可能是重复的)。 The thing I would like to know is: if admin_1 is updating a row, and admin_2 is trying to update the same row at the time admin_1's transaction is still ongoing, how can my script detect that this is happening? 我想知道的是:如果admin_1正在更新一行,而admin_2在尝试进行事务处理时正在尝试更新同一行,我的脚本如何检测到这种情况呢?

This is usually done by adding a version column that is updated every time the row changes. 通常,这是通过添加每次行更改时都会更新的版本列来完成的。 Before updating the row, check if the value is still the same as when the row was last read: 在更新该行之前,请检查该值是否与上次读取该行的值相同:

SELECT img_url, version FROM products WHERE product_id = ?;
-- Suppose this returns ('https://example.com/foo.jpg', 1)
-- Remember that the current version is 1.

Later: 后来:

BEGIN;

SELECT version FROM products WHERE product_id = ? FOR UPDATE;
-- Check if version is still 1. If it's not, someone modified the row (execute ROLLBACK in this case). 
-- Otherwise:
UPDATE products SET img_url = ?, version = version + 1 WHERE product_id = ?;

COMMIT;

If for whatever reason transactions are not available, an alternative is to use a simple compare-and-swap: 如果出于某种原因无法进行交易,则可以使用简单的比较和交换方法:

UPDATE products SET img_url = ?, version = version + 1 WHERE product_id = ? AND version = 1;

If the number of updated rows is zero, the row has been modified in the meantime. 如果更新的行数为零,则此行已被修改。

Arguably, this is slightly quicker than the SELECT FOR UPDATED followed by UPDATE. 可以说,这比SELECT FOR UPDATED和UPDATE快一些。 However, having the conflicting version of the row enables much richer user feedback. 但是,如果该行的版本相互冲突,则会产生更丰富的用户反馈。 With an author column you can tell who updated the row, for instance, not just that it happened. 例如,通过作者列,您可以知道是更新了该行,而不仅是它发生了。

consider below points 1) u want multiple admin to edit different products 2) u do not want multiple admin to edit same product 考虑以下几点1)您要多个管理员编辑不同的产品2)您不希望多个管理员编辑相同的产品

3) in your code u saved product as a single table. 3)在您的代码中,您将产品保存为单个表。 In a real e commerce site product information is split into several tables 在一个真实的电子商务站点中,产品信息被分为几个表

to achieve this u have to set up application wide mysql locks 要实现此目的,您必须设置应用程序范围的mysql锁

mysql> select get_lock('product_id_12345', 5);
+---------------------------------+
| get_lock('product_id_12345', 5) |
+---------------------------------+
|                               1 |
+---------------------------------+
1 row in set (0.00 sec)

above code sets up a lock using a product_id, if some other connection tries to get lock using the same product_id, u will get 0 as response - indicating that some other user is updating the same product_id 上面的代码使用product_id设置了锁定,如果其他连接尝试使用相同的product_id获取锁定,则u的响应将为0-表示其他一些用户正在更新相同的product_id

mysql> 
mysql> select IS_FREE_LOCK('product_id_12345');
+----------------------------------+
| IS_FREE_LOCK('product_id_12345') |
+----------------------------------+
|                                0 |
+----------------------------------+
1 row in set (0.00 sec)

mysql> 
mysql> select IS_USED_LOCK('product_id_12345');
+----------------------------------+
| IS_USED_LOCK('product_id_12345') |
+----------------------------------+
|                               46 |
+----------------------------------+
1 row in set (0.00 sec)

mysql> 
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
|              46 |
+-----------------+
1 row in set (0.00 sec)

SAMPLE ALGORITHM 样本算法

<?php
ini_set('display_errors', 1);

function updateProduct() {

$user = 'root';
$pass = 'xxx';
$DB = 'test';
$host = 'localhost';

  try 
  {
    $conn = new mysqli($host, $user, $pass, $DB);


    $data['productId'] = 'product_id_12345';
    $data['productName'] = 'test';
    $data['productDesc'] = 'testing';

    $isLockFree = 'select IS_USED_LOCK("'.$data['productId'].'") ';
    $isLockFreeResult = mysqli_query($conn, $isLockFree);
    $row=mysqli_fetch_row($isLockFreeResult);

    if(empty($row[0]))
    {
        $lock = 'select get_lock("'.$data['productId'].'")';
        $result = mysqli_query($conn, $lock);

        echo 'lock established';

        $query = "UPDATE products
                  SET product_name='".$data['productName']."', 
                      product_desc='".$data['productDesc']."', 
                      price=".$data['price']."
                  WHERE 
                  product_id=".$data['productId'];

        $result = mysqli_query($conn, $query);
        if ($result == false) 
        {
          throw new Exception('Failed to execute: '. $query . 'Error: '. mysqli_error($conn));
        }
    }
    else
    {
        echo 'sorry! could not lock. somebody is updating the product info';
    }
  } 
  finally 
  {
    mysqli_close($conn);
  }
}
updateProduct();
?>

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

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