Sortable Guids using NewId

How sortable, but unique, Guids can be generated using the NewId library

Home DailyDrop

Daily Knowledge Drop

Often Guids are used as database primary keys, as they are "guaranteed" to be unique and random or un-guessable. However one drawback of them, is that due to their uniqueness there is no ordering, which leads to index fragmentation, an increase in database size, as well as a potential performance degradation.

The NewId library, can assist with this, by generating unique, but sortable Guids.


Guid

In C#, Guid's are mostly created using the Guid.NewGuid method (there are other ways, such as using this technique to create a deterministic Guid)

Using Guid.NewGuid to created 10 Guids:

for (int i = 0; i < 10; i++)
{
    var id = Guid.NewGuid();
    Console.WriteLine(id);
}

We can see, as by design, the values of the 10 Guids are random and are unrelated to one another:

7620e975-26e6-46ca-876d-dff93495ad57
24751db7-6372-4b8c-94fd-2ae3ad92f926
c8791bf1-f85a-4b12-81e7-18cab1516cb9
5a78214c-adb6-4d59-8921-bca021b50b22
e46a7e0e-faf4-4af3-bd2e-39a1102af894
807ad5c4-7314-4ebb-8914-3b9a0f38386f
ca536113-ea85-41a6-844f-891d8aab40e4
44cf6ff1-89da-458f-8f69-57d4c68c73cf
b18e4911-e439-4506-9f23-b5017ce82d55
225557ab-a5eb-48e1-b04e-7a72696e85ca

As mentioned, having unique values is the desired goal, but having them be completely random can cause issues if they are being used as database primary keys.
The non-sequential nature of this approach leads to index fragmentation, but also means records cannot be sorted by this field, and requires another field (e.g. DateCreated) to be sortable.


NewId

The Newid library addresses these issues, by creating unique, semi-random, sortable Ids.

The library is very easy to use, with the NewId.Next() method used to produce an Id. It is also possible to set a start Id, or a start byte-array, from quick subsequent Ids are based.

Using NewId to created 10 Ids:

for (int i = 0; i < 10; i++)
{
    var newId = NewId.Next().ToGuid();
    Console.WriteLine(newId);
}

The 10 unique, but sortable Guids are as follows:

df1d0000-0a83-7cd3-fdf2-08da40cae578
df1d0000-0a83-7cd3-467a-08da40cae579
df1d0000-0a83-7cd3-48ba-08da40cae579
df1d0000-0a83-7cd3-49de-08da40cae579
df1d0000-0a83-7cd3-4aba-08da40cae579
df1d0000-0a83-7cd3-4b95-08da40cae579
df1d0000-0a83-7cd3-4c93-08da40cae579
df1d0000-0a83-7cd3-4d6e-08da40cae579
df1d0000-0a83-7cd3-4e8b-08da40cae579
df1d0000-0a83-7cd3-4f5c-08da40cae579

As one can see, the Guids are unique and sorted, but don't look entirely random - this is because some parts are in fact not entirely random.

The Id generated is made up of three portions:

  • df1d0000-0a83-7cd3: this portion is the same for all Ids generated by the same process/worker Id. This will be constant on a particular machine, but will differ PC to PC.
  • 4f5c: this portion will change with each Id generated.
  • 08da40cae579: this portion increments, but slowly and not for every Id generated (as is evident in the the above example)

There are three sources of information are used to construct the Id:

  • A process/worker Id: unique and constant for each PC
  • A timestamp: by definition, incrementing and thus orderable
  • A sequence: this is an incrementing Id

Generating a NewId is just as easy as generating a Guid directly - and are directly convertible to a Guid (as was done in the above code example)


Notes

NewId is not suitable for every situation - in some cases you would want the generated Id to be completely random and un-guessable. However, if you are using Guids as database primary keys, consider using NewId to generate the Ids instead of Guid.NewGuid.

I appreciate the fact that, while the library does generate its own type (also called NewId), this type is effectively a Guid, and can easy be converted to one. This means that any underlying entities which currently use a Guid do not need to be updated with a new type, or reference a third party package - they can be kept "clean".


References

NewId
Generating sortable Guids using NewId

Daily Drop 98: 17-06-2022

At the start of 2022 I set myself the goal of learning one new coding related piece of knowledge a day.
It could be anything - some.NET / C# functionality I wasn't aware of, a design practice, a cool new coding technique, or just something I find interesting. It could be something I knew at one point but had forgotten, or something completely new, which I may or may never actually use.

The Daily Drop is a record of these pieces of knowledge - writing about and summarizing them helps re-enforce the information for myself, as well as potentially helps others learn something new as well.
c# .net guid newid