mysql - 关系数据库竞争条件

标签 mysql sql ruby-on-rails ruby postgresql

我正在使用 Ruby On Rails(但这并不重要)和 SQL 后端,MySQL 或 Postgres。
Web 应用程序将是多进程的,有一组应用程序服务器进程在同一个数据库上运行和工作。

我想知道:是否有任何好的通用策略来处理比赛条件?

由于这将是一个数据库密集型应用程序,我可以很容易地看到两个客户端如何同时尝试修改相同的数据。

让我们简化一下情况:

  • 两个客户/用户获取相同的数据,这是否同时发生并不重要。
  • 它们使用表示相同数据的两个网页提供服务。
  • 后来他们都尝试对同一条记录进行一些不兼容的修改。

有没有一种简单的方法来处理这种情况?

我正在考虑使用与每条记录关联的 id-tokens。此 token 将在记录更新时更改,从而使基于陈旧数据(旧的过期 token )的任何后续更新尝试无效。

有没有更好的方法?也许已经在 MySQL 中构建了一些东西? 我也对这种情况下使用的编码模式感兴趣。

谢谢

最佳答案

乐观锁

在 webapps 中处理这个问题的标准方法是使用所谓的“乐观锁定”。

每条记录都有一个唯一的ID和一个整数(或时间戳,但整数更好)乐观锁字段。此 oplock 字段在记录创建时初始化为 0。

当您获得记录时,您将获得 oplock 字段。

当您设置记录时,您将 oplock 值设置为您使用 SELECT 加一检索到的 oplock,并使 UPDATE 以 oplock 值仍然是什么为条件这是你最后一次看的时候:

UPDATE thetable
SET field1 = ...,
    field2 = ...,
    oplock = 1
WHERE record_id = ...
  AND oplock = 0;

如果你输掉了与另一个 session 的比赛,这个语句仍然会成功,但它会报告零行受影响。这允许您告诉用户他们的更改与另一个用户的更改发生冲突,或者合并他们的更改并重新发送,具体取决于应用的该部分的意义。

许多框架都提供工具来帮助自动执行此操作,而且大多数 ORM 都可以开箱即用。 Ruby on Rails supports optimistic locking .

为传统应用程序组合乐观锁定和悲观锁定(如下所述)时要小心。它可以工作,您只需要在所有可乐观锁定的表上添加一个触发器,如果​​ UPDATE 语句本身没有这样做,它会增加 UPDATE 上的 oplock 列。我 wrote a PostgreSQL trigger for Hibernate oplock support应该很容易适应 Rails。仅当您要从 Rails 外部更新数据库时才需要它,但在我看来,确保安全始终是个好主意。

悲观锁

更传统的方法是开始事务并在获取要修改的记录时执行 SELECT ... FOR UPDATE。然后,当用户思考他们将要做什么并在 COMMIT 之前对已锁定的记录发出 UPDATE 时,您将事务保持打开和空闲状态。

这不是很好,我不推荐它。它要求每个用户都有一个开放的、通常是空闲的交易。这可能会导致 PostgreSQL 中的 MVCC 行清理出现问题,并可能导致应用程序中出现锁定问题。对于用户数量多的大型应用程序,它的效率也非常低。

插入比赛

处理 INSERT 上的竞争需要您在表上有一个合适的应用程序级唯一键,因此当它们发生冲突时插入会失败。

关于mysql - 关系数据库竞争条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16027155/

相关文章:

php - 如何将相同类型的查询与不同的变量结合起来

ruby-on-rails - 你如何 stub /期待一个状态机事件?

php - 更新列的 SQL 语句

MySQL 表损坏

mysql - 根据ID从多个表中获取数据

mysql - 查找两个表中的值

php - 出现 MySQL 错误 "unknown column"但该列存在

sql - 如何在子查询中使用列的值?

ruby-on-rails - Delayed_job 与 Appoxy SimpleWorker

javascript - 输入 : hover 上的 Bootstrap Popover