我正在将数据库从Microsoft SQL Server迁移到MySQL/MariaDB。在MSSQL上,数据库对所有主键使用uniqueidentifier (GUID)数据类型。使用NHibernate在数据库和应用程序之间映射数据,并使用guid.comb策略生成GUID,以避免聚集索引的碎片。
MySQL没有专用的GUID数据类型,新的数据库模式对所有标识符都使用BINARY(16)。无需对NHibernate映射进行任何更改,我就可以启动我们的应用程序,持久化新实体并从MySQL数据库加载它们。太棒了!然而,事实证明,在BINARY(16)列中,顺序生成的GUID是非常不连续的,从而产生了不可接受的索引碎片。
仔细阅读这个问题,结果发现MSSQL has a quite special method for sorting GUIDs。这16个字节首先按最后6个字节排序,然后按反向分组的前缀排序,而我的朴素的MySQL实现首先按第一个字节排序,然后按下一个字节排序,依此类推。
这就引出了我的问题:如何在保持现有的GUID和guid.comb策略的同时避免MySQL数据库中的这种碎片?我自己有一个解决方案的想法(发表在下面),但我不禁觉得我可能遗漏了什么。当然,其他人以前肯定已经处理过这个问题,也许有一种简单的方法可以绕过它。
发布于 2012-07-09 19:39:34
与observed by Alberto Ferrari和discussed here on StackOverflow一样,Microsoft SQL Server通过按特定顺序比较字节来对GUID进行排序。因为MySQL将对BINARY(16)“直接向前”排序,所以我们需要做的就是在读/写数据库时重新排序字节。
NHibernate允许我们定义自定义数据类型,这些数据类型可用于数据库和对象之间的映射。我已经实现了一个BinaryGuidType,它能够根据Guid.ToByteArray()对GUID进行排序的方式对Guid(byte[])生成的字节进行重新排序,并将它们重新排序为Guid(byte[])构造函数所接受的格式。
字节顺序如下所示:
int[] ByteOrder = new[] { 10,11,12,13,14,15,8,9,6,7,4,5,0,1,2,3 };将System.Guid保存到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);将字节读回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个新对象时,它们完全按顺序存储,没有索引碎片的迹象。
https://stackoverflow.com/questions/11394305
复制相似问题