Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

18 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 8 de octubre de 2024
Imagen de una fábrica creando GUIDs

Probablemente, todos hemos usado en algún momento el método Guid.NewGuid() de .NET para generar identificadores únicos globales. Y muchos hemos sufrido con el problema que supone su (pseudo) aleatoriedad, principalmente cuando necesitamos ordenarlos... sobre todo si su destino final es acabar siendo el campo clave de una base de datos relacional.

Pero pocos éramos conscientes de que el método Guid.NewGuid() de .NET retorna un GUID versión 4, y que existen otras versiones de UUID (¡hasta 8!) que pueden resultar más beneficiosas en determinados escenarios.

En particular, los UUIDs versión 7 son generados (al menos teóricamente) siguiendo un orden natural, lo que permite almacenarlos de forma secuencial y recuperarlos en el mismo orden, gracias a que en su creación se usa un timestamp para proporcionar secuencialidad, combinado con valores aleatorios para aportar la unicidad propia de estos identificadores.

La cuestión es que .NET 9 ha añadido un nuevo método estático a la clase Guid que permite crear GUIDs versión 7: Guid.CreateVersion7(). Para que podáis ver la diferencia con el tradicional NewGuid(), vamos a generar algunos GUIDs con éste último:

Console.WriteLine("Creating v4 GUIDs:");
for (int i = 0; i < 10; i++)
{
    Console.WriteLine(Guid.NewGuid());
}
Creating v4 GUIDs:
96499bb0-6a32-43ed-9a10-fc197870a810
074c3c21-a6ef-4d5b-a66d-21167a6e8be0
9dad3441-0534-4351-823a-d60662728a18
a141fe8e-d6f9-40ae-9854-4748b8c0107f
a05565cc-fe02-433d-86f1-4c8c5a84c5e4
f2286470-445a-40d5-b7ab-b23fcfed6a1c
8b4a771a-7162-42b9-a475-ec1c473a6582
3162d279-144b-429f-b4c8-6be7735171e1
6942610d-c7b7-4366-943c-37d3e2ad369c
d29603a3-e3d8-45c4-b9a5-d2f8fe24c964

Como podréis observar, los identificadores son completamente aleatorios, sin orden aparente. Ahora, vamos a crear algunos GUIDs versión 7:

Console.WriteLine("Creating v7 GUIDs:");
for (int i = 0; i < 10; i++)
{
    Console.WriteLine(Guid.CreateVersion7());
}
Creating v7 GUIDs:
019219a9-516a-7c79-8106-0b8441f9ab43
019219a9-516b-7d02-9781-c77f25095756
019219a9-516b-7851-87fd-5bc6eb0ee404
019219a9-516b-7c0d-b335-0b4846e7e09e
019219a9-516b-7eee-82b2-012155dcac8f
019219a9-516b-7737-a1ff-7ffe53570d9d
019219a9-516b-738d-8699-c69ca9e7e944
019219a9-516b-7e69-84f9-c95c8c8981f3
019219a9-516b-76e9-ae3d-3fdde59ddad8
019219a9-516c-7394-bbac-a3828d3c5611

Fijaos en un detalle importante: aunque a primera vista parece que los valores están ordenados, si nos detenemos un poco veremos que hay algunos que no lo están. Esto es debido a que, según la especificación de la IETF, sólo se reservan para el timestamp los primeros 48 bits, rellenándose el resto con valores aleatorios. Y dado que se usa como referencia el Unix Epoch (milisegundos desde el 1 de enero de 1970), es posible que dos GUIDs generados en el mismo milisegundo no estén ordenados.

Esto podemos comprobarlo introduciendo una pequeña espera entre cada generación de GUIDs. Ahora obtendremos un resultado ordenado, porque aseguramos que cada uno de ellos se ha generado en un milisegundo distinto:

Console.WriteLine("Creating v7 GUIDs with delay:");
for (int i = 0; i < 10; i++)
{
    Thread.Sleep(1);
    Console.WriteLine(Guid.CreateVersion7());
}
Creating v7 GUIDs with delay:
0192381d-83ad-7548-b873-4dd00f737d99
0192381d-83bd-7a69-835e-af2f12ebdc83
0192381d-83cd-7b88-a4c3-b5972dde1aa4
0192381d-83dc-7fac-8afa-1ab485bd80cd
0192381d-83ec-7f0a-b236-b04fa35a40d5
0192381d-83fb-796a-8546-b6134d307f96
0192381d-840b-789d-9aa8-dcdf35598d6f
0192381d-841a-7a5f-b845-90eac2aad978
0192381d-8429-76a9-852d-717eb2965253
0192381d-8439-7d6f-a581-c452c19be749

Por tanto, debemos saber que CreateVersion7() no garantiza que los GUIDs generados estén ordenados, sobre todo:

  • Si estamos generando identificadores a un ritmo elevado (más de uno en cada milisegundo).
  • En escenarios con concurrencia, donde varios hilos podrían estar generando GUIDs en el mismo momento.
  • En sistemas distribuidos o escalados horizontalmente, donde varios servidores podrían estar generando GUIDs simultáneamente.

Pero bueno, si nuestro escenario es más básico, la inclusión de CreateVersion7() en .NET 9 al menos nos facilitará su uso.

Como punto extra, también es interesante saber que a la estructura Guid se le ha añadido la propiedad Version, que permite obtener la versión de un GUID:

var guid4 = Guid.NewGuid();
Console.WriteLine(guid4 + ", version: " + guid4.Version);

var guid7 = Guid.CreateVersion7();
Console.WriteLine(guid7 + ", version " + guid7.Version);
04fd0245-219e-4e3c-b15f-89da92d2bdba, version: 4
0192383b-8ef8-7a12-ab35-7d07905b6fcf, version 7

¡Espero que os sea de utilidad!

Publicado en Variable not found.

Aún no hay comentarios, ¡sé el primero!