使用GUID作为主键修复数据库设计的最佳解决方案


18

在对这一想法进行确认后,我将修复性能不佳的数据库或一个更好的建议(如果有的话)。始终欢迎更好的建议。

我有一个非常大的数据库(20+百万条记录,每天增长约1/2百万条),它们使用GUID作为PK。

我的疏忽大意是,但是PK聚集在SQL Server上,并导致性能问题。

产生引导的原因-该数据库与150个其他数据库部分同步,因此PK需要唯一。同步不是由SQL Server管理的,而是建立了一个自定义过程,该过程使数据保持同步以符合系统要求-全部基于该GUID。

150个远程数据库中的每个数据库都不存储中央SQL数据库中存储的完整数据。他们只存储他们实际需要的数据的子集,并且需求的数据并不是他们唯一的(例如,在150个数据库中,有10个可能具有其他站点数据库中的某些相同记录-它们共享)。另外-数据实际上是在远程站点而不是在中心点生成的,因此需要GUID。

中央数据库不仅用于使所有内容保持同步,而且还将针对该非常大的碎片数据库执行来自3000多个用户的查询。在初始测试中,这已经是一个大问题。

幸运的是我们还没有上线-因此我可以进行更改,并在需要时将其脱机,至少这是必须的。

远程数据库的性能不是问题-数据子集非常小,并且数据库的总大小通常不会超过1GB。记录被定期地反馈到主系统,并在不再需要时从较小的BD中删除。

保留所有记录的中央数据库的性能令人担忧-由于群集GUID是许多记录的主键。索引碎片不在图表上。

所以-我想解决性能问题的想法是创建一个新列-Unsigned BIGINT IDENTITY(1,1),然后更改表BIGINT列的Clustered PK。

我将在GUID字段上创建唯一非聚集索引,该索引是主键。

较小的远程150数据库不需要了解Central SQL Server数据库上的新PK-它仅用于组织数据库中的数据并阻止不良的性能和碎片。

这样做是否可以提高中央SQL数据库的性能,并防止将来出现索引碎片地狱(在一定程度上)?还是我错过了这里很重要的东西,它会跳起来咬我,引起更多的悲伤?


2
@mattytommo我同意。
保罗·弗莱明

2
您是否每周至少运行一次磁盘碎片整理
Andomar

1
您有什么有意义的东西可以集群吗?即,什么查询应该很快?绝对不会在GUI上进行范围扫描,因此,不只是选择自动增量,而是考虑是否可以选择一些查询时最佳聚类。如果不是,那就继续使用bigint

2
@Borik并不是一个好主意,根据他的身体状况和成长速度,他将int在4255天(11.5年)内耗尽。如果他那样做,他只会在11.5年内怪你;)
mattytommo

1
相反的看法:为什么您认为GUID数据类型是个问题?它是一个128位整数。您为什么认为用64位整数(bigint)或32位整数(int)替换它会在速度上产生明显差异?我认为您绝对应该将群集密钥更改为其他名称,以避免导致导致碎片的所有页面拆分,但是除非您非常确定数据类型是问题,否则我不应该更改数据类型。
Greenstone Walker

Answers:


8

您当然不需要在GUID上群集。如果您有可以唯一标识该GUID 以外的记录的内容,建议您考虑在该其他字段上构建唯一索引并对该索引进行聚类。如果不是这样,即使使用非唯一索引,您也可以自由聚集在其他字段上。集群的方法不过是最好的方法,它可以方便地拆分数据并进行查询-因此,如果您有“区域”字段或其他内容,则可能是您的集群方案的候选人。

更改为a的问题BIGINT将是添加其他数据库的数据并将其数据库集成到中央存储中。如果这不是一个考虑因素,并且永远不会成为一个考虑因素,那么可以,BIGINT它将很好地解决索引重新平衡问题。

在后台,如果您不指定聚簇索引,则SQL Server会做很多相同的事情:它创建一个行ID字段并将所有其他索引映射到该字段中。因此,通过自己完成操作,就可以像SQL一样解决它。


该表中唯一真正唯一的字段是GUD-其他列也不是唯一的,并且有一些组合在一起的列开始时可能是唯一的-但是随着时间的流逝,它们会产生重复记录的可能性很小。非常遥远,但鉴于数据的性质,这是可能的。我已经读到所有其他非聚集索引都引用聚集索引以提高搜索性能等。将聚集PK作为GUID会不会对性能产生影响?我知道空间,尽管有一个顾虑,但性能至关重要。
Roddles

如果不指定聚簇索引,性能会受到影响,因为SQL会在后台为您创建一个聚簇索引,并将所有其他索引映射到该索引中。因此,在您的情况下,通过执行SQL可以提高性能,因为现在您不停地对磁盘上的所有数据进行混洗以在排序顺序不重要时保留排序顺序。您将需要更多的存储空间,但将看到存储方面的巨大改进,并且对检索的影响很小/没有影响。
David T. Macknet

所以我想的问题是,如果我不执行BIGINT集群PK,而只是将PK更改为非集群GUID,那么对性能的影响是什么?表上还有其他非聚集索引,这些索引将被频繁搜索。这会影响这些搜索的效果吗?
Roddles

+1我也建议您继续使用GUID。在分布式系统中很难替换它们。根据查询数据的方式,大表聚集索引应该显而易见。
Remus Rusanu

1
嗨,大家好-只是更新-我进行了修改,使PK成为基于GUID的非群集设备,SQL Server忙于将2百万条记录插入数据库中。在插入数据的同时,我能够在数据库中查询信息,而这些查询在更改之前有时会在10分钟后超时,大约需要1-2秒。因此-使PK不成簇并且不担心BIGINT似乎工作得很好。非常感谢大家的投入和帮助。
Roddles

1

这是一个很高的要求。

让我建议一个中间人的方法。

我在使用System.Guid.NewGuid()生成随机向导时遇到问题。(我允许客户端创建自己的guid,而不是依靠数据库创建序列号)。

一旦移到客户端的UuidCreateSequential,我的性能就会好很多,尤其是在INSERT上。

这是DotNet客户端代码voodoo。我确定我是从某处典当的:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;


namespace MyCompany.MyTechnology
{
  public static class Guid
  {


    [DllImport("rpcrt4.dll", SetLastError = true)]
    static extern int UuidCreateSequential(out System.Guid guid);


    public static System.Guid NewGuid()
    {
      return CreateSequentialUUID();
    }


    public static System.Guid CreateSequentialUUID()
    {
      const int RPC_S_OK = 0;
      System.Guid g;
      int hr = UuidCreateSequential(out g);
      if (hr != RPC_S_OK)
        throw new ApplicationException("UuidCreateSequential failed: " + hr);
      return g;
    }


  }
}














    /*

Original Reference for Code:
http://www.pinvoke.net/default.aspx/rpcrt4/UuidCreateSequential.html


*/

/*



Text From URL above:

UuidCreateSequential (rpcrt4)

Type a page name and press Enter. You'll jump to the page if it exists, or you can create it if it doesn't.
To create a page in a module other than rpcrt4, prefix the name with the module name and a period.
. Summary
Creates a new UUID 
C# Signature:
[DllImport("rpcrt4.dll", SetLastError=true)]
static extern int UuidCreateSequential(out Guid guid);


VB Signature:
Declare Function UuidCreateSequential Lib "rpcrt4.dll" (ByRef id As Guid) As Integer


User-Defined Types:
None.

Notes:
Microsoft changed the UuidCreate function so it no longer uses the machine's MAC address as part of the UUID. Since CoCreateGuid calls UuidCreate to get its GUID, its output also changed. If you still like the GUIDs to be generated in sequential order (helpful for keeping a related group of GUIDs together in the system registry), you can use the UuidCreateSequential function.

CoCreateGuid generates random-looking GUIDs like these:

92E60A8A-2A99-4F53-9A71-AC69BD7E4D75
BB88FD63-DAC2-4B15-8ADF-1D502E64B92F
28F8800C-C804-4F0F-B6F1-24BFC4D4EE80
EBD133A6-6CF3-4ADA-B723-A8177B70D268
B10A35C0-F012-4EC1-9D24-3CC91D2B7122



UuidCreateSequential generates sequential GUIDs like these:

19F287B4-8830-11D9-8BFC-000CF1ADC5B7
19F287B5-8830-11D9-8BFC-000CF1ADC5B7
19F287B6-8830-11D9-8BFC-000CF1ADC5B7
19F287B7-8830-11D9-8BFC-000CF1ADC5B7
19F287B8-8830-11D9-8BFC-000CF1ADC5B7



Here is a summary of the differences in the output of UuidCreateSequential:

The last six bytes reveal your MAC address 
Several GUIDs generated in a row are sequential 
Tips & Tricks:
Please add some!

Sample Code in C#:
static Guid UuidCreateSequential()
{
   const int RPC_S_OK = 0;
   Guid g;
   int hr = UuidCreateSequential(out g);
   if (hr != RPC_S_OK)
     throw new ApplicationException
       ("UuidCreateSequential failed: " + hr);
   return g;
}



Sample Code in VB:
Sub Main()
   Dim myId As Guid
   Dim code As Integer
   code = UuidCreateSequential(myId)
   If code <> 0 Then
     Console.WriteLine("UuidCreateSequential failed: {0}", code)
   Else
     Console.WriteLine(myId)
   End If
End Sub




*/

替代想法:

如果您的主数据库和远程数据库是“链接的”(如sp_linkserver)……那么您可以将主数据库用作“ uuid生成器”。

您不想得到uuid的“一对一”信息,这太闲谈了。

但是您可以抓取一组uuid。

下面是一些代码:

IF EXISTS (SELECT * FROM sys.objects WHERE object_id =
 OBJECT_ID(N'[dbo].[uspNewSequentialUUIDCreateRange]') AND type in (N'P',
 N'PC'))

 DROP PROCEDURE [dbo].[uspNewSequentialUUIDCreateRange]

 GO



 CREATE PROCEDURE [dbo].[uspNewSequentialUUIDCreateRange] (

 @newUUIDCount int --return

 )

 AS

 SET NOCOUNT ON

 declare @t table ( dummyid int , entryid int identity(1,1) , uuid
 uniqueidentifier default newsequentialid() )

 insert into @t ( dummyid ) select top (@newUUIDCount) 0 from dbo.sysobjects
 so with (nolock)

 select entryid , uuid from @t

 SET NOCOUNT OFF

 GO

/ *

--START TEST

 set nocount ON

 Create Table #HolderTable (entryid int , uuid uniqueidentifier )

 declare @NewUUIDCount int

 select @NewUUIDCount = 20

 INSERT INTO #HolderTable EXEC dbo.uspNewSequentialUUIDCreateRange
 @NewUUIDCount

 select * from #HolderTable

 DROP Table #HolderTable

 --END TEST CODE

* /


有趣的-我没有考虑过的方法-我会仔细检查一下,因为它看起来不错,并运行一些测试项目。如果我们有150个数据库生成顺序的guid,并将其报告回中央数据库,那么这仍然不会造成碎片,因为当插入到中央数据库中时,guid仍然是相当随机的。当然,除非您的意思是放弃集群PK而拥有非集群PK?
Roddles

150个“远程”数据库是否一次插入一个?还是他们在晚上以大容量方式移动数据?因此,您有点困难。使用bigint最终会耗尽空间(也许),并且您仍然必须在许多数据库中获得唯一的价值。所以这是我的基本想法。150个远程数据库能否从中央服务获取其UUID?那是一个主意。是否将150个远程数据库“链接”(如在sp_addlinkedserver中)到主数据库?然后,我考虑了一个UDF。让我看看是否可以找到它。
granadaCoder

这是一篇讨论序列编号的文章(与我已经写的无关,我认为它很有趣)codeproject.com/Articles/388157/…–
granadaCoder

0

根据您的描述,选择BIGINT。但是,GUID的索引可能是不唯一的,因为无论如何GUID都被认为是全局唯一的。


-1

如果GUID正确存储为uniqueidentifier应该不会有任何性能问题...并且如果可以更好地使用顺序GUID ...

另外@mattytommo使用INT具有大约11.5年的好点...


是的-但是Guid是在远程150个数据库上生成的,而不是在SQL Server数据库上生成的-所以我不能使用sequenceguid-但是感谢您的答复。
Roddles

在那种情况下,我认为您的计划是一个合理的计划,我在我管理的一个数据库上做了类似的事情,我创建了一个INT DENTITY(1,1)并将其设置为Clustered PK以及人性化的数据标识符拉起,我保持GUID(Index)作为跟踪器,以便能够跟踪其起源。但是我的动力更多来自节省空间……
Borik

非常感谢您的回答和见解。:)
Roddles
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.