【问题标题】:SQL Server 2008 index fragmentation problemSQL Server 2008 索引碎片问题
【发布时间】:2011-08-20 18:26:09
【问题描述】:

每天我使用BULK INSERT 将一些文本文件中的 2,000,000 行导入 SQL Server 2008,然后我进行一些后处理以更新记录。

我在表上有一些索引,以尽可能快地执行后处理,在正常情况下,后处理脚本需要大约 40 秒才能运行。

但有时(我不知道何时)后处理不起作用。在我提到的情况下,一个小时后还没有完成!重建索引后一切正常。

我应该怎么做才能防止问题发生?

现在,我有一个夜间工作来重建所有索引。为什么索引碎片会增长到 90%?

更新: 这是我将文本文件导入到的表格:

  CREATE TABLE [dbo].[My_Transactions](
    [My_TransactionId] [bigint] NOT NULL,
    [FileId] [int] NOT NULL,
    [RowNo] [int] NOT NULL,
    [TransactionTypeId] [smallint] NOT NULL,
    [TransactionDate] [datetime] NOT NULL,
    [TransactionNumber] [dbo].[TransactionNumber] NOT NULL,
    [CardNumber] [dbo].[CardNumber] NULL,
    [AccountNumber] [dbo].[CardNumber] NULL,
    [BankCardTypeId] [smallint] NOT NULL,
    [AcqBankId] [smallint] NOT NULL,
    [DeviceNumber] [dbo].[DeviceNumber] NOT NULL,
    [Amount] [dbo].[Amount] NOT NULL,
    [DeviceTypeId] [smallint] NOT NULL,
    [TransactionFee] [dbo].[Amount] NOT NULL,
    [AcqSwitchId] [tinyint] NOT NULL
) ON [PRIMARY]

GO

CREATE NONCLUSTERED INDEX [_dta_index_Jam_Transactions_8_1290487676__K1_K4_K12_K6_K11_5] ON [dbo].[Jam_Transactions] 
(
    [Jam_TransactionId] ASC,
    [TransactionTypeId] ASC,
    [Amount] ASC,
    [TransactionNumber] ASC,
    [DeviceNumber] ASC
)
INCLUDE ( [TransactionDate]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

CREATE NONCLUSTERED INDEX [_dta_index_Jam_Transactions_8_1290487676__K12_K6_K11_K1_5] ON [dbo].[Jam_Transactions] 
(
    [Amount] ASC,
    [TransactionNumber] ASC,
    [DeviceNumber] ASC,
    [Jam_TransactionId] ASC
)
INCLUDE ( [TransactionDate]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

CREATE NONCLUSTERED INDEX [IX_Jam_Transactions] ON [dbo].[Jam_Transactions] 
(
    [Jam_TransactionId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

【问题讨论】:

    标签: sql sql-server-2008 indexing


    【解决方案1】:

    你有没有试过在这么大的插入后刷新统计数据:

    UPDATE STATISTICS my_table
    

    我在大批量插入方面的经验是,统计信息会全部混乱,之后需要刷新,这也比运行 REINDEX 或索引 REORDER 快得多。

    另一种选择是研究填充索引,您的索引可能没有填充填充因子,这意味着如果您的索引是:

    A, B, D, E, F
    

    然后你插入一个带有CardNumber C 的值,那么你的索引将如下所示:

    A, B, D, E, F, C
    

    因此会产生约 20% 的碎片化,如果您改为为索引指定填充因子,例如 15%,我们会看到它大致如下:

    A, B, D, _, E, F
    

    (注意内部的空白区域大致是放在fillfactor%的中点而不是最后)

    因此,当您插入 C 值时,它更接近正确,但它实际上看到 D 刚刚与 C 交换,并且通常在该点移动 D。

    除此之外,您确定碎片实际上是问题所在,因为重新索引表的一部分被读取并完全加载到内存中(前提是它适合),因此您在其上运行的任何查询都会非常快。

    【讨论】:

      【解决方案2】:

      您为什么不将这个表包含在每晚的工作中,而不是将索引维护(特别是在这个表上)作为每晚导入工作的一部分,在BULK INSERT 和任何“后处理”之间?

      我们没有足够的信息来了解为什么索引碎片增长如此之快。哪个索引?有多少个索引?文件中数据的顺序是什么?

      您还可以考虑在BULK INSERT 语句中使用ORDER 选项来更改插入数据的方式。它可能会使负载花费更长的时间,但应该会减少重组的需要。再次取决于源数据的顺序和变得碎片化的索引。

      最后,重建/不重建或重组/不重组索引的影响是什么?你都试过了吗?如果重建,也许它会使后处理运行得更快,但也许只需要进行碎片整理。虽然它可以使后处理更快,但当天晚些时候对表运行的查询呢?您是否针对这些指标进行过任何衡量,以查看它们是否会根据您在夜间的活动而加速或减速?

      【讨论】:

      • 谢谢,主要问题是我有 100,000,000 条记录存在于表上,重建索引需要很长时间,所以似乎重建所有索引(插入 500,000 条新记录后)可能是最好的解决方案。我有 4 个索引(PK Identity 上的一个聚集索引,TransactionTime、CardNumber 和 TransactionNumber 上的 3 个非聚集升序索引)。我所有的字段都是 bigint。
      • 你应该考虑对这个表进行分区,如果可能的话,你也许可以只重建表的一部分。
      • @SqlACID 击败了我...听起来确实像是分区的案例。
      【解决方案3】:

      您的主表是否保持每天 200 万行的增长,或者是否也发生了很多删除操作?您可以批量插入临时导入表并在插入主表之前进行处理吗?您始终可以使用提示来强制您的查询使用某些索引:

      SELECT *
      FROM your_table_name WITH (INDEX(your_index_name))
      WHERE your_column_name = 5
      

      【讨论】:

      • 我没有删除任何记录。我的步骤是: 1- 从文本文件插入 RAW 数据表。 2- 用户批准。 3- 处理批准的记录。 4- 在另一个表中插入已处理的记录(RAW 数据表上没有任何更改)。有时索引碎片的高值会导致进程部分失败。
      • “失败”是指“慢”,对吗?有很大的区别。突然间,我不清楚碎片是如何导致插入速度变慢的。碎片化在哪里?在原始表上还是在目标表上?您是在观察到缓慢问题之前还是之后检查此碎片?发生这种情况时,插入的数据有什么不同吗?要获得更具体的帮助,您确实需要提供有关我在问题中提出的问题的更多详细信息(您观察到碎片的索引,文件中数据的性质)。
      • “失败”是指2小时后它没有完成,然后我停止了它!在正常情况下,需要 30 秒。
      【解决方案4】:

      我会尝试在批量插入行之前使索引脱机,并在批量插入行之后将其重新联机。与重新索引或执行删除并创建索引相比,速度要快得多.....不同之处在于索引在那里 数据正在存储但索引当前未使用,“离线”直到它带回“在线”。我有一个 150 万行的插入过程,并且我的一个非聚集索引出现碎片问题,导致性能不佳。使用 MSSQL 离线在线索引选项将 99% 的碎片化到 0.14%....

      代码示例:

      ALTER INDEX idx_a ON dbo.tbl_A
      REBUILD WITH (ONLINE = OFF);
      

      在 OFF 和 ON 之间切换,一切顺利......

      【讨论】:

      • 这实际上不会使索引脱机。它使用离线表重建索引,即没有其他进程可以使用它。当然这最终几乎没有碎片,因为它在后台所做的是删除索引并从头开始创建它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-29
      • 2016-01-28
      相关资源
      最近更新 更多