<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Leandro Luiz G. Cavalheiro</title>
    <description>The latest articles on Forem by Leandro Luiz G. Cavalheiro (@leandrocavalheiro).</description>
    <link>https://forem.com/leandrocavalheiro</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1203673%2Fdd1fb07c-7ea9-417a-afcd-0432289fd4a1.png</url>
      <title>Forem: Leandro Luiz G. Cavalheiro</title>
      <link>https://forem.com/leandrocavalheiro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/leandrocavalheiro"/>
    <language>en</language>
    <item>
      <title>GUID como Chave Primária</title>
      <dc:creator>Leandro Luiz G. Cavalheiro</dc:creator>
      <pubDate>Wed, 09 Oct 2024 13:03:55 +0000</pubDate>
      <link>https://forem.com/leandrocavalheiro/guid-ordenavel-29o9</link>
      <guid>https://forem.com/leandrocavalheiro/guid-ordenavel-29o9</guid>
      <description>&lt;p&gt;A muito se discute sobre usar ou não usar GUID ( ou UUID para outras linguagens ). Fato é, que temos vantagens e desvantagens ao usarmos essa abordagem, e que não irei abordar todas por aqui, já que existe muitos vídeos e artigos sobre o assunto. Aqui pretendo olhar para apenas um desses pontos "contra" ou pelo menos polêmico: o uso do GUID como chave primária. &lt;br&gt;
A polêmica toda é em torno da perda de performance ao gravar esse dado no banco de dados, devido a sua característica de não ser ordenável, uma vez que é gerado aleatoriamente ( mesmo que ainda siga algumas regras ).&lt;br&gt;
Então bora entender se realmente devemos evitar um GUID como chave primária. E lembrando, que minhas referências, logicamente serão para .NET nos códigos mostrados, mas esse assunto é agnóstico quanto a linguagem de programação. &lt;/p&gt;

&lt;p&gt;Comumente, no .NET, usamos o gerador de GUID nativo, lá do namespace System e esse gerador nos devolve, um GUID na versão 4, que é um modelo de GUID não ordenável. Exemplo a seguir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var myId = Guid.NewGuid();
//75bdd495-698a-487e-a792-3a5be46bdf6e
//3e448b53-b53b-42d0-9ce2-afa666b20671
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E por se tratar de um padrão já conhecido, não vou me aprofundar, mas, notamos que realmente são valores aleatórios, fazendo o banco banco trabalhar além do que deveria para reordenar essa PK a cada novo registro inserido no banco e em um possível &lt;em&gt;select&lt;/em&gt; ordenado pela PK, o segundo valor gerado em nosso exemplo acima, viria para a primeira posição, "bagunçando" assim nossos registros.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mas então, não é boa ideia utilizarmos GUID em uma PK, certo?&lt;/strong&gt;&lt;br&gt;
Olhando única e exclusivamente para esse ponto, realmente o uso seria desencorajado. Mas como disse no início do post, existem outros pontos a serem levados em consideração, para a escolha do um GUID como PK.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Então como solucionar ( ou minimizar ) esse efeito colateral?&lt;/strong&gt;&lt;br&gt;
O .Net gera nativamente GUIDs na versão 4, que são GUIDs randômicos, mas a boa notícia é que temos o GUID na versão 7. Na v7, esses GUIDs são ordenáveis: sim, isso mesmo ordenáveis.  E melhor, ainda assim, é mantida a aleatoriedade, ou seja, temos um dado ordenável, que não exigirá trabalho extra do banco de dados ao inserir um novo registro e de quebra ainda é um valor com parte randômica, ideal para uso em nossas APIs. E lembrando, que teremos essa nova versão do GUID nativamente no .NET, que será lançado em Novembro de 2024.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mas como é possível?&lt;/strong&gt;&lt;br&gt;
Aí que entra a "mágica".&lt;br&gt;
Um GUID é formado por 16 bytes, e na v7 os 6 primeiros bytes representa a data em que foi gerado. O código a seguir exemplifica como é gerado essa parte do GUID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Span&amp;lt;byte&amp;gt; uuidAsBytes = stackalloc byte[16];
var currentDate = BitConverter.GetBytes(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
if (BitConverter.IsLittleEndian)        
   Array.Reverse(currentDate);
currentDate.AsSpan(2..8).CopyTo(uuidAsBytes);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ToUnixTimeMilliseconds, retorna um long, que representa os milissegundos passados desde 01/01/1970 e que pode representar um range quase infinito de datas, tanto para o passado, quanto para o futuro.&lt;br&gt;
Então, nessa linha, temos os bytes que representam o ponto no tempo onde foi gerado o valor.&lt;/p&gt;

&lt;p&gt;Temos que nos atentar, a existência de sistemas BigEndian e LittleEndian, onde resumindo, são formas que os bytes são organizados na memória. Sendo que em sistemas LittleEndian, vamos inverter os bytes, para que o byte mais significativo sejam armazenados primeiro, e isso, é importante para o próximo passo.&lt;/p&gt;

&lt;p&gt;Como dito antes, usamos 6 bytes para representação de data, e temos 8 bytes gerados nos passos anteriores.  Então, como não precisamos das representação de milhões de anos, podemos descartar os 2 primeiros bytes (aqui a importância dos bytes mais significativos primeiro), e utilizar apenas os 6 últimos, assim temos nosso carimbo de tempo, para nosso UUID.&lt;/p&gt;

&lt;p&gt;Agora, temos a parte randômica que é mais simples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RandomNumberGenerator.Fill(uuidAsBytes[6..]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simplesmente iremos usar a RandomNumberGenerator do namespace System.Security.Cryptography, para preencher o restante do bytes, com números randômicos.&lt;/p&gt;

&lt;p&gt;O próximo passo é ajustar o byte 6, para definir que é um GUID v7, com as seguintes ações:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uuidAsBytes[6] &amp;amp;= 0x0F;
uuidAsBytes[6] += 0x70;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;uuidAsBytes[6] &amp;amp;= 0x0F: Limpa (zera) os 4 bits mais significativos do 6º byte.&lt;br&gt;
uuidAsBytes[6] += 0x70: Define os 4 bits mais significativos como 0111 para indicar que este GUID é da versão 7.&lt;/p&gt;

&lt;p&gt;E pronto, temos nosso GUID v7. &lt;br&gt;
Abaixo o código completo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static Guid GuidV7()
{
    Span&amp;lt;byte&amp;gt; uuidAsBytes = stackalloc byte[16];
    var currentDate = BitConverter.GetBytes(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
    if (BitConverter.IsLittleEndian)        
        Array.Reverse(currentDate);
    currentDate.AsSpan(2..8).CopyTo(uuidAsBytes);

    RandomNumberGenerator.Fill(uuidAsBytes[6..]);
    uuidAsBytes[6] &amp;amp;= 0x0F;
    uuidAsBytes[6] += 0x70;
    return new(uuidAsBytes, true);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E aqui um benchmark de geração de GUIDs que fiz comparando as versões 4 e 7:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykg49mz87vpil6b3byip.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykg49mz87vpil6b3byip.jpg" alt="Image description" width="702" height="163"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mesmo com a chegada desse recurso nativamente logo mais no .NET, sei que muitos devs só utilizam as versões LTS e como o .NET 9 não é LTS, criei um método e adicionei em um Nuget: &lt;strong&gt;WeNerds.Commons&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Para utilização é bem simples, basta instalar o pacote e após importar com using, chamar o método estático WeMethods.NewGuid();&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using WeNerds.Commons;
var myGuidV7 = WeMethods.NewGuid();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>csharp</category>
      <category>backend</category>
      <category>dotnet</category>
      <category>backenddevelopment</category>
    </item>
  </channel>
</rss>
