大表建​​议(SQL Server)

时间:2009-11-20 15:41:51

标签: sql-server coldfusion sql-server-2000

我在访问其中一张桌子时遇到了很大的缓慢,我需要一些重新分解的建议。对不起,如果这不是这类事情的正确区域。

我正在开展一个旨在报告内部服务器的服务器性能统计信息的项目。我每晚处理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)

我会非常感谢能够提供一些建议的人,这些建议可以帮助我加快速度。我不介意重新组织事情,我完全控制了项目(可能不是通过服务器硬件)。

干杯(对不起,长篇大论)

3 个答案:

答案 0 :(得分:4)

您的问题是您选择了错误的群集密钥。没有人有兴趣通过ID检索一个特定的日志值。我的系统就像我见过的其他任何东西一样,然后所有的查询都会要求:

  • 一系列日期内所有服务器的所有计数器
  • 一系列日期的所有服务器上的特定计数器值
  • 一系列日期内一台服务器的所有计数器
  • 特定服务器在特定日期范围内的特定计数器

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

我打算假装我真的了解你的要求。您必须忘记有关存储的常识,并实际复制每个非聚集索引中的所有数据。以下是我的建议:

  • 删除[id]上的聚集索引,这是一个无用的。
  • 使用聚簇索引(logfile_it,test_id,timestamp)组织表。
  • 非群集索引(test_id,logfile_id,timestamp)包括(值)
  • NC索引(logfile_id,timestamp)包括(值)
  • NC索引(test_id,timestamp)包括(值)
  • NC索引(时间戳)包括(值)
  • 添加维护任务以定期重新组织所有索引,因为它们容易出现碎片

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

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

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

谷歌“覆盖索引”也会带来很多好文章。

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

答案 1 :(得分:1)

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

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

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

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

    1. 看起来您缺少server_id字段的索引。我会考虑使用此字段和其他字段创建覆盖索引。这里也有一篇文章。
    2. http://www.sql-server-performance.com/tips/covering_indexes_p1.aspx

      修改

      1. 在如此短的时间范围内表中有很多行,我还会检查索引是否有碎片,这可能是导致速度缓慢的原因。在SQL Server 2000中,您可以使用DBCC SHOWCONTIG命令。
      2. 请参阅此链接了解信息http://technet.microsoft.com/en-us/library/cc966523.aspx

        此外,请注意我已将这些项目编号为1,2,3,4,但编辑器会自动重置它们

答案 2 :(得分:0)

一旦还在使用sql server 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