乐观的锁和交错

时间:2015-07-26 16:53:52

标签: mysql database concurrency

我读到了乐观锁定方案,其中客户端可以读取值,执行计算以及何时需要执行写入,在写入数据库之前验证更新。

让我们说如果我们为Optimistic Locks使用版本机制,那么(如果是两个客户端)两者都将具有更新语句:

更新tableName设置字段= val,版本= oldVersion +1其中    version = OldVersion和Id = x;

现在让我们考虑使用两个客户端的以下场景:

  1. 两个客户端都读取字段和版本的值。
  2. 两个客户都在那里计算一些东西。生成新的字段值。
  3. 现在两个客户端都向数据库服务器发送查询请求。

    一旦到达数据库: 一个客户端更新查询开始执行。 但在平均时间交错发生和其他客户端更新 开始执行。

  4. 这些查询交错是否会导致数据竞争 我的意思是说,我们不能说乐观锁自己执行,例如我理解行级锁定发生或其他锁定如表级锁定发生的情况,然后就好了。 但是它像乐观锁一样不能单独工作,它也需要悲观锁(行级/表级,完全取决于底层存储引擎实现)。

    当没有Row / table级锁定但希望实现Optimistic Locking策略时会发生什么。使用查询交错将导致表中的数据竞争。(我的意思是说只更新字段而版本不然后发生交错。这完全取决于为查询设置的隔离级别)?

    我对这种情况有点困惑。

    与悲观锁定相比,乐观锁定真正有用并提高应用程序整体性能的正确用例是什么。

1 个答案:

答案 0 :(得分:1)

最坏情况下伪代码的场景:两个客户端更新相同的记录:

场景1(您的场景:乐观锁定):

在服务器端检查最终约束。乐观锁定仅用于演示目的。

  

客户1订购的产品只有1个库存。

     

客户2订购的产品只有1只。

两位客户都会在屏幕上显示此内容。

产品表:

 CREATE TABLE products (
   product_id VARCHAR(200),
   stock INT,
   price DOUBLE(5,2)
 ) ENGINE=InnoDB;

演示文稿代码:

 -- Presentation:
 SELECT * FROM products WHERE product_id="product_a";
 -- Presented to client

订单代码:

 -- Verification of record (executed in the same block of code within 
 -- an as short time interval as possible):
 SELECT stock FROM products WHERE product_id="product_a";
 IF(stock>0) THEN
 -- Client clicks "order" (one click method=also payment);
   START TRANSACTION;
     -- Gets a record lock
     SELECT * FROM products WHERE product_id="product_a" FOR UPDATE; 
     UPDATE products SET stock=stock-1 WHERE product_id="product_a";
     INSERT INTO orders (customer_id,product_id,price) 
       VALUES (customer_1, "product_a",price);
   COMMIT;
 END IF;
  

此方案的结果是两个订单都可以成功:它们都从第一个选择中获取stock>0,然后执行订单放置。这是一种不受欢迎的情况(几乎在任何情况下)。因此,必须通过取消订单在代码中解决这个问题,并进行更多的交易。

场景2:乐观锁定的替代方案:

在数据库端检查最终约束。乐观锁定仅用于演示目的。在之前的乐观锁定场景中,数据库查询较少,重做的可能性较小。

  

客户1订购的产品只有1个库存。

     

客户2订购的产品只有1只。

两位客户都会在屏幕上显示此内容。

产品表:

 CREATE TABLE products (
   product_id VARCHAR(200),
   stock INT,
   price DOUBLE(5,2),
   CHECK (stock>=-1) -- The constraint preventing ordering
 ) ENGINE=InnoDB;

演示文稿代码:

 -- Presentation:
 SELECT * FROM products WHERE product_id="product_a";
 -- Presented to client

订单代码:

 -- Client clicks "order" (one click method=also payment);
 START TRANSACTION;
   -- Gets a record lock
   SELECT * FROM products WHERE product_id="product_a" FOR UPDATE; 
   UPDATE products SET stock=stock-1 WHERE product_id="product_a";
   INSERT INTO orders (customer_id,product_id,price) 
     VALUES (customer_1, "product_a",price);
 COMMIT;
  

现在,两位客户都会看到此产品,并同时点击订单。系统同时执行两个订单。结果将是:一个订单将被放置,另一个订单将获得异常,因为约束将无法验证,并且事务将被中止。此中止(例外)必须在代码中处理,但不会进行任何进一步的查询或事务。