我读到了乐观锁定方案,其中客户端可以读取值,执行计算以及何时需要执行写入,在写入数据库之前验证更新。
让我们说如果我们为Optimistic Locks使用版本机制,那么(如果是两个客户端)两者都将具有更新语句:
更新tableName设置字段= val,版本= oldVersion +1其中 version = OldVersion和Id = x;
现在让我们考虑使用两个客户端的以下场景:
现在两个客户端都向数据库服务器发送查询请求。
一旦到达数据库: 一个客户端更新查询开始执行。 但在平均时间交错发生和其他客户端更新 开始执行。
这些查询交错是否会导致数据竞争 我的意思是说,我们不能说乐观锁自己执行,例如我理解行级锁定发生或其他锁定如表级锁定发生的情况,然后就好了。 但是它像乐观锁一样不能单独工作,它也需要悲观锁(行级/表级,完全取决于底层存储引擎实现)。
当没有Row / table级锁定但希望实现Optimistic Locking策略时会发生什么。使用查询交错将导致表中的数据竞争。(我的意思是说只更新字段而版本不然后发生交错。这完全取决于为查询设置的隔离级别)?
我对这种情况有点困惑。
与悲观锁定相比,乐观锁定真正有用并提高应用程序整体性能的正确用例是什么。
答案 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;
现在,两位客户都会看到此产品,并同时点击订单。系统同时执行两个订单。结果将是:一个订单将被放置,另一个订单将获得异常,因为约束将无法验证,并且事务将被中止。此中止(例外)必须在代码中处理,但不会进行任何进一步的查询或事务。