使用GUID作为主键的最佳做法是什么,特别是在性能方面?

VAA*_*AAA 311 sql-server database-design entity-framework guid primary-key

我有一个应用程序在几乎所有表中使用GUID作为主键,我已经读过使用GUID作为主键时存在性能问题.老实说,我没有看到任何问题,但我即将开始一个新的应用程序,我仍然想使用GUID作为主键,但我想使用复合主键(GUID和可能是另一个字段) .)

我正在使用GUID,因为当您拥有不同的环境(如"生产","测试"和"开发"数据库)以及数据库之间的迁移数据时,它们很好并且易于管理.

我将使用Entity Framework 4.3,我想在将其插入数据库之前在应用程序代码中分配Guid.(即我不想让SQL生成Guid).

创建基于GUID的主键的最佳做法是什么,以避免与此方法相关的假设性能命中?

mar*_*c_s 463

GUID似乎是您主键的自然选择 - 如果您真的必须,您可能会争辩将其用于表的PRIMARY KEY.我强烈建议不要使用GUID列作为群集密钥,默认情况下SQL Server会执行此操作,除非您明确告知不要这样做.

你真的需要分开两个问题:

  1. 主键是一个逻辑结构-候选键唯一和可靠地识别你的表中每一行的一个.这可以是任何东西,真的 - INT一个GUID,一个字符串 - 选择对你的场景最有意义的东西.

  2. 聚集键(列或定义表上的"聚集索引"列) -这是一个物理存储相关的事情,在这里,一个小的,稳定的,不断增长的数据类型是您最好的挑选- INTBIGINT为您的默认选项.

默认情况下,SQL Server表上的主键也用作群集键 - 但这不一定是这样!我个人看到,在将先前基于GUID的主/群集密钥分解为两个单独的密钥(GUID上的主(逻辑)密钥)和单独INT IDENTITY(1,1)列上的群集(排序)密钥时,可以获得巨大的性能提升.

正如Kimberly Tripp--索引女王 - 和其他人已多次声明 - GUID因为聚类键不是最优的,因为由于其随机性,它将导致大量的页面和索引碎片以及通常不良的性能.

是的,我知道 - newsequentialid()在SQL Server 2005及更高版本中 - 但即使这样也不是真正的,完全顺序的,因此也会遇到与之相同的问题GUID- 只是不那么突出.

然后还有另一个需要考虑的问题:表格上的聚类键也会添加到表格中每个非聚集索引的每个条目上 - 因此,您确实希望确保它尽可能小.通常,INT对于绝大多数表来说,具有2亿个行的行应该足够了 - 并且与GUID作为群集密钥相比,您可以在磁盘和服务器内存中节省数百兆字节的存储空间.

快速计算 - 使用INTvs. GUID作为主要和群集密钥:

  • 基表有1'000'000行(3.8 MB对15.26 MB)
  • 6个非聚簇索引(22.89 MB对91.55 MB)

总计:25 MB对106 MB - 这只是在一张桌子上!

还有一些值得思考的东西 - 金伯利·特里普的优秀作品 - 阅读,再读一遍,消化它!这是SQL Server索引福音,真的.

PS:当然,如果你只处理几百或几千行 - 这些争论中的大部分都不会对你产生太大的影响.但是:如果你进入数十或数十万行,或者你开始数以百万计 - 那么这些点变得非常重要,而且非常重要.

更新:如果您希望将PKGUID列作为主键(但不是群集密钥),将另一列MYINT(INT IDENTITY)作为群集密钥 - 请使用以下命令:

CREATE TABLE dbo.MyTable
(PKGUID UNIQUEIDENTIFIER NOT NULL,
 MyINT INT IDENTITY(1,1) NOT NULL,
 .... add more columns as needed ...... )

ALTER TABLE dbo.MyTable
ADD CONSTRAINT PK_MyTable
PRIMARY KEY NONCLUSTERED (PKGUID)

CREATE UNIQUE CLUSTERED INDEX CIX_MyTable ON dbo.MyTable(MyINT)
Run Code Online (Sandbox Code Playgroud)

基本上:你只需要明确告诉PRIMARY KEY它的约束NONCLUSTERED(否则它默认创建为你的聚簇索引) - 然后你创建一个定义为的第二个索引CLUSTERED

这将有效 - 如果您的现有系统需要"重新设计"以提高性能,那么这是一个有效的选项.对于一个新系统,如果你从头开始,并且你不在复制场景中,那么我总是选择ID INT IDENTITY(1,1)我的集群主键 - 比其他任何东西都更有效率!

  • @Kipei:主要问题是***IF***你有这么自然的价值 - 然后是的,你可以用它作为主键.***但是:例如`DATETIME`这样的值对于聚类键是非常有用的,因为它们只有3.33ms的精度,因此可以存在重复.所以在这种情况下,你仍然*需要一个`INT IDENTITY` - 因此,我通常默认使用它,因为我20多年的经验,一个真正可用的*自然键*几乎从未真正存在.. .. (5认同)
  • 小问题,现在应该在连接上使用 GUID 还是 int id?我的直觉告诉我应该使用 GUID,但我没有看到使用 int id 的技术问题...... (3认同)
  • @marc_s但是在复制场景中,如果int列是identity,我们不应该使用GUID,因为int列可以跨设备重复吗? (3认同)
  • 这是一个旧线程,但我可以补充一下:不要只使用无用的任意 INT 作为聚类键。使用一些有用的东西,比如实际搜索的递增日期,它与您存储的数据有某种关系。你只得到一个集群键,如果你选择正确的你会得到很好的性能 (3认同)
  • 这是一个很好的答案,我要提到的一件事是,在插入之前能够生成密钥通常很有用。使用“ newsequentialid()”可以帮助群集,但是这需要SQL的附加往返。因此,“代理键”方法的另一个好处是,您可以在客户端生成新的id,而不必担心索引碎片。 (2认同)
  • 我读这个的方式是既有一个非聚集的uniqueidentifier列又有一个int identity列,FK也应该是uniqueidentifier?如果你这样做,你何时会直接使用标识列,或者不是吗? (2认同)

Rob*_*ood 48

自2005年以来,我一直在使用GUID作为PK.在这个分布式数据库世界中,它绝对是合并分布式数据的最佳方式.您可以触发并忘记合并表,而无需担心连接表中的整数匹配.可以毫无顾虑地复制GUID连接.

这是我使用GUID的设置:

  1. PK = GUID.GUID的索引类似于字符串,因此高行表(超过5000万条记录)可能需要表分区或其他性能技术.SQL Server变得非常高效,因此性能问题越来越少.

  2. PK Guid是非聚集索引.除非是NewSequentialID,否则永远不要对GUID进行集群索引.但即使这样,服务器重启也会导致订单出现重大中断.

  3. 将ClusterID Int添加到每个表.这是你的CLUSTERED索引...订购你的桌子.

  4. 加入ClusterIDs(int)效率更高,但我使用了2千万到3千万个记录表,因此加入GUID并不会明显影响性能.如果要获得最高性能,请使用ClusterID概念作为主键并加入ClusterID.

这是我的电子邮件表...

CREATE TABLE [Core].[Email] (
    [EmailID]      UNIQUEIDENTIFIER CONSTRAINT [DF_Email_EmailID] DEFAULT (newsequentialid()) NOT NULL,        
    [EmailAddress] NVARCHAR (50)    CONSTRAINT [DF_Email_EmailAddress] DEFAULT ('') NOT NULL,        
    [CreatedDate]  DATETIME         CONSTRAINT [DF_Email_CreatedDate] DEFAULT (getutcdate()) NOT NULL,      
    [ClusterID] INT NOT NULL IDENTITY,
    CONSTRAINT [PK_Email] PRIMARY KEY NonCLUSTERED ([EmailID] ASC)
);
GO

CREATE UNIQUE CLUSTERED INDEX [IX_Email_ClusterID] ON [Core].[Email] ([ClusterID])
GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_Email_EmailAddress] ON [Core].[Email] ([EmailAddress] Asc)
Run Code Online (Sandbox Code Playgroud)

  • 你打赌 索引发生了两件事:1.在ClusterID上集群-在磁盘上排序表(碎片为0%)。2.基于电子邮件ID的非群集-索引电子邮件ID字段以加快GUID ID查找。GUID字段查找的行为类似于字符串,因此如果没有索引,则EmailID查找将很慢。 (2认同)

Eri*_*ser 7

我目前正在使用EF Core开发一个Web应用程序,这是我使用的模式:

我所有的类(表)和一个int PK和FK.我有一个额外的列,其类型为Guid(由c#构造函数生成),其上包含非聚集索引.

EF中表的所有连接都是通过int键管理的,而外部(控制器)的所有访问都是通过Guids完成的.

此解决方案允许不在URL上显示int键,但保持模型整洁和快速.


Mat*_*att 5

这个链接比我能说的更好,并帮助我做出决定。我通常选择 int 作为主键,除非我有特定的需要,并且我也让 SQL Server 自动生成/维护这个字段,除非我有一些特定的原因不这样做。实际上,性能问题需要根据您的特定应用程序来确定。这里有很多因素在起作用,包括但不限于预期的数据库大小、正确的索引、高效的查询等等。尽管人们可能不同意,但我认为在很多情况下您都不会注意到任何一个选项的差异,您应该选择更适合您的应用程序的选项以及允许您更轻松、更快、更有效地开发的选项(如果您从未完成应用程序)其余的有什么区别:)。

https://web.archive.org/web/20120812080710/http://databases.aspfaq.com/database/what-should-i-choose-for-my-primary-key.html

PS 我不确定您为什么要使用复合 PK 或您认为这会给您带来什么好处。