SQL Update 真的很慢(大约 20-50 秒),Select 不到 1 秒

Posted

技术标签:

【中文标题】SQL Update 真的很慢(大约 20-50 秒),Select 不到 1 秒【英文标题】:SQL Update is really slow (about 20-50sec), Select takes less than 1 second 【发布时间】:2012-12-25 20:09:18 【问题描述】:

我有一个包含很多行(最多几百万行)的 SQL 表“文档”。

当我执行 Select-Statement 时,大约需要 0.5 秒。 但是当我使用相同的 WHERE 子句执行更新时,大约需要 20 到 50 秒,具体取决于受影响的行数。

这是我的数据。

//选择

SELECT * FROM Document 
WHERE (State=20 OR State=23) AND 
LetterClosed IS NOT NULL AND 
TYPE=0 AND
SendLetter=1

//更新

UPDATE Document set State=32 
WHERE (State=20 OR State=23) AND 
LetterClosed IS NOT NULL AND 
TYPE=0 AND
SendLetter=1

OR-Mapper 在内部按如下方式将此更新语句发送到数据库:

exec sp_executesql N'Update
Document
SET
    State=@p4
WHERE
(
  (
    (
      (Document.State = @p0 OR Document.State = @p1) 
      AND Document.LetterClosed IS NOT NULL
    ) 
    AND Document.Type = @p2
  ) 
  AND Document.SendLetter = @p3
)'
,N'@p0 int,@p1 int,@p2 int,@p3 bit,@p4 int',@p0=20,@p1=23,@p2=0,@p3=1,@p4=32

问题是,我在 30 秒后从我的 LightSpeed(C# 中的数据库 OR-Mapper)收到超时异常。

有人可以帮我吗?

编辑:

这是我们由 SQL-Server 自动创建的索引:

CREATE NONCLUSTERED INDEX [_dta_index_Document_9_133575514__K42_1_2_3_4_5_6_7_8_9_11_12_13_14_15_16_17_18_19_20_21_22_23_24_25_26_27_28_29_30_31_32_33_34_] ON [Document] 

(
    [State] ASC
)
INCLUDE ( 
[Id],[DocumentId],[SendLetter],[SendFax],[Archive],[Crm],[Validation],[CreationDate],[PageCount],
[InformationLetter],[TermsOfDelivery],[DeliveryTypeNo],[SeparateDelivery],[FormName],[FormDescription],[TemplateFileName],[RecipientType],
[HealthInsuranceNo],[FamilyHealthInsuranceNo],[PensionInsuranceNo],[EmployerCompanyNo],[RecipientName1],[RecipientName2],[RecipientName3],
[RecipientStreet],[RecipientCountryCode],[RecipientZipCode],[RecipientCity],[RecipientFaxNo],[AuthorId],
[AuthorName],[AuthorEmailAddress],[CostcenterDepartment],[CostcenterDescription],[MandatorNo],[MandatorName],[ControllerId],
[ControllerName],[EditorId],[EditorName],[StateFax],[Editable],[LetterClosedDate],[JobId],[DeliveryId],[DocumentIdExternal],[JobGroupIdExternal],
[GcosyInformed]) WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]
go

CREATE NONCLUSTERED INDEX [_dta_index_Document_9_133575514__K2_1_46] ON [Document] 
(
    [DocumentId] ASC
)
INCLUDE ( [Id],
[JobId]) WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]
go

CREATE NONCLUSTERED INDEX [_dta_index_Document_9_133575514__K46_K2] ON [Document] 
(
    [JobId] ASC,
    [DocumentId] ASC
)WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]
go



CREATE NONCLUSTERED INDEX [Document_State_Id] ON [Document] 
(
    [State] ASC,
    [Id] ASC
)WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]
go

CREATE NONCLUSTERED INDEX [Document_State_CreationDate] ON [Document] 
(
    [State] ASC,
    [CreationDate] ASC
)WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]
go

编辑 2: 现在我有一个图形执行计划: 执行计划:https://skydrive.live.com/redir?resid=597F6CF1AB696567!444&authkey=!ABq72SAWXOoAXfI

执行计划索引更新详情:https://skydrive.live.com/?cid=597f6cf1ab696567&id=597F6CF1AB696567%21445&sff=1&authkey=!ADDPWvxB2JLLvWo

此 SQL 更新执行大约需要 35 秒。通常这个更新只需要 0,3 秒。似乎另一个进程阻止了这个。我看到其他一些选择在此更新中间开始并等到更新完成,直到他们在那里完成选择执行。

所以看起来索引本身是正确的(通常是 0,3 秒执行)。 所有选择(来自 java/jtds、php、.net)都是隔离级别读取提交的(默认)。它会帮助我在这里将所有选择更改为未提交的读取以避免在索引更新期间出现这种阻塞吗?

谢谢 托比

【问题讨论】:

SELECT 返回了多少行?您使用的是什么 DBMS? (我猜来自sp_executesql 的SQL-Server)你能显示CREATE TABLE 语句(和现有索引)吗?可以添加执行计划吗? 我们使用的是 MSSQL-Server 2005。我将尝试发布执行计划和创建语句。 还包括该表上的任何索引,您需要这样做以获得准确的答案,它可能是索引太多或太少,或者您可能只需要稍微改变一个。当您在 SSMS 中获得执行计划时,它甚至可能会建议一个可以加快执行计划的索引。 如果从应用程序和 SSMS 运行查询,执行时间是否相同? 通常人们会附上带有计划的屏幕截图,但它不适用于大计划。 【参考方案1】:

可能您在表 Document 上有索引。索引使选择更快,但更新/插入/删除操作缓慢。

尝试删除不必要的索引。

【讨论】:

可能,但只需要更新一个索引(状态;如果它甚至被索引).. 可能是缺少索引导致这种情况比存在很多。 创建表时,我运行了一些测试,使用分析器记录每条语句,并使用 SQLServer DatabaseOptimizer 创建我的索引。 @pst 为什么“只有一个索引”?您是否假设仅存在单列索引?如果State 是许多索引的一部分怎么办?如果没有我们不知道的计划,但我认为您不能说这只会影响一个索引。 嗨亚伦。我已经发布了 XML 执行计划。也许你可以看看它?它的图形应该更容易阅读。这是链接再次:编辑 2:现在我有一个图形执行计划:执行计划:link 执行计划索引更新详细信息:link【参考方案2】:

没有执行计划,我们只能猜测会发生什么。

我将从:

    检查document 表有多少索引(但很难相信更新索引需要这么长时间)。 检查更新时是否执行了任何触发器。

所有这些都应该在执行计划中可见。

另一个原因可能是 SQL 引擎对SELECT 查询有一个执行计划,而对UPDATE 查询有一个不同的执行计划...

更新

查看索引后。

在我看来索引_dta_index_Document_9_133575514__K42_1_2_3_4_5_6_7_8_9_11_12_13_14_15_16_17_18_19_20_21_22_23_24_25_26_27_28_29_30_31_32_33_34_是完全错误的。

它包含很多列,可能会导致更新缓慢。

尝试将其删除或将其替换为 state 列上的 CLUSTERED 索引。 CLUSTERED index* 包括*(可以直接访问)所有记录列,无需额外读取。

可能它应该与以state 列开头的其他索引之一结合使用——我假设state 只有几个值。

很遗憾,我无法以文本格式解释执行计划。

【讨论】:

这是我的执行计划。 link 必须把它放在一个 txt.file 中 感谢您的回答。你能读懂哪种格式的执行计划?也许我可以将其更改为另一种格式。我将尝试删除所有索引并创建新索引,而不信任 MSSQL 数据库优化器创建的索引;) 图片是最简单的,但它没有提示。我看到该计划可以保存为 XML 文件,但我不确定我能否更好地解释它。查看您的计划,搜索 IO 成本、记录数和读取的字节数,检查您是否没有表扫描(通常是坏事)、索引扫描或 RID 查找——这些都是典型的瓶颈。【参考方案3】:

我曾经在 SQL Server 2008 和 SQL Server 2014 链接服务器上遇到过这个问题。对我来说,一个解决方法是将“选择”结果存储到一个临时表中,并使用它来进行更新,而不是同时进行复杂的查询和更新。

在你的情况下,这将是:

--Select

SELECT * FROM Document
into #temp 
WHERE (State=20 OR State=23) AND 
LetterClosed IS NOT NULL AND 
TYPE=0 AND
SendLetter=1

--Update

UPDATE Document set State=32 
from #temp
WHERE #temp.id = Document.id 
--assuming that id is your PK

【讨论】:

它也对我有用。我不知道为什么?任何人都可以对这种行为有所了解吗?

以上是关于SQL Update 真的很慢(大约 20-50 秒),Select 不到 1 秒的主要内容,如果未能解决你的问题,请参考以下文章

sql查询在Hibernate中很慢,在mysql上很快

在 Android 上部署 Qt 应用程序真的很慢吗?

用默认值替换 UPDATE SQL vs Drop & Add COLUMN?

hsqldb select查询真的很慢

HealthKit 锻炼查询似乎真的很慢

SQL Server 中的插入速度真的很慢