在SQL Server 2012中索引PK GUID


13

我的开发人员已将其应用程序设置为将GUID用作几乎所有表的PK,并且默认情况下,SQL Server已在这些PK上设置了聚集索引。

该系统还比较年轻,我们最大的表刚刚超过一百万行,但是我们正在研究索引并希望能够迅速扩展,因为不久的将来可能会需要它。

因此,我的第一个倾向是将聚集索引移动到创建的字段,该字段是DateTime的bigint表示形式。但是,使CX唯一的唯一方法是在此CX中包括GUID列,但先创建顺序。

这会导致群集密钥太宽,是否会提高写入性能?读取也很重要,但是此时写入可能是一个更大的问题。


1
GUID是如何生成的?NEWID或NEWSEQUENTIALID?
swasheck

6
仅在紧靠“性能”之前的单词最小化时,才应在句子中包含聚集的guid和insert性能
billinkc

2
带那些开发人员出去吃午餐,并向他们解释,如果他们再次使用NEWID()作为主键,您将把性能不佳归咎于他们。他们会很快询问您该怎么做。此时,您说使用IDENTITY(1,1)代替。(也许稍微简化了一下,但有10的9倍有效)。
Max Vernon

3
我们讨厌guid的原因是它们很宽(16个字节),并且当不使用时newsequentialid是随机的。聚簇的键窄且增加时最好。GUID相反:胖和随机。想象一下一个几乎满是书的书架。OED进入其中,由于引导的随机性,它插入了架子的中间。为了使事情井井有条,必须将书的右半部分插入一个新的位置,这是一项耗时的工作。这就是GUID对您的数据库所做的,并降低了性能。
billinkc

7
解决使用uniqueidentifier的问题的方法是回到绘图板上,而不使用uniqueidentifier。如果系统很小,它们并不可怕,但是如果您至少有几百万个以上的行表(或任何大于该表的表),您肯定会因使用键的uniqueidentifiers而被压垮。
乔恩·塞格尔

Answers:


20

GUID(尤其是非顺序GUID)的主要问题是:

  • 密钥的大小(16字节,而INT为4字节):这意味着您将在密钥中存储4倍的数据量,如果这是您的聚集索引,则将为任何索引存储额外的空间。
  • 索引碎片:由于键值的完全随机性,几乎不可能使非顺序的GUID列进行碎片整理。

那么这对您的情况意味着什么?这取决于您的设计。如果您的系统仅涉及写操作,而不关心数据检索,那么Thomas K概述的方法是准确的。但是,您必须记住,采用这种策略会在读取和存储数据时造成许多潜在的问题。正如乔恩·塞格尔(Jon Seigel)所指出的那样,您还将占用更多空间,并且本质上会使内存膨胀。

有关GUID的主要问题是它们的必要性。开发人员之所以喜欢它们,是因为它们确保了全局唯一性,但是这种唯一性非常罕见的情况很少见。但是请考虑一下,如果您的最大数量少于2,147,483,647(4字节有符号整数的最大值),则您可能没有为密钥使用适当的数据类型。即使使用BIGINT(8个字节),您的最大值也为9,223,372,036,854,775,807。如果您需要为唯一密钥提供一些自动递增的值,那么对于任何非全局数据库(以及许多全局数据库)而言,这通常就足够了。

最后,就使用堆还是聚簇索引而言,如果纯粹是写数据,则堆将是最有效的,因为可以最大程度地减少插入的开销。但是,SQL Server中的堆对于数据检索效率极低。我的经验是,如果有机会声明一个聚集索引,则总是需要聚集索引。我已经看到在表中添加聚簇索引(超过40亿条记录)将整体选择性能提高了6倍。

附加信息:


13

GUID在OLTP系统中作为键和群集没有问题(除非您的表上有很多索引受群集大小增加的影响)。实际上,它们比IDENTITY列具有更大的可伸缩性。

人们普遍认为GUID是SQL Server中的一个大问题-在很大程度上,这完全是错误的。实际上,GUID在具有大约8个以上内核的盒子上可以具有更大的可扩展性:

抱歉,您的开发人员是对的。在担心GUID之前,请先担心其他事情。

哦,最后:为什么首先要集群索引?如果您关心的是具有许多小索引的OLTP系统,那么最好使用堆。

现在,让我们考虑一下碎片化(GUID将引入哪种碎片化)对您的读取造成的影响。分段存在三个主要问题:

  1. 页面拆分成本磁盘I / O
  2. 半满页的内存效率不如整页
  3. 这会导致页面的存储顺序混乱,从而降低了顺序I / O的可能性

由于您在问题中关注的是可扩展性,因此我们可以将其定义为“添加更多硬件使系统运行更快”,这些问题最少。依次解决每个问题

广告1)如果您想扩展规模,那么您有能力购买I / O。即使是便宜的Samsung / Intel 512GB SSD(几美元/ GB)也可以让您获得超过100K的IOPS。您很快就不会在2插槽系统上使用它。如果您遇到这种情况,请再买一台,

广告2)如果您确实要删除表格,则无论如何您将拥有整整一半的页面。即使您不这样做,内存也很便宜,除了最大的OLTP系统外,其他所有内存都非常便宜-热门数据应该可以容纳在那里。当寻找规模时,寻求将更多数据打包到页面中是次优的。

广告3)由频繁分页,高度碎片化数据构成的表执行随机I / O的速度与顺序填充表的速度完全相同

关于联接,在OLTP之类的工作负载中可能会看到两种主要的联接类型:哈希和循环。让我们依次看一下:

散列连接:散列连接假定已扫描较小的表,通常会查找较大的表。小表很可能在内存中,因此在这里I / O并不是您所关心的。我们已经触及到这样一个事实,即零散索引中的搜寻成本与非零散索引中的搜寻成本相同

循环连接:将搜索外部表。相同成本

您可能还会进行很多错误的表扫描-但是GUID也不是您所关心的,正确的索引是。

现在,您可能会进行一些合法的范围扫描(尤其是在使用外键联接时),在这种情况下,与非分段数据相比,分段数据的“打包”程度较小。但是,让我们考虑一下,在索引良好的3NF数据中,您可能会看到哪些连接:

  1. 来自具有外键引用的表与其所引用表的主键的联接

  2. 另一种方式

广告1)在这种情况下,您要对主键进行一次查找-将n联接为1。是否存在碎片,相同成本(一次查找)

广告2)在这种情况下,您正在连接到同一键,但可能会检索到多行(范围搜索)。在这种情况下,连接为1到n。但是,您正在寻找的外部表正在寻找SAME键,该键与在非碎片索引中的碎片索引在同一页上的可能性相同。

考虑一下这些外键。即使您已经“完美地”按顺序放置了我们的主键-指向该键的任何内容仍将是非顺序的。

当然,您可能正在某个银行的某些SAN中的虚拟机上运行,​​这种虚拟机价格便宜且处理量大。然后,所有这些建议将丢失。但是,如果这就是您的世界,那么可伸缩性就不是您想要的-您正在寻找性能和高速度/成本-两者都是不同的。


1
评论不作进一步讨论;此对话已转移至聊天
保罗·怀特9

5

托马斯(Thomas):您的某些观点完全有道理,我都同意。如果您使用的是SSD,则优化后的平衡确实会发生变化。随机还是顺序与旋转磁盘的讨论不同。

我特别同意,采用纯DB视图是完全错误的。使您的应用程序变慢且不可扩展以提高数据库性能可能会被误导。

IDENTITY(或序列,或数据库中生成的任何内容)的最大问题是,它非常慢,因为它需要往返数据库才能创建密钥,这自动使数据库成为瓶颈,它强制应用程序必须进行数据库调用以开始使用密钥。创建GUID可以通过使用应用程序创建密钥来解决此问题,并保证它是全局唯一的(根据定义),因此应用程序层可以使用它来传递记录,然后再进行数据库往返。

但是我倾向于使用GUID的替代方法。我个人对数据类型的偏好是由应用程序生成的全局唯一的BIGINT。如何做到这一点?在最简单的示例中,您向应用程序添加了一个非常轻巧的小函数,以对GUID进行哈希处理。假设您的哈希函数既快速又相对快(例如,请参见Google的CityHash:http//google-opensource.blogspot.in/2011/04/introducing-cityhash.html-确保正确完成所有编译步骤,或http://tools.ietf.org/html/draft-eastlake-fnv-03的FNV1a变体,以获取简单代码),这将使您受益于应用程序生成的唯一标识符和64位键值,CPU可以更好地与。

还有其他生成BIGINT的方法,在这两种算法中,都有可能发生哈希冲突-阅读并做出有意识的决策。


2
我建议您将答案编辑为OP的问题的答案,而不是(目前)作为Thomas答案的答案。您仍然可以强调托马斯(MikeFal)和您的建议之间的区别。
ypercubeᵀᴹ

2
请回答您对问题的回答。如果您不这样做,我们将为您删除它。
JNK 2014年

2
谢谢马克的评论。当您编辑答案时(我认为它提供了很好的上下文),我将改变一件事:如果您对INSERT谨慎,IDENTITY不需要额外往返服务器。您可以在调用INSERT批次总是返回SCOPE_IDENTITY()..
托马斯Kejser

1
关于“这太慢了,因为它需要往返DB才能创建密钥” –您可以在一次往返中获取任意数量的东西。
AK 2014年

关于“您可以在一次往返中获取任意数量的数据”-您不能使用IDENTITY列或任何其他在数据库级别上基本上使用DEFAULT的方法来做到这一点。
阿维·切里
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.