【问题标题】:Lock selected rows, so that other transactions ignore them on read锁定选定的行,以便其他事务在读取时忽略它们
【发布时间】:2018-02-06 10:05:45
【问题描述】:

这个问题是关于优化的。 有问题的场景如下:

我有一个表,其中包含多个事务应读取和执行的数据。该表由三列组成:{id, message, status}。

ID   MESSAGE          STATUS
1    First Message    NEW 
2    Second Message   MOD
3    Third Message    FIN
....

如图所示,状态是以下三种状态之一:{new, mod, fin}。它们指示一行是新的、此时正在处理还是已成功处理。 我这样做是为了让多个事务在这个表上工作。事务读取所有“新”消息并将状态更改为“MOD”,以指示它们正在被处理并锁定它们。之后,它们被处理。成功处理的行将设置为“FIN”,失败的行将设置为“NEW”,以便下一个事务将再次尝试这些。

除了状态的改变,该表是只读的。要处理的准确信息永远不会改变。

上述过程有效,但我认为它不必要地复杂。我在这里寻找的是一个更简单的解决方案。我已经阅读了不同的隔离级别,但它们基本上都会导致行以某种方式被锁定,以便其他事务必须等待。我不想要那个。相反,我希望那些已经读取的行被其他查询忽略。

现在终于来了一个问题: 是否可以在读取时锁定行,以便它们将被其他事务忽略,而不是等待这些行被解锁的事务?

我想要这样的东西: 假设该表包含 30 行。

  • 事务“reader1”读取 30 行,(id 1-30)。这些行现在已锁定(最好对其他人不可见)。
  • 事务“writer”插入 20 个新行。 (id 31-50)
  • 此时,“reader1”尚未完成。无需等待“reader1”完成,事务“reader2”只读取新的 20 行,因为其他行(1-30)被“reader1”隐藏
  • “reader1”和“reader2”完成。它们完成的顺序无关紧要,因为它们处理分离的数据集。

有没有办法在不需要状态栏的情况下做到这一点?


  • 使用的 DBMS 是 IBM 的 DB2
  • 通过 ODBC 访问

【问题讨论】:

  • 您使用的是哪个 dbms?为什么一个用户读取的行对其他用户是不可见的?
  • 这可能取决于您使用的 DBMS,以及它如何处理锁争用(有各种策略,例如脏读、最后一次清读等),这些通常可以配置。然而,这超出了关系理论甚至 SQL 标准,并且将是特定于实现的。
  • @jarlh 我添加了一些信息。我想这样做,以便每一行只处理一次,这样我就不必让我的状态表明当时正在处理哪一行。我想尽量减少写作和阅读。
  • @Rags 添加到问题中,DBMS 是 DB2
  • 添加 Db2 服务器(Z/os、i 系列、Linux/Unix/Windows)的平台和 Db2 的版本 -服务器软件。

标签: sql database multithreading transactions db2


【解决方案1】:

有没有办法在不需要状态栏的情况下做到这一点?

简而言之,没有,但不是因为锁定/并行性问题。

实际上,您只需要该状态列,以便在您的处理系统最终出现故障时,您知道在系统恢复时需要再次处理哪些行。它实际上使并行工作变得更容易是一个附带好处。

至于你的其他问题:

我已经阅读了不同的隔离级别,但它们基本上都会导致行以某种方式被锁定,以便其他事务必须等待。我不想要那个。相反,我希望那些已经读取的行被其他查询忽略。

...嗯,你很幸运,因为无论如何这都不是你想要发生的。您的主要问题是,为了锁定具有 only 读取的行,您不仅必须阻止其他进程读取这些行(因为只读锁通常是共享的),您还必须阻止行从被插入为好。但是,如果相反,您使用 write 锁定一行,则交错进程会变得非常简单和容易。

你只需要Cursor Stability:

但是,如果行中的任何数据发生更改,则锁定将一直保持,直到提交更改。

在此隔离级别下,当可更新游标位于该行上时,其他应用程序无法更新或删除该行。在 CS 下,无法访问其他应用程序的未提交数据。 但是,不可重复读取和幻读是可能的。

...允许其他处理器更新类似数据是不可重复读取的意义所在。我们实际上希望它们发生!我们不希望幻读(如果两行读取相同的数据然后尝试更新它会发生这种情况),但我们可以围绕它设计我们的应用程序。

执行此操作的一种方法是启动您的工作单元(这可能是隐式的或显式的,具体取决于平台),然后发出更新语句:

UPDATE Transactions SET status = 'MOD'
WHERE status = 'NEW'

然后只需检索您更新的行,以便您知道要处理什么:

SELECT id, message
FROM Transactions
WHERE status = 'MOD'

将它们标记为已完成:

UPDATE Transaction SET status = 'FIN'
WHERE status = 'MOD'

最后提交更改:

COMMIT

(注意这个版本有一个小弱点——如果更新到'FIN'之后提交之前系统宕机,行会被重置为'NEW',这可能是它自己的问题。但至少@ 987654329@会被正确回滚)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-06
    • 1970-01-01
    • 2019-08-21
    • 1970-01-01
    • 2019-04-25
    • 2016-05-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多