【问题标题】:SQL Query get a lot of timeoutsSQL 查询得到很多超时
【发布时间】:2011-10-07 20:13:05
【问题描述】:

我有一个大型数据库表 (SQL Server 2008),其中存储了我的所有论坛消息(该表目前有超过 450 万个条目)。

这是表架构:

CREATE TABLE [dbo].[ForumMessage](
    [MessageId] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
    [ForumId] [int] NOT NULL,
    [MemberId] [int] NOT NULL,
    [Type] [tinyint] NOT NULL,
    [Status] [tinyint] NOT NULL,
    [Subject] [nvarchar](500) NOT NULL,
    [Body] [text] NOT NULL,
    [Posted] [datetime] NOT NULL,
    [Confirmed] [datetime] NULL,
    [ReplyToMessage] [int] NOT NULL,
    [TotalAnswers] [int] NOT NULL,
    [AvgRateing] [decimal](18, 2) NOT NULL,
    [TotalRated] [int] NOT NULL,
    [ReadCounter] [int] NOT NULL,
 CONSTRAINT [PK_GroupMessage] PRIMARY KEY CLUSTERED 
(
    [MessageId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

我看到不断出现的一个问题是,当我运行选择一条消息及其所有回复的存储过程时,我有时会从 SQL 服务器收到超时错误。

这是我的存储过程:

select fm1.[MessageId]
      ,fm1.[ForumId]
      ,fm1.[MemberId]
      ,fm1.[Type]
      ,fm1.[Status]
      ,fm1.[Subject]
    ,fm1.[Body]
      ,fm1.[Posted]
      ,fm1.[Confirmed]
      ,fm1.[ReplyToMessage]
      ,fm1.[TotalAnswers]
      ,fm1.[AvgRateing]
      ,fm1.[TotalRated]
      ,fm1.[ReadCounter],
     Member.NickName AS MemberNickName, Forum.Name as ForumName
from ForumMessage fm1 LEFT OUTER JOIN
                      Member ON fm1.MemberId = Member.MemberId INNER JOIN
                Forum On fm1.ForumId = Forum.ForumId
where MessageId = @MessageId or ReplyToMessage=@MessageId
order by MessageId 

我得到的错误如下所示:“超时已过期。在操作完成之前超时时间已过或服务器没有响应”

我正在查看执行计划,唯一看起来可疑的是,在 forummessage 表(我不明白为什么,因为我将它设置为集群,所以我希望它会更有效率)。我一直认为,当您搜索聚集索引时,查询应该非常有效。

有没有人知道我可以如何改进这个问题和这个查询以获取消息及其回复?

谢谢。

【问题讨论】:

  • 能否在问题中包含执行计划?
  • 为什么 loeft 加入,不是所有的消息都必须来自成员吗?

标签: sql sql-server stored-procedures timeout


【解决方案1】:

我想到了两个建议:

  • 删除ugly OR 并为条件添加UNION(代码如下)
  • 您必须在ReplyToMessage 上有一个非聚集索引

作为最后的手段,创建一个非聚集索引并将MessageId AND ReplyToMessage 放入其中。 (在这里查看我对另一个问题的回答Why does this Sql Statement (with 2 table joins) takes 5 mins to complete?


代码:

select fm1.[MessageId]
      ,fm1.[ForumId]
      ,fm1.[MemberId]
      ,fm1.[Type]
      ,fm1.[Status]
      ,fm1.[Subject]
    ,fm1.[Body]
      ,fm1.[Posted]
      ,fm1.[Confirmed]
      ,fm1.[ReplyToMessage]
      ,fm1.[TotalAnswers]
      ,fm1.[AvgRateing]
      ,fm1.[TotalRated]
      ,fm1.[ReadCounter],
     Member.NickName AS MemberNickName, Forum.Name as ForumName
from ForumMessage fm1 LEFT OUTER JOIN
                      Member ON fm1.MemberId = Member.MemberId INNER JOIN
                Forum On fm1.ForumId = Forum.ForumId
where MessageId = @MessageId
UNION
select fm1.[MessageId]
      ,fm1.[ForumId]
      ,fm1.[MemberId]
      ,fm1.[Type]
      ,fm1.[Status]
      ,fm1.[Subject]
    ,fm1.[Body]
      ,fm1.[Posted]
      ,fm1.[Confirmed]
      ,fm1.[ReplyToMessage]
      ,fm1.[TotalAnswers]
      ,fm1.[AvgRateing]
      ,fm1.[TotalRated]
      ,fm1.[ReadCounter],
     Member.NickName AS MemberNickName, Forum.Name as ForumName
from ForumMessage fm1 LEFT OUTER JOIN
                      Member ON fm1.MemberId = Member.MemberId INNER JOIN
                Forum On fm1.ForumId = Forum.ForumId
where MessageId = @MessageId
order by MessageId 

【讨论】:

  • 为什么 OR 子句这么“丑”
  • 为什么你觉得'OR'很丑?至于我的方法很好,我会说标准,我错了吗?真的很有趣,我总是对这些简单的约束做同样的“或”。顺便说一句,由于两次 SELECT,您的查询显然会执行两次表扫描!
  • 我实际上是在此之前有工会,而足迹更糟。你确定工会在这里会更好吗?
  • 我也得到(使用联合时):'文本数据类型不能选择为 DISTINCT,因为它不可比较'
  • 像瘟疫一样避免OR在有限的情况下我们必须使用它,而在其他情况下使用OR在计算上是可以接受的,但是一般尽量避免
【解决方案2】:

根据您运行的 MS SQL Server 的版本,您还可以尝试使用分区表重新创建表以提高SELECT 的性能。

【讨论】:

  • 如果我添加分区,这会影响检索旧消息的性能吗?
  • 不应该,但它确实决定了您的分区方案。请记住,您需要重建该表。如果您查看 MSDN 的文档,我相信它们会显示删除表上的任何约束、重命名表、创建新的分区表、然后移动数据、重新创建约束,最后删除旧表。跨度>
【解决方案3】:

ReplyToMessage上创建索引:

CREATE INDEX
        IX_ForumMessage_ReplyToMessage
ON      ForumMessage (ReplyToMessage)

这很可能会导致两个索引查找(通过 MessageId 上的 PRIMARY KEYReplyToMessage 上的索引)并包含合并或哈希连接,而不是您现在进行的全表扫描.

【讨论】:

  • @OrA:这在您的脚本中并不明显。请发布查询计划:运行SET SHOWPLAN_TEXT ON,然后在同一会话中运行您的查询。
  • 我实际上是在运行你的脚本,无论出于何种原因,它把 87% 降到了 14% !!!好多了!我应该尝试将其降低还是这是一个“可敬”的数字?
  • 另外,现在它向我展示了成员表上的聚集索引扫描(在 MemberId 列上具有聚集索引,它也是标识),占 37%,无论如何也可以改进它?
  • @ora:我们可以先发布查询计划吗?我的第一条评论解释了如何做到这一点。
【解决方案4】:

你为什么要ORDER BY MessageId,有必要订购吗?

尝试将您的SELECT 重构为SELECT FROM Forum,然后加入Member,最后加入LEFT JOIN ForumMessage。所以从小到大排列桌子

【讨论】:

  • 是的,我必须以正确的顺序返回它们以确保正确解析数据。
  • 我只是想知道您如何使用消息 ID,我相信任何逻辑都不应该引用此 ID
猜你喜欢
  • 2011-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多