堆上的非聚集索引与聚集索引的性能 [关闭]

Posted

技术标签:

【中文标题】堆上的非聚集索引与聚集索引的性能 [关闭]【英文标题】:Performance of Non Clustered Indexes on Heaps vs Clustered Indexes [closed] 【发布时间】:2011-07-02 22:30:48 【问题描述】:

This 2007 White Paper 比较了单个 select/insert/delete/update 和 range select 语句在以聚集索引组织的表上与在相同键列上以非聚集索引组织为堆的表上的性能CI 表。

通常,聚集索引选项在测试中表现更好,因为只需要维护一个结构并且不需要书签查找。

本文未涵盖的一个可能有趣的案例是堆上的非聚集索引与聚集索引上的非聚集索引之间的比较。在那种情况下,我预计堆甚至可能会表现得更好,因为一旦在 NCI 叶级 SQL Server 有一个 RID 可以直接遵循,而不需要遍历聚集索引。

有没有人知道在这个领域已经进行了类似的正式测试,如果知道,结果如何?

【问题讨论】:

我已经为这个问题添加了一个赏金,看看它是否会吸引更多相关的答案。请注意,这个问题是关于一个非常具体的方面,而不是关于 CI 是否比堆更好的一般问题。 NB:我也意识到 CI 和堆之间在该领域的相对性能无疑将取决于细节,例如 CI 的深度以及可能还有很多其他事情,这就是为什么我希望找到其他人的测试结果的原因。 ...或者换一种说法,我问的是使用逻辑 RID 而不是物理 RID 对性能的(任何)影响。 最初迁移到DBA,但应要求未删除。 请求的原因是取消迁移时取消赏金的unfair bounty situation。 【参考方案1】:

为了检查您的请求,我按照此方案创建了 2 个表:

790 万条记录代表余额信息。 一个从 1 到 790 万的身份字段 一个数字字段,将记录分组到大约 500k 组中。

名为heap 的第一个表在字段group 上获得了非聚集索引。第二个名为clust 的表在名为key 的顺序字段上有一个聚集索引,在group 字段上有一个非聚集索引

测试在具有 2 个超线程内核、4Gb 内存和 64 位 Windows 7 的 I5 M540 处理器上运行。

Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (X64) 
Apr  2 2010 15:48:46 
Developer Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1)  

2011 年 3 月 9 日更新:我通过运行以下 .net 代码并在 Sql Server Profiler 中记录 Duration、CPU、Reads、Writes 和 RowCounts 进行了第二次更广泛的基准测试。 (使用的 CommandText 会在结果中提到。)

注意:CPU 和持续时间以毫秒表示

1000 个查询 从结果中消除了零 CPU 查询 从结果中消除了 0 行受影响的行
int[] idList = new int[]  6816588, 7086702, 6498815 ... ; // 1000 values here.
using (var conn = new SqlConnection(@"Data Source=myserver;Initial Catalog=mydb;Integrated Security=SSPI;"))
            
                conn.Open();
                using (var cmd = new SqlCommand())
                
                    cmd.Connection = conn;
                    cmd.CommandType = CommandType.Text;
                    cmd.CommandText = "select * from heap where common_key between @id and @id+1000"; 
                    cmd.Parameters.Add("@id", SqlDbType.Int);
                    cmd.Prepare();
                    foreach (int id in idList)
                    
                        cmd.Parameters[0].Value = id;

                        using (var reader = cmd.ExecuteReader())
                        
                            int count = 0;
                            while (reader.Read())
                            
                                count++;
                            
                            Console.WriteLine(String.Format("key: 0 => 1 rows", id, count));
                        
                    
                
            

2011 年 3 月 9 日更新结束

选择性能

为了检查性能数字,我在堆表和集群表上执行了一次以下查询:

select * from heap/clust where group between 5678910 and 5679410
select * from heap/clust where group between 6234567 and 6234967
select * from heap/clust where group between 6455429 and 6455729
select * from heap/clust where group between 6655429 and 6655729
select * from heap/clust where group between 6955429 and 6955729
select * from heap/clust where group between 7195542 and 7155729

此基准测试的结果适用于heap

rows  reads CPU   Elapsed 
----- ----- ----- --------
1503  1510  31ms  309ms
401   405   15ms  283ms
2700  2709  0ms   472ms
0     3     0ms   30ms
2953  2962  32ms  257ms
0     0     0ms   0ms

2011 年 3 月 9 日更新cmd.CommandText = "select * from heap where group between @id and @id+1000";

721 行的 CPU > 0 并且影响超过 0 行
Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts    1001      69788    6368         -         
Cpu            15        374      37   0.00754
Reads        1069      91459    7682   1.20155
Writes          0          0       0   0.00000
Duration   0.3716   282.4850 10.3672   0.00180

2011 年 3 月 9 日更新结束


对于表clust,结果为:

rows  reads CPU   Elapsed 
----- ----- ----- --------
1503  4827  31ms  327ms
401   1241  0ms   242ms
2700  8372  0ms   410ms
0     3     0ms   0ms
2953  9060  47ms  213ms
0     0     0ms   0ms

2011 年 3 月 9 日更新cmd.CommandText = "select * from clust where group between @id and @id+1000";

721 行的 CPU > 0 并且影响超过 0 行
Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts    1001      69788    6056         -
Cpu            15        468      38   0.00782
Reads        3194     227018   20457   3.37618
Writes          0          0       0       0.0
Duration   0.3949   159.6223 11.5699   0.00214

2011 年 3 月 9 日更新结束


SELECT WITH JOIN 性能

cmd.CommandText = "select * from heap/clust h join keys k on h.group = k.group where h.group between @id and @id+1000";


此基准测试的结果适用于heap

873 行的 CPU > 0 并且影响超过 0 行

Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts    1009       4170    1683         -
Cpu            15         47      18   0.01175
Reads        2145       5518    2867   1.79246
Writes          0          0       0   0.00000
Duration   0.8215   131.9583  1.9095   0.00123

此基准测试的结果适用于clust

865 行有 > 0 CPU 并影响超过 0 行

Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts    1000       4143    1685         -
Cpu            15         47      18   0.01193
Reads        5320      18690    8237   4.97813
Writes          0          0       0   0.00000
Duration   0.9699    20.3217  1.7934   0.00109

更新性能

第二批查询是更新语句:

update heap/clust set amount = amount + 0 where group between 5678910 and 5679410
update heap/clust set amount = amount + 0 where group between 6234567 and 6234967
update heap/clust set amount = amount + 0 where group between 6455429 and 6455729
update heap/clust set amount = amount + 0 where group between 6655429 and 6655729
update heap/clust set amount = amount + 0 where group between 6955429 and 6955729
update heap/clust set amount = amount + 0 where group between 7195542 and 7155729

heap 的此基准测试结果:

rows  reads CPU   Elapsed 
----- ----- ----- -------- 
1503  3013  31ms  175ms
401   806   0ms   22ms
2700  5409  47ms  100ms
0     3     0ms   0ms
2953  5915  31ms  88ms
0     0     0ms   0ms

2011 年 3 月 9 日更新cmd.CommandText = "update heap set amount = amount + @id where group between @id and @id+1000";

811 行的 CPU > 0 并且影响超过 0 行
Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts    1001      69788    5598       811         
Cpu            15        873      56   0.01199
Reads        2080     167593   11809   2.11217
Writes          0       1687     121   0.02170
Duration   0.6705   514.5347 17.2041   0.00344

2011 年 3 月 9 日更新结束


clust 的此基准测试结果:

rows  reads CPU   Elapsed 
----- ----- ----- -------- 
1503  9126  16ms  35ms
401   2444  0ms   4ms
2700  16385 31ms  54ms
0     3     0ms   0ms 
2953  17919 31ms  35ms
0     0     0ms   0ms

2011 年 3 月 9 日更新cmd.CommandText = "update clust set amount = amount + @id where group between @id and @id+1000";

853 行的 CPU > 0 并且影响超过 0 行
Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts    1001      69788    5420         -
Cpu            15        594      50   0.01073
Reads        6226     432237   33597   6.20450
Writes          0       1730     110   0.01971
Duration   0.9134   193.7685  8.2919   0.00155

2011 年 3 月 9 日更新结束


删除基准

我运行的第三批查询是删除语句

delete heap/clust where group between 5678910 and 5679410
delete heap/clust where group between 6234567 and 6234967
delete heap/clust where group between 6455429 and 6455729
delete heap/clust where group between 6655429 and 6655729
delete heap/clust where group between 6955429 and 6955729
delete heap/clust where group between 7195542 and 7155729

heap 的此基准测试结果:

rows  reads CPU   Elapsed 
----- ----- ----- -------- 
1503  10630 62ms  179ms
401   2838  0ms   26ms
2700  19077 47ms  87ms
0     4     0ms   0ms
2953  20865 62ms  196ms
0     4     0ms   9ms

2011 年 3 月 9 日更新cmd.CommandText = "delete heap where group between @id and @id+1000";

724 行的 CPU > 0 并且影响超过 0 行
Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts     192      69788    4781         -
Cpu            15        499      45   0.01247
Reads         841     307958   20987   4.37880
Writes          2       1819     127   0.02648
Duration   0.3775  1534.3383 17.2412   0.00349

2011 年 3 月 9 日更新结束


clust 的此基准测试结果:

rows  reads CPU   Elapsed 
----- ----- ----- -------- 
1503  9228  16ms  55ms
401   3681  0ms   50ms
2700  24644 46ms  79ms
0     3     0ms   0ms
2953  26955 47ms  92ms
0     3     0ms   0ms

2011 年 3 月 9 日更新

cmd.CommandText = "delete clust where group between @id and @id+1000";

751 行的 CPU > 0 并且影响超过 0 行
Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts     144      69788    4648         -
Cpu            15        764      56   0.01538
Reads         989     458467   30207   6.48490
Writes          2       1830     127   0.02694
Duration   0.2938  2512.1968 24.3714   0.00555

2011 年 3 月 9 日更新结束


插入基准

基准测试的最后一部分是插入语句的执行。

插入堆/簇(...) 值(...), (...), (...), (...), (...), (...)


heap 的此基准测试结果:

rows  reads CPU   Elapsed 
----- ----- ----- -------- 
6     38    0ms   31ms

2011 年 3 月 9 日更新

string str = @"insert into heap (group, currency, year, period, domain_id, mtdAmount, mtdAmount, ytdAmount, amount, ytd_restated, restated, auditDate, auditUser)
                    values";

                    for (int x = 0; x < 999; x++)
                    
                        str += string.Format(@"(@id + 0, 'EUR', 2012, 2, 0, 100, 100, 1000 + @id,1000, 1000,1000, current_timestamp, 'test'),  ", x);
                    
                    str += string.Format(@"(@id, 'CAD', 2012, 2, 0, 100, 100, 1000 + @id,1000, 1000,1000, current_timestamp, 'test') ", 1000);

                    cmd.CommandText = str;
912 语句的 CPU > 0
Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts    1000       1000    1000         -
Cpu            15       2138      25   0.02500
Reads        5212       7069    6328   6.32837
Writes         16         34      22   0.02222
Duration   1.6336   293.2132  4.4009   0.00440

2011 年 3 月 9 日更新结束


clust 的此基准测试结果:

rows  reads CPU   Elapsed 
----- ----- ----- -------- 
6     50    0ms   18ms

2011 年 3 月 9 日更新

string str = @"insert into clust (group, currency, year, period, domain_id, mtdAmount, mtdAmount, ytdAmount, amount, ytd_restated, restated, auditDate, auditUser)
                    values";

                    for (int x = 0; x < 999; x++)
                    
                        str += string.Format(@"(@id + 0, 'EUR', 2012, 2, 0, 100, 100, 1000 + @id,1000, 1000,1000, current_timestamp, 'test'),  ", x);
                    
                    str += string.Format(@"(@id, 'CAD', 2012, 2, 0, 100, 100, 1000 + @id,1000, 1000,1000, current_timestamp, 'test') ", 1000);

                    cmd.CommandText = str;
946 个语句的 CPU > 0
Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts    1000       1000    1000         -      
Cpu            15       2403      21   0.02157
Reads        6810       8997    8412   8.41223
Writes         16         25      19   0.01942
Duration   1.5375   268.2571  6.1463   0.00614

2011 年 3 月 9 日更新结束


结论

虽然在使用聚集索引和非聚集索引访问表时(使用非聚集索引时)会进行更多的逻辑读取,但性能结果是:

SELECT 语句具有可比性 使用聚集索引,UPDATE 语句速度更快 使用聚集索引可以更快地删除语句 使用聚集索引,INSERT 语句速度更快

当然,我的基准测试仅限于特定类型的表和非常有限的查询集,但我认为基于这些信息,我们已经可以开始说,创建聚集索引实际上总是更好你的桌子。

2011 年 3 月 9 日更新

从添加的结果中我们可以看出,有限测试的结论并非在所有情况下都是正确的。

现在的结果表明,唯一受益于聚集索引的语句是更新语句。其他语句在具有聚集索引的表上慢约 30%。

一些额外的图表,我在其中绘制了堆与集群的每个查询的加权持续时间。

正如您所见,插入语句的性能配置文件非常有趣。峰值是由几个数据点引起的,这些数据点需要更长的时间才能完成。

2011 年 3 月 9 日更新结束

【讨论】:

@Martin 我将在下周找到一些时间时尝试在有几张表和 5 亿条记录的服务器上运行它。 +1 很棒的东西。一些有趣的结果。 我怀疑这个测试的真实性。有些部分需要认真注意,例如声称聚集索引更快的 INSERT 性能 - CLUST 版本中的读取次数更多,但经过的时间更少。我个人会忽略 10 毫秒以内的经过时间(时间可变性)——这意味着小于读取计数。 查看 Kimberly Tripp 的 The Clustered Index Debate Continues,她解释了为什么大多数(如果不是全部)使用聚簇表的操作比使用堆更快 - 有些与您的结果相反... @Martin,@Richard,@marc_s。我现在正在研究一个更严肃的基准。我希望能够在今天晚些时候添加结果。【参考方案2】:

正如索引女王 Kimberly Tripp 在她的博客文章 The Clustered Index Debate continues... 中很好地解释的那样,在数据库表上拥有一个集群键几乎可以加快 所有 操作 - 而不仅仅是 SELECT .

与聚簇表相比,在堆上的 SELECT 通常较慢,只要您选择 good 聚簇键 - 类似于 INT IDENTITY。如果您使用非常糟糕的集群键,例如 GUID 或具有大量可变长度组件的复合键,那么,但只有这样,堆可能会更快。但在这种情况下,您确实需要首先清理您的数据库设计......

所以总的来说,我认为堆中没有任何意义 - 选择一个好的、有用的集群键,你应该会在所有方面受益。

【讨论】:

这是一个非答案。 Martin 在 SQL Server 上相当扎实;该问题旨在从性能测试中获得经过实际测试验证的结果,而不是更多的理论。

以上是关于堆上的非聚集索引与聚集索引的性能 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

MySQL中怎样创建聚集索引和非聚集索引,求创建这两种索引的SQL语句。谢谢

SQL SERVER大话存储结构

怎么取消自增列上的聚集索引

聚焦-聚集索引对非聚集索引的影响

非聚集列存储索引与 bigint 字段上的传统非聚集行存储索引

SQL Server的聚集索引和非聚集索引