2

Microsoft SQL Server から MySQL/MariaDB にデータベースを移行中です。MSSQL では、データベースはuniqueidentifierすべての主キーに (GUID) データ型を使用します。NHibernate を使用してデータベースとアプリケーションの間でデータをマップしguid.comb、クラスター化されたインデックスの断片化を回避するために GUID 生成にこの戦略を採用しています。

MySQL には専用の GUID データ型がないため、新しいデータベース スキーマはBINARY(16)すべての識別子に使用します。NHibernate マッピングに変更を加えることなく、アプリケーションを起動し、新しいエンティティを保持して、MySQL データベースからそれらをロードすることができます。すごい!ただし、順次生成された GUID はBINARY(16)列内で非常に非順次に並べられており、許容できないインデックスの断片化が発生していることが判明しました。

問題を読んでみると、MSSQL には GUID をソートするための非常に特殊な方法があることがわかりました。16 バイトは、最初に最後の 6 バイトで並べ替えられ、次に前置されたグループの逆順で並べ替えられますが、私の単純な MySQL 実装では、最初のバイトが最初に並べ替えられ、次に次のバイトが並べ替えられます。

そして、これは私の質問につながります:既存のGUIDとguid.comb戦略を維持しながら、MySQLデータベースでこの断片化を回避するにはどうすればよいですか? 私は自分で解決策のアイデアを持っていますが(以下に投稿)、何かを見逃したのではないかと感じずにはいられません。確かに、他の人は以前にこの問題に対処したに違いありません。また、簡単な回避方法があるかもしれません。

4

1 に答える 1

3

Alberto Ferrari が観察し、ここ StackOverflow で説明したように、Microsoft SQL Server は特定の順序でバイトを比較して GUID を並べ替えます。MySQL はBINARY(16)「順方向」にソートするため、データベースの読み取り/書き込み時にバイトを並べ替えるだけで済みます。

NHibernate を使用すると、データベースとオブジェクト間のマッピングで使用できるカスタム データ型を定義できます。MSSQL が GUID を並べ替える方法に従ってBinaryGuidType生成されたバイトを並べ替え、コンストラクターが受け入れる形式に戻すことができる を実装しました。Guid.ToByteArray()Guid(byte[])

バイト順は次のようになります。

int[] ByteOrder = new[] { 10,11,12,13,14,15,8,9,6,7,4,5,0,1,2,3 };

System.Guidaを aに保存するには、次のBINARY(16)ようにします。

var bytes = ((Guid) value).ToByteArray();
var reorderedBytes = new byte[16];

for (var i = 0; i < 16; i++)
{
    reorderedBytes[i] = bytes[ByteOrder[i]];
}

NHibernateUtil.Binary.NullSafeSet(cmd, reorderedBytes, index);

バイトを aSystem.Guidに読み込むと、次のようになります。

var bytes = (byte[]) NHibernateUtil.Binary.NullSafeGet(rs, names[0]);
if (bytes == null || bytes.Length == 0) return null;

var reorderedBytes = new byte[16];

for (var i = 0 ; i < 16; i++)
{
    reorderedBytes[ByteOrder[i]] = bytes[i];
}

ここの完全なソースコードBinaryGuidType

これはうまくいくようです。テーブルに 10,000 個の新しいオブジェクトを作成して永続化すると、それらは完全に順番に格納され、インデックスの断片化の兆候はありません。

于 2012-07-09T11:39:33.583 に答える