GUIDを主キーとして使用してデータベース設計を修正する最適なソリューション


18

私は、このアイデアをある程度確認した後、パフォーマンスの悪いデータベースを修正するか、データベースがある場合はより良い提案をします。常により良い提案を受け入れます。

GUIDをPKとして使用している非常に大規模なデータベース(1日あたり約20万件増加する2000万件以上のレコード)があります。

私の側の見落としですが、PKはSQLサーバーにクラスター化されており、パフォーマンスの問題を引き起こしています。

GUIDの理由-このデータベースは他の150個のデータベースと部分的に同期されているため、PKは一意である必要がありました。同期はSQL Serverによって管理されるのではなく、システムの要件に合わせてデータの同期を維持するカスタムプロセスが構築され、すべてがそのGUIDに基づいています。

150のリモートデータベースのそれぞれは、中央のSQLデータベースに格納されているような完全なデータを格納しません。実際に必要なデータのサブセットのみを保存し、必要なデータはそれらに固有ではありません(150のデータベースのうち10が他のサイトデータベースからの同じレコードの一部を持っている場合があります-それらは共有しています)。また、データは実際には中央サイトではなくリモートサイトで生成されるため、GUIDが必要です。

中央データベースは、すべての同期を維持するためだけでなく、3000以上のユーザーからのクエリがその非常に大きな断片化されたデータベースに対して実行されます。すでにこれは初期テストの大きな問題です。

幸いなことに、私たちはまだ生きていません。必要に応じて変更を加えてオフラインにすることができます。これは少なくとも何かです。

リモートデータベースのパフォーマンスは問題ではありません。データサブセットは非常に小さく、通常、データベースの合計サイズが1GBを超えることはありません。レコードはメインシステムに非常に定期的にフィードバックされ、不要になったときに小さいBDから削除されます。

すべてのレコードのキーパーである中央DBのパフォーマンスは、その多くのレコードの主キーとしてのクラスター化されたGUIDのために悲惨です。インデックスの断片化はチャートから外れています。

だから-パフォーマンスの問題を修正するための私の考えは、新しい列を作成することです-符号なしBIGINT IDENTITY(1,1)し、テーブルBIGINT列のクラスターPKを変更します。

主キーであるGUIDフィールドに一意の非クラスター化インデックスを作成します。

小規模なリモート150データベースは、Central SQL Serverデータベースの新しいPKについて知る必要はありません。これは、データベース内のデータを整理し、パフォーマンスの低下と断片化を防ぐために純粋に使用されます。

これは機能し、中央のSQLデータベースのパフォーマンスを向上させ、将来のインデックスの断片化を防ぎます(もちろん)。または、ここで非常に重要な何かを見逃したことがあります。


2
@mattytommo同意します。
ポールフレミング

2
少なくとも週に1回、インデックスの最適化を実行していますか?
アンドマー

1
クラスター化する意味のあるものはありますか?つまり、どのクエリが高速になりますか?GUIDの範囲スキャンは絶対に行わないので、自動インクリメントを選択する代わりに、クエリ時間に最適なクラスタリングを選択できるかどうかを検討してください。そうでない場合は、先に進み、bigint

2
@Borik素晴らしいアイデアではありません。彼が持っているものと成長率に基づいて、彼はint4255日(11.5年)で消耗します。彼がそうするなら、彼はあなたを11。5年で非難するだけです;)
mattytommo

1
逆の見解:GUI​​Dデータ型が問題だと思うのはなぜですか?128ビット整数です。なぜ64ビット整数(bigint)または32ビット整数(int)に置き換えると、速度に顕著な違いが生じると思いますか?フラグメンテーションにつながるすべてのページ分割を避けるために、クラスター化キーを他の何かに間違いなく変更する必要があると思いますが、データ型が問題であることが確実でない限り、データ型を変更する必要はないと思います。
グリーンストーンウォーカー

回答:


8

確かにGUIDでクラスタリングする必要はありません。そのGUID 以外のレコードを一意に識別できるものがある場合は、他のフィールドに一意のインデックスを作成し、そのインデックスをクラスター化することをお勧めします。そうでない場合は、一意でないインデックスを使用しても、他のフィールドに自由にクラスタリングできます。ただし、クラスター化するアプローチは、データの分割とクエリの実行を最も容易にします。つまり、「地域」フィールドなどがある場合、それはクラスター化スキームの候補になります。

に変更する際の問題はBIGINT、他のデータベースからのデータへの追加と、それらのデータベースを中央ストアに統合することです。これは考慮されていない場合-となります決して考慮しない-そして、そう、BIGINTうまくインデックスリバランスの問題を解決するだろう。

舞台裏では、クラスター化インデックスを指定しない場合、SQL Serverはほぼ同じことを行います。行IDフィールドを作成し、他のすべてのインデックスをその中にマップします。したがって、自分で実行することで、SQLが解決するのと同じように解決できます。


テーブルの唯一の真に一意なフィールドはGUDです-他の列は一意ではなく、最初から一意である可能性のある列の組み合わせがありますが、時間が経つと重複レコードが生成される可能性がわずかにあります。非常にリモートですが、データの性質を考慮すると可能です。検索パフォーマンスなどを改善するために、他のすべての非クラスター化インデックスがクラスター化インデックスを参照していることを読みました。GUIDがクラスター化PKを持たないと、パフォーマンスに影響を与えませんか?私はスペースに気づいていますが、懸念があります-パフォーマンスが最重要です。
ロドルズ

クラスタ化インデックスを指定しない場合、パフォーマンスの低下は、SQLがバックグラウンドで1つを作成し、他のすべてのインデックスをそのインデックスにマップすることです。したがって、あなたの場合、SQLを実行させることでパフォーマンスが向上します。これは、現在、ディスク上のすべてのデータを常にシャッフルして、ソート順が重要でないときにソート順を維持しているためです。より多くのストレージスペースが必要になりますが、ストレージが大幅に改善され、取得への影響が最小限/まったくなくなります。
デビッドT.マックネット

だから私が推測する質問は、BIGINT Clustered PKを実行せず、PKをNon Clustered GUIDに変更するだけである場合、パフォーマンスへの影響は何ですか?テーブルには、頻繁に検索される他の非クラスター化インデックスがあります。これは、これらの検索のパフォーマンスに影響しますか?
ロドルズ

+1 GUIDを使用することもお勧めします。分散システムでそれらを置き換えるのは非常に困難です。大きなテーブルのクラスター化インデックスは、データのクエリ方法に基づいて明確にする必要があります
レムスルサヌ

1
こんにちはGuys-ただの更新-変更を加え、PKをGUIDで非クラスター化したので、SQL Serverは2百万件のレコードをデータベースに挿入するのに忙しくなります。データが挿入されると同時に、データベースに情報を照会し、変更前の10分でタイムアウトするクエリを1〜2秒で完了することができました。そのため、PKをクラスター化せず、BIGINTを心配することはうまく機能しているように見えます。皆の入力と支援に感謝します。
ロドルズ

1

それは大変な注文です。

中間者アプローチを提案させてください。

System.Guid.NewGuid()がランダムなGUIDを生成する際に問題が発生していました。(私は、データベースに依存してシーケンシャル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




*/

代替案:

メインデータベースとリモートデータベースが「リンク」されている場合(sp_linkserverなど)......メインウィンドウを「uuidジェネレータ」として使用できます。

uuidの「1つずつ」を取得したくない場合、それはあまりにもおしゃべりです。

ただし、一連の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を持っているという意味でない限り、
ロドルズ

150の「リモート」データベースに1つずつ挿入されていますか?または、夜間にバルクセットでデータを移動していますか?だから、あなたはちょっと岩と難しい場所の間です。bigintを使用すると、最終的には(おそらく)スペースが不足しますが、多くのデータベース全体で一意の値を取得する必要があります。だからここに私の急進的なアイデアがあります。150のリモートデータベースは、中央サービスからUUIDを取得できますか?それは一つのアイデアです。150のリモートデータベースは(sp_addlinkedserverのように)メインデータベースに「リンク」されていますか?次に、考慮される可能性のあるUDFがあります。見つけられるかどうか見てみましょう。
グラナダコーダー

ここでsequentialidさんについて協議(私はすでに書いたものに関連していないが、私はその面白いと思う)という記事であるcodeproject.com/Articles/388157/...
granadaCoder

0

説明に基づいて、BIGINTを使用します。ただし、GUIDはとにかくグローバルに一意であるため、GUIDのインデックスは一意でなくてもかまいません。


-1

uniqueidentifierがパフォーマンスの問題を起こさないようにGUIDが適切に保存されている場合、およびシーケンシャルGUIDをさらに適切に使用できる場合...

また、@ mattytommoにはINTを使用して約11.5年の良い点があります...


はい-しかし、GUIDはSQL Serverデータベースではなく、リモート150データベースで生成されます-したがって、sequentialguidを使用することはできませんが、応答に感謝します。
ロドルズ

その場合、私の意見ではあなたの計画は健全なものです、私は私が管理しているDBの1つで同様のことをしましたプルアップすると、GUID(Index)がトラッカーとして保持され、それがどこから発生したかを追跡できます。しかし、私のモチベーションは...より省スペースからだった
Borikの

回答と洞察に感謝します。:)
ロドルズ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.