0

我们有一个 C# 系统抛出了一个我们无法用 SQL (SQL2k5) 深入了解的奇怪现象。

情况是我们有两个独立的进程同时运行,查看同一个表,它们都在两个不同集群上的两个不同服务 COM+ 组件中运行在自己的事务中。Order两者都在与OrderItemSQL 表交谈。两者都在READ COMMITTED隔离级别运行。

工作 1:将一个订单和一个或多个项目加载到这些表中。

作业 2:每隔几分钟发生的计划任务,从所述表中选择所有已完全处理的订单。

问题:我们发现,在过去的八个月中,工作 2 有四次对来自工作 1 的部分加载的订单做出反应,而工作 1 尚未完全完成(因为订单上的一个项目没有库存,它在完成状态,因此作业会看到订单的其余部分尚未加载)。

根据调查,虽然工作 1 正在进行中,但我们无法做到:

从订单中选择 *

一旦订单被插入其中。但是,我们可以这样做:

select * fromOrder o inner join OrderItem oi on oi.orderid = o.id

同时(这是为什么?)。

在复制相同情况的调查中,我们无法让工作 2 看到来自工作 1 的部分创建的订单,它确实返回了一些行,但从不返回工作 1 插入的订单。然而,这正是我们生活环境中发生的事情!

有谁知道为什么会发生这种情况?

4

2 回答 2

0

问题在于,当您打开事务并添加几行时,这些行会被锁定,直到事务提交。

当您SELECT *执行包含锁定行的选择时,因此在它允许您选择之前,您必须通过提交或回滚事务来解锁它们。

于 2012-04-30T10:15:15.130 回答
0

我会尝试回滚作业 1 执行的一些工作作为实验,以确认您认为包含在事务中的所有内容在回滚发生时实际上已被删除。我和你一样,会怀疑你永远无法从不同的事务中读取数据,这些数据在使用 READ COMMITTED 时会被回滚删除。您能够读取行的事实表明其中一些可能超出了事务的上下文。

我的下一个想法是回顾 READ COMMITTED 的含义。这种隔离级别应该确保您正在读取的数据已由一个事务提交,但不一定确保它仍然不会因另一个事务而更改。将此与 REPEATABLE READ 进行对比,其中所有数据保持锁定直到事务结束,或 SERIALIZABLE,它试图表现得好像所有事务都按顺序执行。我怀疑如果您将锁定更改为 SERIALIZABLE(最严格的),则不会发生此特定问题,但这可能是矫枉过正。

在考虑了 READ COMMITTED 与其他隔离级别相比的含义之后,我怀疑您看到的行为在此隔离级别中是自然的,因为您插入的行实际上将是在末尾仍然存在的行交易。我认为 READ COMMITTED 并不是为了确保查询返回一组完整的行(这将需要由 SERIALIZABLE 施加的范围锁),而只是为了确保您正在阅读的各个行已被完全插入(所以你'不读取部分行)。我可能是错的,但鉴于你描述的行为,这是我的怀疑。如果您需要确保整组行是完整的,我认为您需要 SERIALIZABLE 隔离。

哪个事务需要 SERIALIZABLE?显然,如果它们都是可序列化的,那应该可以。但是有可能只使其中一个成为 SERIALIZABLE 并且仍然获得正确的行为。我目前的精神力量无法完成确定真正需要可序列化的任务,但也许其他人的一些评论会带来更多的启示。

于 2010-02-24T12:29:20.473 回答