Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Como evitar a fragmentação ao usar a chave primária NHibernate guid.comb no MySQL?


Como observado por Alberto Ferrari e discutido aqui no StackOverflow , o Microsoft SQL Server classifica os GUIDs comparando os bytes em uma ordem específica. Como o MySQL classificará um BINARY(16) "direto", tudo o que precisamos fazer é reordenar os bytes ao ler/gravar no banco de dados.

NHibernate nos permite definir tipos de dados personalizados, que podem ser usados ​​em mapeamentos entre banco de dados e objetos. Eu implementei um BinaryGuidType , capaz de reordenar os bytes produzidos pelo Guid.ToByteArray() de acordo com a maneira como o MSSQL classifica os GUIDs e os reordena de volta para o formato aceito pelo Guid(byte[]) construtor.

A ordem dos bytes fica assim:
int[] ByteOrder = new[] { 10,11,12,13,14,15,8,9,6,7,4,5,0,1,2,3 };

Salvando um System.Guid para um BINARY(16) fica assim:
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);

Lendo os bytes de volta para um System.Guid fica assim:
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];
}

Código-fonte completo para o BinaryGuidType aqui.

Isso parece funcionar bem. Criando e persistindo 10.000 novos objetos em uma tabela, eles são armazenados de forma totalmente sequencial, sem sinais de fragmentação do índice.