我有一个应用程序在几乎所有表中都使用GUID作为主键,我已经读过使用GUID作为主键时存在性能问题。老实说,我没有看到任何问题,但我即将开始一个新的应用程序,我仍然想使用GUID作为主键,但我想使用复合主键(GUID和可能是另一个字段) 。)
我正在使用GUID,因为当您拥有不同的环境(例如“生产”,“测试”和“开发”数据库)以及数据库之间的迁移数据时,它们很好且易于管理。
我将使用Entity Framework 4.3,我想在将其插入数据库之前在应用程序代码中分配Guid。 (即我不想让SQL生成Guid)。
创建基于GUID的主键的最佳做法是什么,以避免与此方法相关的假设性能点击?
答案 0 :(得分:444)
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
相同的问题 - 只是少了一点显着如此。
然后还有另一个需要考虑的问题:表格上的聚类键也会被添加到表格中每个非聚集索引的每个条目上 - 因此你真的想确保它尽可能小。通常,对于绝大多数表来说,具有2亿行的INT
应该足够 - 并且与作为群集密钥的GUID
相比,您可以在磁盘上保存数百兆字节的存储空间。服务器内存。
快速计算 - 使用INT
与GUID
作为主要和群集密钥:
TOTAL:25 MB vs. 106 MB - 这只是在一张桌子上!
更多值得思考的东西 - 金佰利特里普的优秀作品 - 阅读,再读一遍,消化它!这是SQL Server索引福音,真的。
更新:如果您希望将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)
作为我的集群主键 - 比其他任何东西都更有效率!
答案 1 :(得分:43)
自2005年以来,我一直使用GUID作为PK。在这个分布式数据库世界中,它绝对是合并分布式数据的最佳方式。您可以触发并忘记合并表,而无需担心连接表中的整数匹配。可以毫无顾虑地复制GUID连接。
这是我使用GUID的设置:
PK = GUID。 GUID的索引类似于字符串,因此高行表(超过5000万条记录)可能需要表分区或其他性能技术。 SQL Server变得非常高效,因此性能问题越来越少适用。
PK Guid是非聚集索引。除非是NewSequentialID,否则永远不要对GUID进行集群索引。但即使这样,服务器重启也会导致订单出现重大中断。
将ClusterID Int添加到每个表。这是你的CLUSTERED索引...订购你的桌子。
加入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)
答案 2 :(得分:5)
我目前正在使用EF Core开发一个Web应用程序,这是我使用的模式:
我的所有类(表)和一个int PK和FK。 我有一个额外的列,其类型为Guid(由c#构造函数生成),上面有非聚集索引。
EF中表的所有连接都是通过int键管理的,而外部(控制器)的所有访问都是通过Guids完成的。
此解决方案不允许在URL上显示int键,但保持模型整洁和快速。
答案 3 :(得分:4)
如果您使用GUID作为主键并创建聚簇索引,那么我建议使用默认的NEWSEQUENTIALID()值
答案 4 :(得分:3)
此链接表明它比我能做得更好,并帮助我做出决策。我通常选择将int作为主键,除非我有特殊需要,我也让SQL服务器自动生成/维护这个字段,除非我有一些特定的理由不这样做。实际上,需要根据您的特定应用确定性能问题。这里有许多因素,包括但不限于预期的数据库大小,正确的索引,有效的查询等。虽然人们可能不同意,但我认为在许多情况下你都不会注意到任何一个选项的差异,你应该选择更适合你的应用程序的东西,以及什么让你更容易,更快,更有效地开发(如果你从未完成应用程序)其余的有什么区别:)。
P.S。我不确定你为什么要使用复合PK,或者你认为会给你什么好处。
答案 5 :(得分:1)
具有顺序ID可以使黑客或数据挖掘者更容易地窃取您的站点和数据。为网站选择PK时请记住这一点。
答案 6 :(得分:0)
在大多数情况下,不应将其用作表的主键,因为它确实会影响数据库的性能。 有关GUID影响性能并作为主键的有用链接。
答案 7 :(得分:0)
好吧,如果您的数据从未达到数百万行,那么您很好。如果你问我,我从不使用 GUID 作为任何类型的数据库标识列,包括 PK,即使你强迫我用霰弹枪设计。
使用 GUID 作为主键是一个明确的扩展限制器,也是一个关键的限制器。 我建议您检查数据库标识和序列选项。序列是独立于表的,可以为您的需求提供解决方案(MS SQL 有序列)。
如果您的表开始最多达到几千万行,例如5000 万,您将无法在可接受的时间读取/写入信息,甚至标准的数据库索引维护也变得不可能。
然后你需要使用分区,并且可以扩展到 50 亿甚至 1-20 亿行。途中添加分区不是最简单的事情,所有读/写语句都必须包含分区列(完整的应用程序更改!)。
当然,这些数字(5000 万和 500 百万)仅用于轻量级选择。如果您需要以复杂的方式选择信息和/或有大量的插入/更新/删除,对于要求非常高的系统,这些甚至可能是 1-2 百万和 50 百万。如果您还添加现代系统常见的完整恢复模型、高可用性和无维护窗口等因素,事情就会变得非常糟糕。
此时请注意,20 亿是 int 限制,看起来很糟糕,但 int 小 4 倍并且是顺序类型的数据,小尺寸和顺序类型是数据库可扩展性的第一因素。并且您可以使用 big int,它只是小两倍但仍然是顺序的,顺序是非常重要的 - 甚至比大小更重要 - 当涉及数百万或数十亿行时。
如果 GUID 也聚集在一起,情况就更糟了。只是插入一个新行实际上会随机存储在物理位置的任何地方。
即使只是一个列,不是PK或PK部分,只是索引它很麻烦。从碎片化的角度来看。
拥有一个 guid 列就像任何 varchar 列一样完全可以,只要您不将其用作 PK 部分,并且通常不用作连接表的键列。您的数据库必须有自己的 PK 元素,使用它们过滤和连接数据 - 之后也可以通过 GUID 进行过滤。
答案 8 :(得分:-1)
不在用户界面中公开 Id 的另一个原因是,竞争对手可以看到您的 Id 在一天或其他时间段内递增,从而推断出您的业务量。