2012-07-09 21 views
2

我正在將數據庫從Microsoft SQL Server遷移到MySQL/MariaDB。在MSSQL上,數據庫對所有主鍵使用uniqueidentifier(GUID)數據類型。 NHibernate用於在數據庫和應用程序之間映射數據,並且採用guid.comb策略來生成GUID以避免聚簇索引的碎片化。如何避免在MySQL中使用NHibernate guid.comb主鍵時出現碎片?

MySQL沒有專用的GUID數據類型,新數據庫模式對所有標識符都使用BINARY(16)。在不對NHibernate映射做任何更改的情況下,我可以啓動我們的應用程序,堅持新的實體並從MySQL數據庫加載它們。大!但是,事實證明,在BINARY(16)列中產生了不連續的非序列GUID,導致不可接受的索引碎片。

讀到這個問題,事實證明MSSQL has a quite special method for sorting GUIDs。這16個字節先按最後六個字節排序,然後再按前後排列的順序排列,而我的天真的MySQL實現先排序第一個字節,然後排序第二個字節,然後轉發。

這導致我的問題:如何避免這種MySQL數據庫中的碎片,同時保持現有的GUIDs和guid.comb策略?我自己有一個想法(下面張貼),但我不禁感到我可能錯過了一些東西。當然,以前其他人一定處理過這個問題,也許有一個簡單的方法來解決它。

回答

3

由於observed by Alberto Ferraridiscussed here on StackOverflow,Microsoft SQL Server通過按特定順序比較字節來排序GUID。由於MySQL會對BINARY(16)進行排序,所以我們只需在讀取/寫入數據庫時​​對字節進行重新排序。

NHibernate允許我們定義自定義數據類型,它可以用在數據庫和對象之間的映射中。我實現了一個BinaryGuidType,它能夠按照MSSQL對GUID進行排序並將它們重新排序爲Guid(byte[])構造函數所接受的格式的方式對Guid.ToByteArray()生成的字節進行重新排序。

字節順序是這樣的:

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

保存System.GuidBINARY(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); 

讀取的字節回一個System.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]; 
} 

Full source code for the BinaryGuidType here.

這似乎工作得很好。在一個表中創建並保存10.000個新對象,它們完全按順序存儲,沒有索引碎片的跡象。

相關問題