单列与多列索引的性能开销

时间:2011-12-05 14:34:29

标签: sql sql-server sql-server-2008

您好我想知道在插入方面单列与多列索引的性能开销是什么样的。因此,例如,如果我有3个单列索引,那么对于插入到该表中更好,而不是具有多个列的1个索引。当我谈论表演时,我对原始速度感兴趣。

4 个答案:

答案 0 :(得分:5)

无论是单列还是多列,INDEX的INSERT开销通常都可以忽略不计。

除非写入显着权衡读取,否则请始终包含提高查询性能所需的任何索引。

在某些情况下,多个单列索引可以充分提高比单个多列索引更多查询的性能。更常见的是,单个多列索引进一步提高了较小查询集的性能。

一般规则是考虑查询性能,而不是插入性能,除非您可以预测或查看插入的特定问题。

答案 1 :(得分:2)

我认为回答的唯一方法是使用您的系统使用模式测试硬件上的几个场景。与没有索引相比,通常索引开销很大。因此索引中的列越多,开销就越大。但是用户认为的影响可能看起来没什么,但可以分析不同的测试并查看数字。我曾在他们只是扔掉所有东西的索引的地方工作过。我不同意这种做法。我认为你应该在何时可以证明它具有价值的时候加上一个指数。除了增加开销外,它们还占用空间。同样,您需要测试自己的设置以回答您的问题。

答案 2 :(得分:1)

  

如果我有3个单列索引,那么插入更好   进入该表,而不是让1个索引包含多列。

对于修改数据的操作,具有3列的单个索引应该比具有单列的3个索引更快,原因如下:

  • 更新索引需要在B-Tree中搜索正确的位置以执行修改。在一个索引中执行搜索(即使它是复合索引)往往比在3个索引中执行3次搜索更快。
  • 每个B-Tree leaf包含一行“指针”。 1个索引的行指针比3个索引少3倍(忽略NULL的影响,通常没有索引)。较小的缓存效果往往更快。
  • 1个索引可能与3个索引相比,往往具有更少的splitting和B树节点的合并。

考虑以下MS SQL Server基准:

CREATE TABLE ONE_INDEX (
    ID int PRIMARY KEY NONCLUSTERED,
    F1 uniqueidentifier NOT NULL,
    F2 uniqueidentifier NOT NULL,
    F3 uniqueidentifier NOT NULL
);
CREATE INDEX ONE_INDEX_IE1 ON ONE_INDEX (F1, F2, F3);

CREATE TABLE THREE_INDEXES (
    ID int PRIMARY KEY NONCLUSTERED,
    F1 uniqueidentifier NOT NULL,
    F2 uniqueidentifier NOT NULL,
    F3 uniqueidentifier NOT NULL
);
CREATE INDEX THREE_INDEXES_IE1 ON THREE_INDEXES (F1);
CREATE INDEX THREE_INDEXES_IE2 ON THREE_INDEXES (F2);
CREATE INDEX THREE_INDEXES_IE3 ON THREE_INDEXES (F3);
GO



SET NOCOUNT ON
DECLARE @t DATETIME;
DECLARE @id INT;
DECLARE @count INT;
SET @count = 100000;



PRINT 'ONE_INDEX:'

SET @t = CURRENT_TIMESTAMP
SET @id = 0;
BEGIN TRANSACTION;
WHILE @id < @count BEGIN
    INSERT INTO ONE_INDEX VALUES(@id, NEWID(), NEWID(), NEWID());
    SET @id = @id + 1;
END
COMMIT TRANSACTION;
PRINT '    INSERT ' + CAST(@count AS VARCHAR) + ' rows: ' + CAST(DATEDIFF(ms, @t, CURRENT_TIMESTAMP) AS VARCHAR) + ' ms';

SET @t = CURRENT_TIMESTAMP
SET @id = 0;
BEGIN TRANSACTION;
WHILE @id < @count BEGIN
    UPDATE ONE_INDEX SET F1 = NEWID(), F2 = NEWID(), F3 = NEWID() WHERE ID = @id
    SET @id = @id + 1;
END
COMMIT TRANSACTION;
PRINT '    UPDATE ' + CAST(@count AS VARCHAR) + ' rows: ' + CAST(DATEDIFF(ms, @t, CURRENT_TIMESTAMP) AS VARCHAR) + ' ms';

SET @t = CURRENT_TIMESTAMP
DELETE FROM ONE_INDEX;
PRINT '    DELETE ' + CAST(@count AS VARCHAR) + ' rows: ' + CAST(DATEDIFF(ms, @t, CURRENT_TIMESTAMP) AS VARCHAR) + ' ms';



PRINT 'THREE_INDEXES:'

SET @t = CURRENT_TIMESTAMP
SET @id = 0;
BEGIN TRANSACTION;
WHILE @id < @count BEGIN
    INSERT INTO THREE_INDEXES VALUES(@id, NEWID(), NEWID(), NEWID());
    SET @id = @id + 1;
END
COMMIT TRANSACTION;
PRINT '    INSERT ' + CAST(@count AS VARCHAR) + ' rows: ' + CAST(DATEDIFF(ms, @t, CURRENT_TIMESTAMP) AS VARCHAR) + ' ms';

SET @t = CURRENT_TIMESTAMP
SET @id = 0;
BEGIN TRANSACTION;
WHILE @id < @count BEGIN
    UPDATE THREE_INDEXES SET F1 = NEWID(), F2 = NEWID(), F3 = NEWID() WHERE ID = @id
    SET @id = @id + 1;
END
COMMIT TRANSACTION;
PRINT '    UPDATE ' + CAST(@count AS VARCHAR) + ' rows: ' + CAST(DATEDIFF(ms, @t, CURRENT_TIMESTAMP) AS VARCHAR) + ' ms';

SET @t = CURRENT_TIMESTAMP
DELETE FROM THREE_INDEXES;
PRINT '    DELETE ' + CAST(@count AS VARCHAR) + ' rows: ' + CAST(DATEDIFF(ms, @t, CURRENT_TIMESTAMP) AS VARCHAR) + ' ms';
GO



DROP TABLE ONE_INDEX;
DROP TABLE THREE_INDEXES;
GO

其中(在虚拟机下的MS SQL Server Express 2008 R2上)打印:

ONE_INDEX:
    INSERT 100000 rows: 4173 ms
    UPDATE 100000 rows: 5530 ms
    DELETE 100000 rows: 2706 ms
THREE_INDEXES:
    INSERT 100000 rows: 6640 ms
    UPDATE 100000 rows: 10436 ms
    DELETE 100000 rows: 3516 ms

@count增加到1000000会导致:

ONE_INDEX:
    INSERT 1000000 rows: 40143 ms
    UPDATE 1000000 rows: 55796 ms
    DELETE 1000000 rows: 95576 ms
THREE_INDEXES:
    INSERT 1000000 rows: 61360 ms
    UPDATE 1000000 rows: 91766 ms
    DELETE 1000000 rows: 99500 ms

请注意,在大多数工作负载中读取的数量超过了写入次数,因此优化工作通常会优先考虑使用索引覆盖SELECT,但会牺牲INSERT,UPDATE和DELETE。此外,与3个单独的索引相比,复合索引将以不同的方式影响查询性能。

只有才能执行相应的测试,并在所有这些问题之间取得适当的平衡。

答案 3 :(得分:0)

请记住,当您需要在City上搜索时,由LastName,FirstName和City三个列组成的复合索引无用。因此,在这种情况下,您需要为City提供一个索引。