2012-07-09 24 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个新对象,它们完全按顺序存储,没有索引碎片的迹象。

相关问题