1

目前,我们有一个LOGSTABLE,其中包含多个日志编号(列XLOG),并在有限的时间范围内访问。

该表使用聚集的“自然”主键声明,其中XLOG是日志标识符,XDATE是时间戳,XHW and XCELL是确保日志事件唯一性的硬件标识符:

 CREATE TABLE [dbo].[LOGS](
    [XDATE] [datetime] NOT NULL,
    [XHW] [nvarchar](3) NOT NULL,
    [XCELL] [nvarchar](3) NOT NULL,
    [XALIAS] [nvarchar](255) NULL,
    [XMESSAGE] [nvarchar](255) NULL,
    [XLOG] [int] NOT NULL,
 CONSTRAINT [PK_LOG] PRIMARY KEY CLUSTERED ([XLOG] ASC,[XDATE] ASC,[XHW] ASC,[XCELL] ASC)

问题是当使用相同的查询(例如在下面的示例查询中)访问多个日志时,会出现一个可怕的执行计划XLOG = 1 OR XLOG = 1002,请求 #1 :

SELECT TOP 100 XDATE, XHW, XCELL, XMESSAGE, XLOG FROM LOGS
WHERE XDATE > '2012-06-12T00:00:00' AND XDATE < '2012-07-13T08:29:03.250'
AND (XLOG = 1 OR XLOG = 1002)
ORDER BY XDATE DESC, XLOG DESC

编辑:需要的 100 行不仅来自日志 #1,而且来自两个日志,混合,日期排序。这就是两个查询返回的结果。

统计数据在测试前更新。

实际的执行计划基本上使用聚集索引搜索来获取数百万行数据,其中谓词为XLOGand XDATE(因为我们有 XLOG= 并且我们按 XDATE 排序,所以它可能只获取 100 首/最后一行)实际计划

聚集索引查找操作的详细信息:工具提示

预期的执行计划是预期计划

我试图重写查询,但除了UNION ALL. 生成的查询返回相同的结果(具有正确的计划!),但感觉过于复杂(并且不能通过 XLOG 上的 JOIN 进行调整,但这不是问题)请求 #2:

WITH A AS (SELECT TOP 100 XDATE,  XHW, XCELL, XMESSAGE, XLOG FROM LOGS
WHERE XDATE > '2012-06-12T00:00:00' AND XDATE < '2012-07-13T08:29:03.250'
AND XLOG = 1
ORDER BY XDATE DESC),

B AS (SELECT TOP 100 XDATE,  XHW, XCELL, XMESSAGE, XLOG FROM LOGS
WHERE XDATE > '2012-06-12T00:00:00' AND XDATE < '2012-07-13T08:29:03.250'
AND XLOG = 1002
ORDER BY XDATE DESC)

SELECT TOP 100 * FROM (
    SELECT * FROM A
    UNION ALL 
    SELECT * FROM B 
) A
ORDER BY XDATE DESC, XLOG DESC

问题:请求 #1 有什么问题?在尝试对数百万行进行排序之前,如何重写/修改它以考虑“TOP”?是否需要另一个索引、提示或一些额外的统计信息来解决问题?我有义务重写请求 #2 之类的查询吗?

编辑:从数量上看,这张表包含十几个日志,有些每月只有一个事件,而另一些每月有数百万个事件。

这种查询最常用于此表(还有其他带有额外过滤器的变体,但它们与此问题无关——使用请求 #2 时的复杂性除外)。

编辑#2:我尝试了将聚集索引更改为 (XDATE,XLOG,...) 而不是 (XLOG,XDATE,...) 的解决方案——nb:这种复合主键是这样设计的,因为XLOG 列的选择性。

我在生产数据库的副本上针对只有一千行的日志测试了这个查询:查询计划生成了很多 I/O(它只从XLOG=12广泛的XDATEs 中过滤掉几行)。所以这个特殊的解决方案是不行的。

SELECT TOP 100 XDATE, XHW, XCELL, XMESSAGE, XLOG FROM LOGS
WHERE XDATE > '2012-06-12T00:00:00' AND XDATE < '2012-07-13T08:29:03.250'
AND (XLOG = 12 AND XALIAS LIKE 'KEYWORD%' )
ORDER BY XDATE DESC, XLOG DESC, XHW DESC, XCELL DESC

具有大量 I/O 的漂亮查询计划

PS:顺便说一下,我们在PostgreSQL 9.1中也有同样的行为——所以它与数据库无关,更可能是错误的查询或错误的表设计。

4

2 回答 2

1

问题是数据库不知道您想要的前 100 行都是 XLOG=1,因此必须获取所有可能的 XLOG,然后排序以找到前 100。

在第二种情况下,您已经提供了更多信息或减少了所选行,以便优化器可以只使用索引进行排序。

另一种方法是在 XDATE DESC、XLOG DESC 上创建聚集索引,然后优化器将知道它不必排序并使主键成为哈希或其他索引。如果此查询是最常用的查询,则此查询是有意义的。

于 2012-07-17T09:40:40.037 回答
0

按 XDate 排序导致问题。数据必须按 xdate 排序才能获得前 100 名,这就是为什么你有这种排序。最好的方法是在 xdate、xlog 上建立一个索引。但这会增加开销。当其他事情不起作用时,这应该是一个选项。试试下面的方法。

SELECT TOP 100 XDATE, XHW, XCELL, XMESSAGE, XLOG
into #mytop100
FROM LOGS 
WHERE XDATE > '2012-06-12T00:00:00' AND XDATE < '2012-07-13T08:29:03.250' 
AND (XLOG = 1) 
ORDER BY XDATE DESC
union all
SELECT TOP 100 XDATE, XHW, XCELL, XMESSAGE, XLOG FROM LOGS 
WHERE XDATE > '2012-06-12T00:00:00' AND XDATE < '2012-07-13T08:29:03.250' 
AND (XLOG = 1002) 
ORDER BY XDATE DESC

select TOP 100 XDATE, XHW, XCELL, XMESSAGE, XLOG from #mytop 100 ORDER BY XDATE DESC, XLOG DESC 

还可以尝试将主 sql withwout 放入#mytop100 中,看看它是否选择了一个好的计划。我敢打赌它会但仍然检查。

于 2012-07-17T11:17:24.447 回答