【问题标题】:How to ensure data consistency across multiple related tables with concurrent queries如何通过并发查询确保跨多个相关表的数据一致性
【发布时间】:2019-12-12 11:13:19
【问题描述】:

所以,我的数据模型类似于分配问题。因此,假设我们有一家公司,可以为所要求的工作提供合适的工人。

目前,我有这样的关系:

  • Customer(身份证)
  • Job(身份证)
  • Worker(id,可用)
  • Jobs In Progress (customer_id, job_id)
  • Busy Workers (customer_id, worker_id)

CustomerJob 之间存在多对多,CustomerWorker 之间存在多对多。这些数据是实时的,因此是高度动态的。

我们想要维护这样的查询:

  1. 向工作人员申请工作。
  2. 工作完成后返回工人。

此查询需要读取、更新、删除和插入多个表中的数据。

例如,如果客户请求一个工人,我们要检查这个客户是否已经存在于表中;他是否已经在Busy Workers拥有合适的工人;如果没有,请在Worker 中找到合适的可用工人;检查此类工作是否已在Job 中注册。而在最坏的情况下,我们必须原子地在Customer中插入customer,在Job中插入job,在Jobs In Progress中插入相应的行,在Worker.avaiable中递减一行并在Busy Workers中插入一行。

在第二个查询中,我们必须以相反的顺序执行所有这些操作:递增 Worker.available,如果客户没有工作则删除,如果没有客户需要则删除工作,等等。

所以我们有很多一致性规则:忙碌工作人员的数量必须与Worker.available 一致,只有当客户请求未完成的工作时,他才必须出现在表中,工作必须出现在仅当没有客户请求此类工作时才使用该表。

我阅读了很多关于数据库中的隔离级别和锁定的信息,但我仍然不明白如何确保跨多个表的一致性。似乎隔离级别不起作用,因为涉及多个表,并且从两个表中选择的数据可能会变得不一致。而且锁似乎也不起作用,因为 AFAIK SQL Server 无法原子地获取多个表上的锁,因此锁之间的数据可能会变得不一致。

实际上,我正在寻找一个解决方案或解决方案的想法,而不参考具体的 RDBMS,它应该以一种或另一种方式适用于最著名的 RDBMS,如 MySQL、PostgreSQL、SQL服务器和甲骨文。因此,它不一定是包含所有这些 RDMS 示例的适当解决方案,也许是一些实践、提示或 参考文献。

我为我的英语道歉,提前谢谢你。

【问题讨论】:

  • 你的问题实在是太笼统了。您希望将数据存储在一个地方,如果需要保持更新,请使用触发器,并在需要时计算值。设计一个完整的预订和分配系统是一项艰巨的任务。
  • @GordonLinoff 好吧,我认为我的问题可以简化为“如何确保 2 个相关表之间的一致性”。我相信,如果我可以用 2 张桌子做事,那么我可以把它传播到 n 桌子上。但我什至不知道如果有 2 张桌子该怎么办。我在问题中描述的隔离和锁仍然存在问题。但我可能会出错,我对隔离和锁的理解可能是错误的,我很确定我是因为并发访问多个表并不是我想的那么罕见的任务。

标签: sql database concurrency transactions consistency


【解决方案1】:

首先:多考虑一下您的模型。我不会保留这么多冗余信息。 “减少 Worker.avaiable 并在 Busy Workers 中插入一行”是完全多余的,因为您可以通过询问其他表轻松获取信息。您可能会说查询成本更高。我称之为过早优化。冗余本身的成本很高。

第二:将锁视为只有一个人可以获得的独占资源。因此,确保一致性的最简单方法是让所有 dbms 用户使用 select ... for update 仅锁定数据库中的一条记录。所有更改都将被序列化。如果你使用像 postgres、oracle 甚至 sql-server 这样的 MVCC-Dbms,读者总是会看到一致的情况。

第三:做你的改变也许你只需要检测,如果另一个用户/交易已经改变了某个记录。这可以通过维护所谓的版本属性并在更新期间检查(如果这些属性发生更改)来完成。如果检测到更改,您必须重复整个事务。这就是所谓的乐观锁定。

第四点或更好的是最重要的一点:我希望您理解 dbms-transaction 的概念,它是一种将 dbms 从一个一致状态带入另一个的手段。

【讨论】:

  • 感谢您的回答!第一:我考虑过,但在我看来,这种方式控制并发会更困难,但也许我错了。我再想想。第二:是的,此时我尝试使用for update 来实现它。它会导致死锁,但无论如何,我想我会解决它。第三:我也会考虑的。我考虑过时间戳排序,但它看起来不适用于我的情况,也许有了新知识我会改变主意。
猜你喜欢
  • 2017-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 2022-01-14
  • 2021-05-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多