使用GUID作为主键,特别是关于性能的最佳实践是什么?
我有一个在几乎所有表中都使用GUID作为主键的应用程序,并且我读过使用GUID作为主键时存在性能问题。 老实说,我没有看到任何问题,但我即将开始一个新的应用程序,我仍然想要使用GUID作为主键,但我想使用复合主键(GUID和可能是另一个字段。)
我使用的是一个GUID,因为当你有不同的环境,如“生产”,“testing”和“开发”数据库,以及数据库之间的迁移数据,它们很好,易于pipe理。
我将使用entity framework4.3,我想在应用程序代码中分配Guid,然后将其插入到数据库中。 (即我不想让SQL生成Guid)。
创build基于GUID的主键的最佳做法是什么,以避免与此方法相关的假定的性能命中?
GUID似乎是你的主键的一个自然select – 如果你真的必须的话,你可能会争论使用它作为表的PRIMARY KEY。 我强烈build议不要做的是使用GUID列作为默认的SQL Server默认的集群密钥 ,除非你明确地告诉它不要。
你真的需要分开两个问题:
-
主键是一个逻辑结构 – 唯一可靠地标识表中每一行的候选键之一。 这可以是任何东西,真的 – 一个
INT
,一个GUID
,一个string – select最适合你的场景。 -
聚簇键 (在表中定义“聚簇索引”的列或列) – 这是一个物理存储相关的东西,在这里,一个小的,稳定的,不断增长的数据types是你最好的select
INT
或BIGINT
作为您的默认选项。
默认情况下,SQL Server表上的主键也被用作集群键 – 但这并不需要那样! 将以前基于GUID的主/集群键分解为两个单独的键(GUID上的主键(逻辑)键和单独的INT IDENTITY(1,1)
上的集群(sorting)键) INT IDENTITY(1,1)
栏。
正如Kimberly Tripp (索引的女王)和其他人已经说过很多次那样, GUID
作为集群的关键并不是最优的,因为它的随机性会导致大量的页面和索引碎片,并且性能通常很差。
是的,我知道 – 在SQL Server 2005中有newsequentialid()
,但即使这样也不是真正完全顺序的,因此也会遇到与GUID
相同的问题 – 只是稍微突出一些。
那么还有一个问题需要考虑:表中的集群键将被添加到表中每个非聚集索引中的每个条目上 – 因此您确实要确保它尽可能小。 通常情况下,对于绝大多数表而言,具有超过250亿行的INT
应该足够了,并且与GUID
作为集群密钥相比,您可以为磁盘和服务器内存节省数百兆的存储空间。
快速计算 – 使用INT
与GUID
作为主键和集群键:
- 1'000'000行的基表(3.8 MB比15.26 MB)
- 6非集群索引(22.89 MB比91.55 MB)
总计:25 MB与106 MB – 这只是一张桌子!
还有更多的想法 – 金伯利·特里普(Kimberly Tripp)的优秀作品 – 阅读,再读一遍,消化它! 这是SQL Server索引福音,真的。
- GUID作为PRIMARY KEY和/或集群密钥
- 聚集指数的争论仍在继续
- 不断增加的集群密钥 – 聚集索引辩论……….再次!
- 磁盘空间很便宜 – 这不是重点!
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
(否则它被创build为你的聚集索引,默认情况下) – 然后你创build第二个索引被定义为CLUSTERED
这将起作用 – 如果您有一个需要“重新devise”性能的现有系统,这是一个有效的select。 对于一个新的系统,如果你从头开始,而不是复制场景,那么我总是selectID INT IDENTITY(1,1)
作为我的集群主键 – 比其他任何东西都更有效率!
自2005年以来,我一直使用GUID作为PK。在这个分布式数据库世界里,这绝对是合并分布式数据的最好方法。 您可以触发并忘记合并表,而不必担心整个连接表匹配ints。 GUIDs连接可以被复制而不用担心。
这是我使用GUID的设置:
-
PK = GUID。 GUID的索引类似于string,因此高行表(超过5000万条logging)可能需要表分区或其他性能技术。 SQL Server变得非常高效,所以性能问题越来越不适用。
-
PK Guid是非聚集索引。 除非是NewSequentialID,否则永远不要对GUID进行聚簇索引。 但即使如此,服务器重新启动将导致重大的中断订购。
-
将ClusterID Int添加到每个表。 这是您的CLUSTERED索引…订购您的表格。
-
joinClusterID(int)效率更高,但是我使用了2千万到3千万个logging表,所以joinGUID不会明显影响性能。 如果要获得最佳性能,请使用ClusterID概念作为您的主键并joinClusterID。
这是我的电子邮件表…
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)
如果您使用GUID作为主键并创build聚集索引,那么我build议使用它的缺省值NEWSEQUENTIALID()
这个链接说,它比我能做得更好,并帮助我做出决定。 我通常select一个int作为主键,除非我有一个特定的需要不,我也让SQL服务器自动生成/维护这个领域,除非我有一些特定的原因不。 实际上,性能问题需要根据您的特定应用程序来确定。 这里有很多因素,包括但不限于预期的分贝大小,适当的索引,有效的查询等等。 尽pipe人们可能会不同意,但我认为在很多情况下,您不会注意到两种方法的区别,您应该select更适合您的应用程序的内容,以及可以更轻松,更快速,更有效地进行开发(如果您从未完成应用程序剩下的有什么不同:)。
PS我不知道为什么你会使用复合PK或你相信会给你什么好处。
我目前正在开发一个EF核心的Web应用程序,这里是我使用的模式:
我所有的类(表)和int PK和FK。 我有一个Guidtypes(由C#构造函数生成)的非聚集索引的附加列。
EF中表的所有连接都通过int键进行pipe理,而外部(控制器)的所有访问都是通过Guid完成的。
这个解决scheme允许不显示URL上的int键,但保持模型整洁和快速。