【问题标题】:Why is SQL Server 2008 blocking SELECT's on long transaction INSERT's?为什么 SQL Server 2008 在长事务 INSERT 上阻止 SELECT?
【发布时间】:2009-04-27 04:28:55
【问题描述】:

我们正在尝试创建一个只接受定期插入的新记录的事务表。

这个简单的表需要我们随着时间的推移不断地向它添加新记录。此表中的交易量预计会非常高,并且可能会定期批量导入交易(>1000),这可能需要几秒钟才能完成。

然后我们根据这些数据执行一组选择语句,将不同的列分组以返回所需的值。

从我们最初的测试中,我们发现了一个与 SQL Server 相关的瓶颈,它在 INSERTS 事务的中间阻塞了我们的 SELECT。

下面是一个简单的例子,可以用来说明问题。

-- 简单数据库表

create table LOCK_TEST (
LOCK_TEST_ID int identity ,
AMOUNT int);

-- 在 1 个查询窗口中运行它

begin tran
insert into LOCK_TEST (AMOUNT) values (1);
WAITFOR DELAY '00:00:15' ---- 15 Second Delay
insert into LOCK_TEST (AMOUNT) values (1);
commit

-- 在查询 2 中并行运行

select SUM(AMOUNT)
from LOCK_TEST;

我希望查询 2 立即返回,在查询 1 完成之前返回 0,然后显示 2。我们绝不希望看到从第二个查询返回 1。

我们看到的答案与 select 语句中的 WITH (NOLOCK) 相关。但这违反了交易边界,返回的信息可能是财务性质的,我们不希望在查询中看到任何未提交的细节。

我的问题似乎出在 INSERT 方面...
为什么 INSERT 会阻止 SELECT 语句,即使它没有修改任何现有数据?

加分问题:这是 SQL Server 的“功能”,还是我们也会在其他数据库类型中找到此功能?

更新 我现在有时间找到一个本地 oracle 数据库并运行相同的简单测试。正如我所料,这次测试通过了。

也就是说,我可以根据需要多次运行查询,它会在第一个事务提交之前返回 null,然后返回 2。

有没有办法让 SQL Server 像这样工作?还是我们需要迁移到 Oracle?

【问题讨论】:

  • 这些表上有哪些索引?你们有定期的索引维护吗?
  • 目前没有索引。看这篇博文中列出的简单例子,无非就是简单的2列表,基本的insert和select都是用来重现这个问题的。

标签: sql-server sql-server-2008


【解决方案1】:

这种锁定行为是 SQL Server 的一项功能。 对于 2005 及更高版本,您可以使用 row level versioning(这是 Oracle 默认使用的)来获得相同的结果并且不会阻止您的选择。这会给 tempdb 带来额外的压力,因为 tempdb 维护行级别的版本控制,因此请确保您适应这一点。 要使 SQL 按您希望的方式运行,请运行以下命令:

ALTER DATABASE MyDatabase
SET ALLOW_SNAPSHOT_ISOLATION ON

ALTER DATABASE MyDatabase
SET READ_COMMITTED_SNAPSHOT ON

【讨论】:

    【解决方案2】:

    这在 SQL Server 和 Sybase 中是完全标准的行为(至少)。

    数据更改(插入、更新、删除)需要exclusive locks。这会导致读者被屏蔽:

    使用排他 (X) 锁,没有其他 事务可以修改数据;读 操作只能发生 使用 NOLOCK 提示或阅读 未提交的隔离级别。

    在 SQL Server 2005 及更高版本中,他们引入了快照隔离(也称为行版本控制)。来自 MS BOL:Understanding Row Versioning-Based Isolation Levels。这意味着读者有最新提交的数据,但如果你想写回,比如说,你可能会发现数据是错误的,因为它在阻塞事务中发生了变化。

    现在,这就是最佳做法是缩短交易时间的原因。例如,仅在 BEGIN/COMMIT 之间处理您需要的内容,不要从触发器发送电子邮件等。

    编辑:

    这样的锁定在 SQL Server 中一直发生。但是,锁定时间过长会变成阻塞,从而降低性能。显然,太长是主观的。

    【讨论】:

    • 事务很短,但它们的数量会导致根本不需要的阻塞。请参阅有关 Oracle 的问题的评论。
    • 如果需要,请移至 Oracle。如果您可以简单地迁移到 Oracle,那么我怀疑您的 SQL 不是最佳的,您是从客户端到服务器的往返,或者导致过多阻塞的事情。
    • 这么简单的例子怎么可能不是最优的呢?从提供的简单示例来看,它仍然阻塞。您能否建议我如何更改提供的示例以使其最佳?请注意,这些数据永远不会更新,只会有新的插入。
    • 另外,您可以根据需要在 SQL Server 中使用快照隔离。这模仿了 Oracle 的行为。
    • 感谢会调查它。数据库中“快照”的概念让我有点害怕。但我会看看它能提供什么。
    【解决方案3】:

    这也存在于 MySQL 中。这是它背后的逻辑:如果您对某些数据执行 SELECT,您希望它对最新的数据集进行操作。这就是 INSERT 导致表级锁定的原因(除非引擎能够执行行级锁定,就像 innodb 一样)。有一些方法可以禁用这种行为,以便您可以进行“脏读”,但它们都是特定于软件(甚至是数据库引擎)的。

    【讨论】:

    • 我同意,但我也希望它确认交易。从我的角度来看,新记录(交易中的那些)不存在,而且可能永远不会存在。为什么数据库阻塞(从我的角度来看)不存在的数据似乎是错误的。我知道,如果我在一个地方更新数据并从其他地方选择它,我可能想等待更新,但我想控制它……尤其是在插入时。甲骨文似乎如我所料。如果 SQL Server 可以,仍在尝试解决。
    【解决方案4】:

    我也遇到了这个问题。经过调查,似乎不是数据本身被锁定,而是由于插入而被修改的聚集索引。由于聚集索引驻留在数据页上,因此关联的数据行也被锁定。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-23
      • 2021-01-07
      • 1970-01-01
      相关资源
      最近更新 更多