GUID를 기본 키로 사용하여 데이터베이스 디자인을 수정하는 최상의 솔루션


18

성능이 좋지 않은 데이터베이스를 수정하거나 데이터베이스가있는 경우 더 나은 제안을하기 위해이 아이디어를 확인한 후 있습니다. 더 나은 제안에 항상 열려 있습니다.

GUID를 PK로 사용하는 매우 큰 데이터베이스 (하루에 약 2 천만 개가 증가하는 2 천만 건의 레코드)가 있습니다.

내 부분에 대한 감독이지만 PK는 SQL Server에 클러스터되어 성능 문제를 일으 킵니다.

guid의 이유-이 데이터베이스는 150 개의 다른 데이터베이스와 부분적으로 동기화되므로 PK는 고유해야했습니다. 동기화는 SQL Server에서 관리하는 것이 아니라 시스템의 요구 사항에 맞게 데이터를 동기화하는 GUI를 기반으로 구축 된 사용자 지정 프로세스가 있습니다.

150 개의 원격 데이터베이스 각각은 중앙 SQL 데이터베이스에 저장된 전체 데이터를 저장하지 않습니다. 그들은 실제로 필요한 데이터의 하위 집합 만 저장하며 요구하는 데이터는 고유하지 않습니다 (150 개의 데이터베이스 중 10 개는 다른 사이트 데이터베이스와 동일한 레코드 중 일부를 공유 할 수 있음). 또한 데이터는 실제로 중앙 지점이 아닌 원격 사이트에서 생성되므로 GUID가 필요합니다.

중앙 데이터베이스는 모든 것을 동기화 상태로 유지하는 데 사용될뿐만 아니라 3000 명 이상의 사용자의 쿼리가 매우 큰 조각난 데이터베이스에 대해 실행됩니다. 이미 이것은 초기 테스트에서 큰 문제입니다.

다행히도 우리는 아직 살아 있지 않기 때문에 필요한 경우 최소한 무언가를 변경하고 오프라인으로 가져갈 수 있습니다.

원격 데이터베이스의 성능은 문제가되지 않습니다. 데이터 서브 세트가 매우 작으며 데이터베이스의 크기가 총 1GB를 넘지 않습니다. 기록은 주 시스템으로 상당히 정기적으로 피드백되어 더 이상 필요하지 않으면 더 작은 BD에서 제거됩니다.

많은 레코드의 기본 키로 클러스터 된 GUID로 인해 모든 레코드를 보관하는 중앙 DB의 성능이 좋지 않습니다. 인덱스 조각화가 차트에서 벗어났습니다.

따라서 성능 문제를 해결하려는 생각은 새 열-서명되지 않은 BIGINT IDENTITY (1,1)을 만든 다음 테이블 BIGINT 열의 클러스터 된 PK를 변경하는 것입니다.

기본 키인 GUID 필드에 Unique Non Clustered 인덱스를 생성합니다.

작은 원격 150 데이터베이스는 중앙 SQL Server 데이터베이스의 새로운 PK에 대해 알 필요가 없습니다. 순수한 PK는 데이터베이스의 데이터를 구성하고 나쁜 성능과 조각화를 중지하는 데 사용될 것입니다.

이것이 작동하고 중앙 SQL 데이터베이스의 성능을 향상시키고 향후 색인 조각화를 막을 수 있습니까? 아니면 내가 여기서 뛰어 올라 물고 더 슬픔을 일으킬 중요한 것을 놓쳤습니까?


2
@mattytommo 동의합니다.
Paul Fleming

2
적어도 일주일에 한 번 인덱스 조각 모음 을 실행 하고 있습니까?
Andomar

1
클러스터링에 의미가있는 것이 있습니까? 즉, 어떤 쿼리가 빨라야합니까? 확실히 guid에서 범위를 스캔하지 않을 것이므로 자동 증가를 선택하는 대신 선택할 수있는 쿼리 시간 최적의 클러스터링이 있는지 고려하십시오. 그렇지 않다면 계속해서 bigint

2
@Borik 자신이 갖고있는 것과 그의 성장 속도에 따라, 좋은 아이디어는 아니지만, int4255 일 (11.5 년)에 소진 될 것 입니다. 그가 그렇게한다면, 그는 11.5 년 만에 당신을 비난 할 것입니다;)
mattytommo

1
반대 견해 : 왜 GUID 데이터 유형이 문제라고 생각합니까? 128 비트 정수입니다. 64 비트 정수 (bigint) 또는 32 비트 정수 (int)로 바꾸면 속도가 눈에 띄게 달라지는 이유는 무엇입니까? 조각화로 이어지는 모든 페이지 분할을 피하기 위해 클러스터링 키를 다른 것으로 확실히 변경해야한다고 생각하지만 데이터 유형이 문제인지 확실하지 않으면 데이터 유형을 변경하지 않아야한다고 생각합니다.
Greenstone Walker

답변:


8

GUID에서 클러스터링하지 않아도됩니다. 해당 GUID 이외의 레코드를 고유하게 식별 할 수있는 것이 있으면 다른 필드에서 고유 인덱스를 작성하고 해당 인덱스를 클러스터링하는 것이 좋습니다. 그렇지 않은 경우 고유하지 않은 인덱스를 사용해도 다른 필드에서 자유롭게 클러스터링 할 수 있습니다. 그러나 클러스터링하는 방법은 데이터 분할 및 쿼리를 가장 용이하게하는 것입니다. 따라서 "region"필드 또는 이와 유사한 것이 있으면 클러스터링 스키마의 후보가 될 수 있습니다.

로 변경하는 문제 BIGINT는 다른 데이터베이스의 데이터를 추가하고 데이터베이스를 중앙 저장소에 통합하는 것입니다. 이것은 고려하지 않는 경우 - 것이다 결코 다음, 예,이 - 고려있을 BIGINT잘 인덱스 리 밸런싱 문제를 해결할 것입니다.

배후에서 클러스터형 인덱스를 지정하지 않으면 SQL Server는 거의 동일한 작업을 수행합니다. 행 ID 필드를 만들고 다른 모든 인덱스를 매핑합니다. 따라서 직접 수행하면 SQL에서 해결하는 것처럼 해결할 수 있습니다.


테이블에서 유일하게 고유 한 필드는 GUD입니다. 다른 열은 고유하지 않으며 처음에는 고유 한 열 조합이 있지만 시간이 지남에 따라 중복 레코드를 생성 할 가능성이 약간 있습니다. 매우 원격이지만 데이터의 특성상 가능합니다. 클러스터되지 않은 다른 모든 인덱스가 검색 성능 등을 향상시키기 위해 클러스터 된 인덱스를 참조한다는 것을 읽었습니다. GUID로 클러스터 된 PK가 성능에 영향을 미치지 않습니까? 나는 공간을 알고 있으며 걱정하는 동안 성능이 가장 중요합니다.
Roddles

클러스터형 인덱스를 지정하지 않으면 성능 저하가 발생하여 SQL이 사용자를 대신하여 하나를 생성하고 다른 모든 인덱스를 해당 인덱스에 매핑합니다. 따라서 귀하의 경우 에는 정렬 순서가 중요하지 않은 경우 정렬 순서를 유지하기 위해 디스크의 모든 데이터를 지속적으로 섞어서 SQL을 수행함으로써 성능을 향상시킬 수 있습니다. 더 많은 저장 공간이 필요하지만 저장 공간이 크게 향상되고 검색에 미치는 영향은 최소화됩니다.
David T. Macknet

BIGINT Clustered PK를하지 않고 PK를 Non Clustered GUID로 변경하면 성능에 어떤 영향이 있습니까? 자주 검색되는 다른 비 클러스터형 인덱스가 테이블에 있습니다. 이것이 해당 검색 성능에 영향을 줍니까?
Roddles

+1 또한 GUID를 유지하는 것이 좋습니다. 분산 시스템에서 교체하기가 매우 어렵습니다. 데이터 를 쿼리 하는 방법에 따라 빅 테이블 클러스터형 인덱스가 분명해야합니다 .
레무스 루사 누

1
안녕 얘들 아-그냥 업데이트-나는 수정하고 PK를 GUID에서 비 클러스터 클러스터로 만들었고 SQL Server는 2 백만 레코드를 데이터베이스에 삽입하는 중입니다. 동시에 데이터가 삽입되는 동안 데이터베이스에 정보를 쿼리하고 변경 전 시간이 10 분으로 시간이 초과되어 1-2 초 안에 완료된 쿼리를 쿼리 할 수있었습니다. 따라서 PK를 클러스터하지 않고 BIGINT에 대해 걱정하지 않으면 멋지게 작동 한 것으로 보입니다. 모두의 의견과 도움에 감사드립니다.
Roddles

1

큰 주문입니다.

중개인 접근 방식을 제안하겠습니다.

임의의 guid를 생성하는 System.Guid.NewGuid ()에 문제가 있습니다. (클라이언트가 데이터베이스를 사용하여 순차 ID를 작성하는 대신 자체 guid를 작성할 수 있도록 허용했습니다).

클라이언트 측에서 UuidCreateSequential로 이동하면 특히 INSERT에서 성능이 훨씬 향상되었습니다.

다음은 DotNet 클라이언트 코드 부두입니다. 나는 어딘가에서 나왔을 것이라고 확신한다.

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




*/

대체 아이디어 :

주 DB와 원격 DB가 "링크 된"경우 (sp_linkserver에서와 같이) ... 주 DB를 "uuid generator"로 사용할 수 있습니다.

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

* /


흥미롭고 접근하지 않은 접근법-이것이 멋져 보이고 일부 테스트 프로젝트를 실행함에 따라 이것을 자세히 살펴볼 것입니다. 중앙 데이터베이스에 다시보고되는 순차적 guid를 생성하는 150 개의 데이터베이스가있는 경우 중앙 데이터베이스에 삽입 할 때 guid가 여전히 상당히 무작위이므로 조각화가 발생하지 않습니다. 물론 클러스터링 된 PK를 삭제하고 클러스터되지 않은 PK를 가지고 있습니까?
Roddles

150 개의 "원격"데이터베이스가 한 번에 하나씩 삽입됩니까? 아니면 밤에 또는 대량으로 데이터를 대량으로 이동합니까? 그래서 당신은 바위와 힘든 곳 사이에 있습니다. bigint를 사용하면 결국 공간이 부족할 수 있으며 여전히 많은 DB에서 고유 한 가치를 얻어야합니다. 여기에 나의 근본적인 아이디어가 있습니다. 150 개의 원격 데이터베이스가 중앙 서비스에서 UUID를 가져올 수 있습니까? 그게 하나의 아이디어입니다. 150 개의 원격 데이터베이스가 sp_addlinkedserver에서와 같이 주 데이터베이스에 "연결되어"있습니까? 그런 다음 고려해야 할 UDF가 있습니다. 내가 찾을 수 있는지 보자.
granadaCoder

다음은
sequenceid (

0

설명에 따라 BIGINT로 이동하십시오. 그러나 GUID는 전 세계적으로 고유해야하므로 GUID에 대한 인덱스는 고유하지 않을 수 있습니다.


-1

GUID가 uniqueidentifier로 올바르게 저장되어 있으면 성능 문제가 없어야하며 Sequential GUID를 더 잘 사용할 수 있다면 ...

또한 @mattytommo는 INT를 사용하여 약 11.5 년을 지적합니다 ...


예-그러나 guid는 SQL Server 데이터베이스가 아닌 원격 150 데이터베이스에서 생성되므로 순차적 GUID를 사용할 수 없지만 응답에 감사드립니다.
Roddles

이 경우 내 의견으로는 당신의 계획은 건전한 것이고, 내가 관리하는 DB 중 하나에서 비슷한 일을했고, INT DENTITY (1,1)를 만들고 그것을 클러스터 된 PK로 설정하고 데이터에 대한 인간이 읽을 수있는 식별자로 설정했습니다. 당겨서 GUID (Index)를 추적기로 유지하여 원래 위치를 추적 할 수 있습니다. 그러나 저의 동기는 공간 절약에서 비롯되었습니다 ...
Borik

귀하의 답변과 통찰에 감사드립니다. :)
Roddles
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.