为什么SQL Server 2008在长事务INSERT上阻止了SELECT?

时间:2009-04-27 04:28:56

标签: sql-server sql-server-2008

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

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

然后,我们从这些数据中执行一组select语句,对不同的列进行分组以返回所需的值。

从我们的初始测试开始,我们发现了一个与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立即返回,0直到查询1完成,然后显示2.我们永远不希望看到第二个查询返回1。

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

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

加分点问题:这是SQL Server的“功能”,还是我们会在其他数据库版本上找到它?

更新 我现在有时间找到一个本地oracle数据库并运行相同的简单测试。这个测试通过我的预期。

即我可以根据需要经常运行查询,并且在第一个事务提交之前返回null,然后返回2.

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

4 个答案:

答案 0 :(得分:12)

此锁定行为是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

答案 1 :(得分:4)

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

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

  

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

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

现在,这就是为什么最佳做法是保持交易简短。例如,只处理BEGIN / COMMIT之间需要的内容,不要从触发器等发送电子邮件。

编辑:

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

答案 2 :(得分:1)

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

答案 3 :(得分:1)

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