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

我之所以使用GUID,是因为当你有不同的环境,如“生产”、“测试”和“开发”数据库时,它们很好且易于管理,而且还用于在数据库之间迁移数据。

我将使用实体框架4.3,我想在应用程序代码中分配Guid,然后将其插入数据库。(例如,我不想让SQL生成Guid)。

为了避免与此方法相关的性能损失,创建基于gui的主键的最佳实践是什么?


This link says it better than I could and helped in my decision making. I usually opt for an int as a primary key, unless I have a specific need not to and I also let SQL server auto-generate/maintain this field unless I have some specific reason not to. In reality, performance concerns need to be determined based on your specific app. There are many factors at play here including but not limited to expected db size, proper indexing, efficient querying, and more. Although people may disagree, I think in many scenarios you will not notice a difference with either option and you should choose what is more appropriate for your app and what allows you to develop easier, quicker, and more effectively (If you never complete the app what difference does the rest make :).

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

附注:我不确定你为什么要使用复合PK,或者你认为这会给你带来什么好处。

guid似乎是主键的自然选择——如果确实必须,可能会主张将它用于表的primary key。我强烈建议不要使用GUID列作为集群键,SQL Server默认是这样做的,除非您特别告诉它不要这样做。

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

主键是一个逻辑结构——唯一且可靠地标识表中的每一行的候选键之一。这可以是任何东西——一个INT,一个GUID,一个字符串——选择对你的场景最有意义的。 聚类键(在表上定义“聚类索引”的一列或多列)——这是一个与物理存储相关的东西,在这里,一个小的、稳定的、不断增长的数据类型是您的最佳选择——INT或BIGINT作为默认选项。

默认情况下,SQL Server表上的主键也被用作集群键——但这并不需要这样!我个人看到过将先前基于GUID的主键/集群键分解为两个单独的键——GUID上的主键(逻辑键)和单独的INT IDENTITY(1,1)列上的集群键(排序键)——获得了巨大的性能提升。

正如索引女王Kimberly Tripp和其他人多次指出的那样,将GUID作为聚类键并不是最优的,因为它的随机性,它将导致大量的页面和索引碎片,并且通常会导致糟糕的性能。

是的,我知道-在SQL Server 2005及更高版本中有newsequentialid() -但即使是它也不是真正的和完全的顺序的,因此也遭受与GUID相同的问题-只是没有那么突出。

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

快速计算-使用INT vs. GUID为主键和聚类键:

1'000'000行基本表(3.8 MB vs. 15.26 MB) 6个非聚集索引(22.89 MB vs. 91.55 MB)

总计:25mb vs. 106mb——这只是在单个表上!

金伯利·特里普的精彩作品——再读一遍,消化它!它是SQL Server索引的福音,真的。

guid作为主键和/或聚集键 关于聚集指数的争论仍在继续 不断增加的聚类关键-聚类索引辩论..........再次! 磁盘空间很便宜——这不是重点!

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)

基本上:你只需要显式地告诉PRIMARY KEY约束它是NONCLUSTERED(否则默认情况下它会被创建为你的聚集索引)——然后你创建第二个定义为clustered的索引

这是可行的——如果您的现有系统需要“重新设计”以提高性能,那么这是一个有效的选择。对于一个新系统,如果从头开始,并且不是在复制场景中,那么我总是选择ID INT IDENTITY(1,1)作为我的集群主键——这比其他任何方法都要高效得多!

如果您使用GUID作为主键并创建聚集索引,那么我建议使用默认的NEWSEQUENTIALID()值。

我从2005年开始使用guid作为pk。在这个分布式数据库世界中,它绝对是合并分布式数据的最佳方法。您可以触发并忘记合并表,而不必担心在连接的表之间进行整型匹配。可以毫无顾虑地复制guid连接。

这是我使用guid的设置:

PK = GUID. GUIDs are indexed similar to strings, so high row tables (over 50 million records) may need table partitioning or other performance techniques. SQL Server is getting extremely efficient, so performance concerns are less and less applicable. PK Guid is NON-Clustered index. Never cluster index a GUID unless it is NewSequentialID. But even then, a server reboot will cause major breaks in ordering. Add ClusterID Int to every table. This is your CLUSTERED Index... that orders your table. Joining on ClusterIDs (int) is more efficient, but I work with 20-30 million record tables, so joining on GUIDs doesn't visibly affect performance. If you want max performance, use the ClusterID concept as your primary key & join on 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)

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

我所有的类(表)都有一个int PK和FK。 然后,我有一个类型为Guid的附加列(由c#构造函数生成),列上有一个非聚集索引。

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

这个解决方案允许在url上不显示int键,但保持模型整洁和快速。

大多数情况下,它不应该用作表的主键,因为它确实会影响数据库的性能。 关于GUID对性能的影响和作为主键的有用链接。

https://www.sqlskills.com/blogs/kimberly/disk-space-is-cheap/ https://www.sqlskills.com/blogs/kimberly/guids-as-primary-keys-andor-the-clustering-key/

使用顺序ID会让黑客或数据挖掘者更容易破坏你的网站和数据。在为网站选择PK时,请记住这一点。

不要在用户界面中公开Id的另一个原因是,竞争对手可以看到您的Id在一天或其他时间段内的增量,从而推断出您正在做的业务量。

好吧,如果您的数据从来没有达到数百万行,那就没问题了。如果你问我,我从来不使用GUID作为任何类型的数据库标识列,包括PK,即使你强迫我用霰弹枪在头上设计。

使用GUID作为主键是一个决定性的缩放停止器,而且是一个关键的。 我建议您检查数据库标识和序列选项。序列是独立于表的,可以为您的需求提供解决方案(MS SQL有序列)。

如果你的表最多达到几千万行,例如5000万行,你将无法在可接受的时间读/写信息,甚至标准的数据库索引维护也将变得不可能。

然后您需要使用分区,并且可扩展到5亿甚至1- 20亿行。添加分区的方式不是最简单的事情,所有读/写语句必须包括分区列(完整的应用程序更改!)

这些数字(5000万和5亿)当然是为轻选择使用。如果您需要以复杂的方式选择信息和/或有大量的插入/更新/删除,对于一个非常苛刻的系统,这些甚至可能是1-2百万和5千万。如果您还添加了完整恢复模型、高可用性和无维护窗口等现代系统常见的因素,情况就会变得非常糟糕。

注意,在这一点上,20亿是int的限制,看起来很糟糕,但int是4倍小,是一个顺序类型的数据,小的大小和顺序类型是数据库可伸缩性的首要因素。你可以使用big int,它只小了两倍,但仍然是顺序的,顺序是非常重要的,甚至比大小更重要,当涉及到数百万或数十亿行的时候。

如果GUID也是聚集的,情况就更糟了。插入一个新行实际上会随机存储在物理位置的任何位置。

即使只是一个列,不是PK或PK部分,只是索引它是麻烦的。从碎片化的角度来看。

有一个guid列是完全可以的,就像任何varchar列一样,只要你不使用它作为PK部分,通常作为连接表的键列。您的数据库必须有自己的PK元素,使用它们过滤和连接数据-过滤后也通过GUID是完全可以的。