1

我正在使用 MS SQL Server 2008 我有一个不断使用的表(数据总是在更改并插入其中)它现在包含 ~70 Mill 行,我正在尝试使用存储过程对表运行一个简单的查询应该适当几天,

我需要表保持可用,现在我执行了存储过程,过了一会儿,我尝试在表上执行的每个简单的身份查询选择都没有响应/运行太多时间,我破坏了它

我应该怎么办?这是我的存储过程的样子:

 SET NOCOUNT ON;
update SOMETABLE
set
[some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
WHERE 
[some_col] = 243

即使我在 where 子句上尝试它(使用“和”逻辑..):

ID_COL > 57000000 and ID_COL < 60000000 and

它仍然不起作用

BTW- SomeFunction 做了一些简单的数学运算,并在另一个包含大约 300k 项的表中查找行,但从未更改

4

1 回答 1

3

从我的角度来看,您的服务器存在严重的性能问题。即使我们假设查询中没有任何记录

select some_col with (nolock) where id_col between 57000000 and 57001000

在内存中,从磁盘顺序读取几页不应该花费 21 秒(如果它是一个自动标识并且你没有做一些愚蠢的事情,比如添加一个“desc”,那么你在 id_col 上的聚集索引不应该是碎片化的到索引定义)。

但是如果你不能/不会解决这个问题,我的建议是一次以小包进行更新,比如 100-1000 条记录(取决于查找函数消耗的时间)。一项更新/事务不应超过 30 秒。

您会看到每次更新都会对其修改的所有记录保持独占锁定,直到事务完成。如果您不使用显式事务,则每个语句都在单个自动事务上下文中执行,因此当更新语句完成时锁会被释放。

但是您仍然可能会以这种方式遇到死锁,具体取决于其他进程的操作。如果他们一次修改了多个记录,或者即使他们收集并持有多行的读锁,您也可能会出现死锁。

为避免死锁,您的更新语句需要锁定它一次将修改的所有记录。这样做的方法是将单个更新语句(只有几行受 id_col 限制)放在可序列化的事务中,例如

IF @@TRANCOUNT > 0
  -- Error: You are in a transaction context already

SET NOCOUNT ON
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

-- Insert Loop here to work "x" through the id range
  BEGIN TRANSACTION
    UPDATE SOMETABLE
      SET [some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
      WHERE [some_col] = 243 AND id_col BETWEEN x AND x+500 -- or whatever keeps the update in the small timerange
  COMMIT
-- Next loop

-- Get all new records while you where running the loop. If these are too many you may have to paginate this also:
BEGIN TRANSACTION
  UPDATE SOMETABLE
    SET [some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
    WHERE [some_col] = 243 AND id_col >= x
COMMIT

对于每次更新,这将对给定记录进行更新/排他键范围锁定(但仅限于它们,因为您通过聚集索引键限制更新)。它将等待同一记录上的任何其他更新完成,然后获取它的锁(导致所有其他事务阻塞,但仍仅针对给定记录),然后更新记录并释放锁。

最后一个额外的语句很重要,因为它会将键范围锁定到“无穷大”,从而在更新语句运行时甚至阻止在范围末尾的插入。

于 2013-03-18T16:54:29.673 回答