数据库隔离级别和读写问题

Database IsolationLevel and read write problem

我一直在阅读有关数据库隔离级别和 TransactionScope here, here and here 的内容,但 none 似乎回答了我的问题。我遇到的问题是一个简单的读写问题。

下面描述一个具体的场景

  1. process1 读取初始状态:ReadyForShipment
  2. process2 读取状态:ReadyForShipment
  3. process1 将状态更改为 Canceled 并提交其事务
  4. process2 将状态变为已发货,这是无效的,因为不应发货已取消的项目。

进程 1 和进程 2 不相互通信,我希望有一个数据库级别的解决方案来保持这种状态。我知道Isolation level Serializable解决了这个问题,因为step2获取的读锁阻止step3成功。

为了找到限制较少的隔离级别,我还阅读了 ReadCommitted 和行版本控制。根据 here

中的这段文字

Locking and row versioning prevent users from reading uncommitted data and prevent multiple users from attempting to change the same data at the same time. Without locking or row versioning, queries executed against that data could produce unexpected results by returning data that has not yet been committed in the database

这似乎暗示行版本控制可以解决读写问题。在第 4 步中,通过行版本控制,数据库应该能够检测到它正在尝试更改自第 2 步中读取以来版本已经更改的行。但是我的实验证明这不是行为。 在 ReadCommited 隔离和数据库的 READ_COMMITTED_SNAPSHO 设置为 ON 的情况下,step4 成功,状态更新为 Shipped。

我的问题是,除了隔离级别Serializable之外,是否还有其他数据库级别的解决方案来解决上述读写问题?

SQL 服务器中的行版本控制是(这等同于其他数据库中多版本并发控制 MVCC 的工作方式)- 对于每个更改的行,它维护一个单独的版本,以便如果有该行的读取请求 - 它利用该版本而不是引用尚未提交的行。这是实现并发的更好方法,因为读取然后不需要锁定,因此在所有主要数据库中都实现了。现在您可以明白为什么使用行版本控制(具有基于语句或事务级别的读取一致性)只能为您提供一致读取的保证(使用尚未提交的事务开始发生更改之前的数据版本)。

如果您正在寻找纯数据库方面的解决方案,我认为可序列化隔离级别是您最好的选择。假设没有多少事务同时处理同一个数据行,那么您持有锁的时间可能会很短。

另一种解决方案是使用 table 中的版本列来使用乐观并发控制。后面的事务将在更新子句中包含 "where version =1",这将更新 return 0 行,因为第一个事务已经将版本增加到 2。这可以被视为应用程序端的逻辑错误,并相应地传播消息。