2009-11-20 91 views
1

访问我的一个表时,我需要一些重新分解咨询我遇到了巨大的进展缓慢。对不起,如果这不是这种事情的正确领域。大表建议(SQL Server)的

我工作的一个项目,旨在为我们的内部服务器的服务器性能统计报表。我每天晚上处理Windows性能日志(12台服务器,10台性能计数器和每15秒记录一次)。我将数据存储在一个表中,如下所示:

CREATE TABLE [dbo].[log](
    [id] [int] IDENTITY(1,1) NOT NULL, 
    [logfile_id] [int] NOT NULL, 
    [test_id] [int] NOT NULL, 
    [timestamp] [datetime] NOT NULL, 
    [value] [float] NOT NULL, 
CONSTRAINT [PK_log] PRIMARY KEY CLUSTERED 
(
    [id] ASC 
)WITH FILLFACTOR = 90 ON [PRIMARY] 
) ON [PRIMARY] 

目前有16,529,131行,它将继续增长。

我访问以产生报告和创建从ColdFusion的曲线图,像这样的数据:

SET NOCOUNT ON 

CREATE TABLE ##RowNumber (RowNumber int IDENTITY (1, 1), log_id char(9)) 

INSERT ##RowNumber (log_id) 
SELECT l.id 
FROM log l, logfile lf 
WHERE lf.server_id = #arguments.server_id# 
and l.test_id = #arguments.test_id#" 
and l.timestamp >= #arguments.report_from# 
and l.timestamp < #arguments.report_to# 
and l.logfile_id = lf.id 
order by l.timestamp asc 

select rn.RowNumber, l.value, l.timestamp 
from log l, logfile lf, ##RowNumber rn 
where lf.server_id = #arguments.server_id# 
and l.test_id = #arguments.test_id# 
and l.logfile_id = lf.id 
and rn.log_id = l.id 
and ((rn.rownumber % #modu# = 0) or (rn.rownumber = 1)) 
order by l.timestamp asc 

DROP TABLE ##RowNumber 

SET NOCOUNT OFF 

(不CF开发者#value#插入value##映射到#

我基本上创建临时表以便我可以使用rownumber来选择每x行。通过这种方式,我只选择了我可以显示的行数。这有帮助,但它仍然非常缓慢。

SQL Server Management Studio中告诉我,我的索引的情况如下(我对使用几乎没有知识指数的正常):

IX_logfile_id (Non-Unique, Non-Clustered) 
IX_test_id (Non-Unique, Non-Clustered) 
IX_timestamp (Non-Unique, Non-Clustered) 
PK_log (Clustered) 

我会很感激的人谁可以给一些建议,可以帮助我加快了一点。我不介意重新组织东西,并且我完全控制了这个项目(尽管可能不在服务器硬件上)。

干杯(抱歉长后)

+2

顺便说一句,1600万行很大,但决不是不可能的。我们在这里有一个SQL Server数据库,其中包含大约10亿行的表格,而且我们仍然可以在不到一秒的时间内完成查询。正如你推断的那样,索引在这里非常重要。 – 2009-11-20 16:39:43

+0

丹尼尔是正确的,我们的数据仓库建立在一个sql服务器平台上,我们经常在包含3.5亿行的表上报告(也很快)。 – 2009-11-20 16:43:10

+0

我很高兴我不负责你的数据库,我不知道从哪里开始!有趣的是,我在大学学习了数据库设计,作为我的CS课程的一部分,但他们甚至没有涉及索引或任何类型的实际数据维护,真的令人失望。 – OrganicPanda 2009-11-23 10:00:54

回答

4

你的问题是,你选择了一个糟糕的聚集键。没有人有兴趣通过ID检索一个特定的日志值。我的系统是象别的我见过的,那么所有的查询要问:在一个日期范围

  • 所有计数器对所有服务器上的所有服务器
  • 特定的计数器值的范围日期
  • 一台服务器的所有计数器在一定范围的日期
  • 特定服务器的特定计数器超过一定范围的日期

由于表的大小,你的所有非聚集索引是无用的。他们都会打到index tipping point,有保证,所以他们可能不存在。我假设你所有的非聚集索引被定义为名称中字段的简单索引,不包含字段。

我会假装我其实知道你的要求。你必须忘记关于存储的常识,并且实际上复制每个非聚集索引中的所有数据。这里是我的建议:

  • 删除[id]上的聚集索引,是一样没用的,因为它得到。
  • 用聚簇索引(logfile_it,test_id,timestamp)组织表。上(为test_id,logfile_id,时间戳)
  • 非clusterd指数包括(logfile_id,时间戳)(值)
  • NC指数包括(值)
  • 上(为test_id,时间戳)包括(值)
  • NC指数
  • 上(时间戳)NC指标包括:(值)
  • 添加维护任务,定期重新组织所有索引,因为它们容易产生碎片

聚集索引覆盖了查询的特定计数器值的历史在特定的机'。非聚集索引涵盖了其他各种可能的查询(随着时间的推移,一台机器上的所有计数器,随时间推移的所有机器上的特定计数器等)。

你注意到我没有评论你的查询脚本。这是因为世界上没有任何东西可以使查询在您拥有的表结构上运行得更快。

现在你不应该做的一件事实际上是实施我的建议。我说我打算假装我知道你的要求。但我其实并没有。我刚刚举了一个可能的结构的例子。你真正应该做的是研究该问题,并找出正确的索引结构为您的要求:

同样在 '覆盖索引' 一个谷歌带来了不少好文章。

当然,在一天结束时,存储空间并不是免费的,所以您必须平衡要求在每个可能的组合上都有一个非聚集索引,并且需要检查数据库的大小。幸运的是,你有一个非常小而狭窄的表格,所以在许多非聚集索引上复制它并不是什么大不了的事情。另外我不会关心插入性能,在15秒时120个计数器每个意味着每秒8-9个插入,这没什么。

+0

优秀的答案!非常感谢您的建议和链接。我会进一步研究这个问题,但你给了我一个很好的起点。 – OrganicPanda 2009-11-23 09:18:09

+0

对不起,最后一件事。我只是试图消化你的建议,并且当你说'非test_id,logfile_id,timestamp包含(value)'你是否有'test_id,logfile_id,timestamp,value'的NC索引有一个特殊的机制来包含一列。对不起,我只是有点困惑,因为它不在列表中与其他列,再次感谢。 – OrganicPanda 2009-11-23 11:42:23

+0

自SQL 2005以来,有一种特殊的机制可以包含列。在2000年,你必须将“包含”列添加到索引键本身。 – 2009-11-23 16:28:28

1

有两件事情浮现在脑海中。

  1. 您是否需要保留那么多数据?如果不是,请考虑创建一个归档表(如果要保留它)(但不要仅在每次运行查询时将其与主表联接)。

  2. 我会避免使用具有这么多数据的临时表。请参阅关于临时表性能的文章以及如何避免使用它们。

http://www.sql-server-performance.com/articles/per/derived_temp_tables_p1.aspx

  • 您似乎缺少在SERVER_ID字段的索引。我会考虑使用这个字段和其他字段来创建一个覆盖索引。这里还有一篇文章。
  • http://www.sql-server-performance.com/tips/covering_indexes_p1.aspx

    编辑

  • 随着在表中在如此短的时间内,许多行,我也查询索引碎片,其可能是缓慢的原因。在SQL Server 2000中,您可以使用DBCC SHOWCONTIG命令。
  • 请参阅此链接信息http://technet.microsoft.com/en-us/library/cc966523.aspx

    而且,请注意,我有编号这些项目为1,2,3,4但是编辑器会自动重置他们

    +0

    伟大的链接! 1.到目前为止,我有4个月的数据,理想情况下,我希望在当前表中保留6个月,然后我可以考虑归档。 2.我将使用该方法运行一些测试 (再次)。 server_id是另一个表(称为日志文件)的一部分。每台服务器有多个日志文件,每个日志文件有多个日志。我是否仍然可以在索引中包含这些内容? – OrganicPanda 2009-11-20 16:13:51

    +0

    不,如果它在不同的表中,则无法将此字段包含在覆盖索引中。我用另外一点更新了我的答案。 – 2009-11-20 16:32:37

    +0

    运行这些检查后,我发现我的逻辑扫描碎片为2.3%,范围扫描碎片为0.11%。根据该文件,我的页面密度为90%,这是一个健康的数量。这些数字对我来说看起来不错,但我会确保按照该文档的建议添加碎片整理。 – OrganicPanda 2009-11-23 09:51:07

    0

    有一次,当仍然工作SQL服务器2000,我需要做一些分页,并且我遇到了一种分页方法,这种分页方法真的让我大惑不解。看看这个方法。

    DECLARE @Table TABLE(
         TimeVal DATETIME 
    ) 
    
    DECLARE @StartVal INT 
    DECLARE @EndVal INT 
    
    SELECT @StartVal = 51, @EndVal = 100 
    
    SELECT * 
    FROM (
          SELECT TOP (@EndVal - @StartVal + 1) 
            * 
          FROM (
             --select up to end number 
             SELECT TOP (@EndVal) 
               * 
             FROM @Table 
             ORDER BY TimeVal ASC 
            ) PageReversed 
          ORDER BY TimeVal DESC 
         ) PageVals 
    ORDER BY TimeVal ASC 
    

    举个例子

    SELECT * 
    FROM (
          SELECT TOP (@EndVal - @StartVal + 1) 
            * 
          FROM (
             SELECT TOP (@EndVal) 
               l.id, 
               l.timestamp 
             FROM log l, logfile lf 
             WHERE lf.server_id = #arguments.server_id# 
             and l.test_id = #arguments.test_id#" 
             and l.timestamp >= #arguments.report_from# 
             and l.timestamp < #arguments.report_to# 
             and l.logfile_id = lf.id 
             order by l.timestamp asc 
            ) PageReversed ORDER BY timestamp DESC 
         ) PageVals 
    ORDER BY timestamp ASC 
    
    +0

    这看起来像一个很好的方式来模拟MySQL限制功能,我非常想念!问题是我不知道我想要的数据在哪里,我必须在日期之间找到数据,并且数据并不总是按照正确的顺序排列的,所以我不认为我现在可以使用它,但我肯定会保存以备后用。 – OrganicPanda 2009-11-20 16:18:39

    +0

    请参阅编辑示例,因为我会尝试它。 – 2009-11-20 16:26:42

    +0

    工作起来非常快!我会看看我能否使用它。非常感谢! – OrganicPanda 2009-11-23 09:57:58