我正在使用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项目的表中查找行,但永远不会更改
答案 0 :(得分:3)
从我的角度来看,您的服务器存在严重的性能问题。即使我们假设查询中没有任何记录
select some_col with (nolock) where id_col between 57000000 and 57001000
在内存中,从磁盘顺序读取几页不应该花费21秒(如果它是自动标识,你的id_col上的聚簇索引不应该被分段,你没有像添加一样愚蠢“desc”到索引定义)。
但是如果你不能/不会解决这个问题,我的建议是一次在100-1000条记录的小包中进行更新(取决于查找函数消耗的时间)。一次更新/交易不应超过30秒。
您会看到每个更新都会对其修改的所有记录保持独占锁定,直到事务完成为止。如果不使用显式事务,则每个语句都在单个自动事务上下文中执行,因此在更新语句完成时会释放锁。
但是你仍然可以通过这种方式遇到死锁,具体取决于其他进程的作用。如果他们一次修改多个记录,或者即使他们在几行上收集并保持读锁,也会导致死锁。
为避免死锁,您的update语句需要锁定它将立即修改的所有记录。这样做的方法是在可序列化的事务中放置单个更新语句(只有少量行由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
对于每次更新,这将对给定记录执行更新/独占键范围锁定(但仅限于它们,因为您通过聚簇索引键限制更新)。它将等待相同记录上的任何其他更新完成,然后获取它的锁定(导致阻止所有其他事务,但仍仅针对给定记录),然后更新记录并释放锁定。
最后一个额外的语句很重要,因为它会将键范围锁定为“无穷大”,从而防止在更新语句运行时偶然插入范围的末尾。