我正在练习一些"系统设计"编码问题,我对如何解决MySQL中的并发问题感兴趣。问题是"设计库存结帐系统"。
假设您正在尝试从库存中查看特定物品,例如图书馆书。
如果有两个人在网站上,想预定它,他们是否有可能签出?假设查询正在更新行的状态,以将布尔 checked_out
标记为 True
。
交易会解决此问题吗?这将导致第二个查询失败(假设它们是同一查询)。
另外,我们将行插入checkouts
表中。由于两个查询都读到目前未检查该项目,因此它们都可以插入表中。我认为交易不会解决此问题,除非交易包括阅读表以查看目前尚未结束的该项目是否存在结帐。
建议的方法之一
我将如何同时模拟两个写作?
否,仅交易不会解决并发问题。让我们快速重新审视mysql的交易定义:
交易是可以承诺或回滚的工作单位。当交易对数据库进行多次更改时,要么在进行交易时所有更改成功,要么在交易回滚时都取消所有更改。
总结一下:交易是确保数据完整性的一种方式。
RDBMS使用各种类型的锁定,隔离级别和存储引擎级解决方案来解决并发。人们经常将交易误认为是控制并发性的均值
专注于InnoDB:当您发布update
语句时,MySQL在更新的记录上放置了一个独家锁定。只有持有独家锁的交易才能修改给定记录,其他人必须等到交易进行。
这如何帮助您防止多个用户查看同一本书?假设您有一个id
字段,可以唯一地识别书籍和一个指示本书状态的checked_out
字段。
您可以使用以下原子update
查看一本书:
update books set checked_out=1 where id=xxx and checked_out=0
checked_out=0
标准确保update
仅在未签出本书时才成功。因此,如果以上语句影响一行,则当前用户会查看本书。如果它不影响任何行,那么其他人已经检查了这本书。独家锁定确保只有一项交易可以在任何给定时间更新记录,从而序列访问该记录的访问。
如果您想使用单独的checkouts
表作为保留书籍,则可以在书ID上使用唯一的索引来防止被检查一次以上的同一本书。
交易不会导致更新失败。它们导致查询序列被序列化。只有一个访问者可以运行查询的顺序;其他等待。
SQL中的所有内容都是交易,单键更新操作。BEGIN TRANSACTION; ... COMMIT;
表示的交易类型捆绑在一起。
我认为交易不会解决此问题,除非交易 包括阅读表以查看当前是否存在结帐 这个项目。
这通常是正确的。结帐方案必须始终从数据库中读取可用性。交易的目的是避免当多个用户尝试查看同一项目时避免种族条件。
SQL没有诸如多线程处理器内核之类的线程安全测试和集合指令。因此,您需要将交易用于这种事情。
最简单的结帐形式使用交易,类似的东西。
BEGIN TRANSACTION;
SELECT is_item_available, id FROM item WHERE catalog_number = whatever FOR UPDATE;
/* if the item is not available, tell the user and commit the transaction without update*/
UPDATE item SET is_item_available = 0 WHERE id = itemIdPreviouslySelected;
/* tell the user the checkout succeeded. */
COMMIT;
显然,两个或多个用户可以尝试同时尝试同时查看同一项目。但是他们中只有一个实际上得到了项目。
此处未详细介绍的更复杂的结帐方案使用了两步系统。第一步:为用户保留项目的交易,如果其他人已将其签出或保留,则拒绝预订。第二步:预订持有人有固定的时间接受预订并查看物品,或者预订到期,其他一些用户可以保留该物品。