单列主键和多列主键对SQL Server性能的影响有什么区别?

9

如果主键是单列(例如为每行生成的GUID)或多列(例如外键GUID + 偏移量),那么在插入/更新和查询方面,表现是否有差异?

我认为,如果使用多列主键,则查询速度应该更快,但是由于稍微复杂的唯一性检查,我想插入可能会变慢。我还想象多列主键的数据类型也可能很重要(例如,如果其中一个列是DateTime类型,则会增加复杂性)。这些只是我的想法,旨在引发答案和讨论(希望如此!),并非基于事实。

我意识到有一些其他问题涉及此主题,但我想知道的是性能影响,而不是管理/业务方面的问题。

4个回答

4
每个关键组件的变长(a)和宽度(宽列而不是窄列)(b),对性能影响更大,比关键组件的数量更重要。除非微软在最新版本中再次破坏了它(他们在2005年破坏了堆)。数据类型不会减慢速度;宽度,特别是可变长度(任何数据类型),会减慢速度。请注意,如果将固定长度的列设置为可空,则会使其变为可变长度列。索引中的可变长度列是个坏消息,因为每次访问都需要执行一些“解包”操作才能获取数据。
显然,仅使用固定且不可为空的列,尽可能使索引列变窄。
就复合键中的列数而言,一个列比七个列快,但差别不是很大:三个宽的变量列比七个窄的固定列慢得多。
GUID当然是一个非常宽的键;GUID加上其他内容就更宽了;GUID可空是吉尼斯材料。不幸的是,这是解决IDENTITY问题的膝跳反应,而IDENTITY问题的根源则是没有选择好的自然关系键。所以,最好在源头上修复真正的问题,并选择好的自然键;避免IDENTITY;避免GUID。
经验和性能调优,而不是推测。

“经验和性能调优,而非猜测。”这句话的意思是强调在编程中需要依靠实践经验和对程序性能的优化,而不是凭空臆想。 - Mitch Wheat
除非微软在最新版本中再次出现问题(他们在2005年破坏了堆栈)。- 详细说明一下吗? - Mitch Wheat
1
我们(作为顾问)曾经根据多个站点的经验和基准提供建议,关于堆与设计良好的CI(特定用途)的速度。对于某些操作,堆比较快。我们惊讶地发现,在2005年,MVP建议使用CI更快。当我们再次进行基准测试时,我们发现了真相:CI的速度没有改变;微软已经破坏了堆,它们实际上比CI在这些操作中更慢;“CI更快”只是微软营销宣传的惯常手法,以掩盖负面性能的现实。 - PerformanceDBA
1
我是这个网站的新手,还在了解该社区的文化。我注意到许多人只根据知识、书籍和文章来发表回答,却没有实际经验或测试变化。我要声明,我有实践经验,能够识别并解决问题,并且能够对此问题进行基准测试。 - PerformanceDBA
1
这变得很无聊了。[所有人]“其他人不这样做”是你的解释。我在评论那些确实没有这样做的人。把一切都当成个人攻击。那些在边角区域进行长时间交流的人忘记了我们在这里是为了回答OP的问题,而不是为了自己的个人战斗。 - PerformanceDBA
1
谢谢你的帖子,帮了很大的忙。就记录而言,我已经决定使用int(4字节)+ char(4)作为主键字段(对于时间序列表,还会添加一个datetime字段)。公司之前在各个地方都使用GUID作为ID,但经过一些思考后发现它们完全是杀鸡用牛刀。 - mike

1

这取决于您的访问模式、读写比例以及(可能最重要的)聚集索引是否定义在主键上。

经验法则是尽可能使您的主键小(32位整数),并在可能的情况下将聚集索引定义为单调递增的键(考虑IDENTITY),除非您对该表的查询中有大量的范围搜索。

如果您的应用程序具有写入密集型,并且您在GUID列上定义了聚集索引,则应注意:

  1. 所有非聚集索引都将包含聚集索引键,因此会更大。如果有许多NC索引,则可能会对性能产生负面影响。

  2. 除非您使用“有序”的GUID(例如COMB或使用NEWSEQUENTIALID()),否则您的插入操作将随时间而使索引碎片化。这意味着您需要定期重建索引,并可能增加页面中留下的空闲空间量(填充因子)

由于有许多因素在起作用(硬件、访问模式、数据大小),我建议您运行一些测试并基准测试您特定的情况。


1
我的情况将会是写入操作比读取操作更频繁,写入速度比读取速度更为关键(尽管读取速度仍需达到要求!)。 - mike
1
顺便说一下,我不是那个给你点踩的人,但感谢提供额外的信息。我正在进行数据库新设计的早期阶段,因此很难进行一些基准测试。我希望能借鉴他人的经验来帮助决定该怎么做。我会考虑您的评论,谢谢 :) - mike
1
不是“downvoter”。你的陈述是正确的,但你的结论是错误的。例如,对于GUID,其思想是有意获得接近“随机”的密钥,并因此获得分布式插入(避免NextSequential热点);因此,必须使用FillFactor。我会投反对票。 - PerformanceDBA
1
如果您为预期的[GUID或其他随机值]插入正确设置了填充因子,CI将不会被碎片化,您就不需要定期进行索引“重建”。我确实说过“您的语句是正确的,但是……”我知道GUID的原因,那只是其中之一,不是主要原因。 - PerformanceDBA
1
呃,注释的长度是有限制的。到目前为止,你和我都是正确的。当然,还有更多的内容,但讨论它需要更多的互动。我很感激你和我都在努力帮助 OP,我们是站在同一边的。如果你真的认为这种交流是必要的,请提出一个新问题,这样我们就可以关闭这个问题。让我们不要劫持这个线程。 - PerformanceDBA
显示剩余4条评论

0

这取决于每种情况下的索引和存储方式。其他所有条件相同,主键的选择与性能无关。索引和其他存储选项的选择将是决定性因素。


0

如果您的情况将面向更多的插入操作,那么越小的磁盘空间越好。

有两个需要分离的事情,一个是数据库级别的主键概念,另一个是应用程序使用的键概念。

为什么需要 GUID?您是否要插入到多个数据库服务器,然后将信息合并到一个集中式数据库中?

如果是这样,我的建议是使用标识后跟 GUID。在标识上集群索引,在 GUID 上唯一非聚集索引。如果将 GUID 用作集群索引,则数据插入将无序地分布在整个表中。这意味着您的系统会随机插入和移动页面,从而导致性能问题。

通过标识使数据插入有序是正确的方法。您可以将排序留给索引结构(包含 GUID 的非聚集唯一索引),这比使用表数据进行排序要高效得多。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接