UUIDs vs. ULIDs
Why do we use UUIDs?
UUID is one of the most commonly used universal identifiers in software development. However, new alternatives have challenged its existence over the past few years.
Most systems have moved on from using basic sequential IDs to using UUIDs (Universally Unique IDentifiers), with whom they achieve universal uniqueness across the whole system, because the probability of collision between two UUIDs is so minuscule that we can say it’s practically 0.
?
Another benefit that these IDs provide is their unpredictability. In a RESTful API scenario where we attach IDs to the URL, it would be impossible for an ‘outside’ user to guess the previous or next ID of a customer in our system:
Using sequential IDs (easy guess of prev user in the system: 15–1=14 ): https://ali-alachkar.netlify.app/api/ideas/15
Using UUIDs (no way we’re guessing the prev or next user in our system): https://ali-alachkar.netlify.app/api/ideas/11c105dc-74e4–41b2-a014-bced748111a3
???? What is UUID?
A universally unique identifier (UUID), also known as a globally unique identifier (GUID), uses 128 bits to store random identifiers. For example, the number of random version-4 UUIDs that need to be generated in order to have a 50% probability of at least one collision is 2.71 quintillion. This number is equivalent to generating 1 billion UUIDs per second for about 85 years.
UUIDs are often represented as 32-digit hexadecimal strings:
123e4567-e89b-12d3-a456-426614174000
Pros
Cons
Then, What is ULID?
ULIDs are also using 128 bits to store identifiers, but this time the first 48 bits are used for timestamps while the rest of the 80 bits are used for randomness. This is a key difference between it and UUID because it gives you the possibility to sort them. ULID is even lexicographically sortable, which gives us the ability to sort them as strings!
As the identifier is now bound to timestamps, we can create as many as 1.21e+24 unique ULIDs per millisecond. While UUID theoretically can't be duplicated in practice, it happens that some faulty implementations of concepts leave people with multiple identifiers for different data. While human error is still possible with ULIDs, it's a lot more unlikely.
ULIDs are represented as a 26-character string:
0001EHZADJ5FDB1RJS00JK7VD8
From this ULID we can extract timestamps and random parts:
2019-07-05 15:49:06 5FDB1RJS00JK7VD8
?? Why should we consider ULIDs?
Universally Unique Lexicographically Sortable Identifier
It comes with some amazing features that resolve some of the drawbacks of UUID. For example, when using UUID with relational databases, there can be difficulties indexing the data due to the lack of inbuilt ordering. In such situations, you might be forced to include another attribute to make the data sortable.
ULIDs offer everything that UUIDs offer, but in addition:
领英推荐
?? The structure of a ULID
ULID consists of two main components:
which are then combined into one, forming the 26-character and 128-bit compatible ID.
The first 10 characters of the ULID represent the timestamp, and the second part of the ULID represents randomness. Both of these parts are base 32-encoded strings and are represented using 48 bits and 80 bits, respectively.
?? What do we gain from the sortability of ULIDs?
The main gain, in my opinion, is simplified indexing. Sorting ULIDs simplifies the process of indexing and querying data by creation time.
By utilizing ULIDs as a part of the primary key or indexed field in databases or search engines, we can achieve efficient sorting of records based on their creation order. (removing the need to explicitly add a created_on field to the DB table)
By sorting ULIDs in ascending order, we can retrieve data in the order it was created. This removes the need to explicitly add a created_on field to our table and write more extensive database queries.
? ULIDs and security
ULID takes a different approach compared to many random ID generators that, for example, in JavaScript, rely on the potentially unsafe Math.random() function. ULID addresses this concern by disabling the usage of Math.random() by default.
Instead, ULID automatically selects an appropriate random number generator based on the specific situation or context in which it is being used. But by default, it uses CSPRNGs (system-provided or platform-specific ones, coming from external libraries for that purpose).
ULIDs can also leverage system randomness or configurable options as tools for generating random characters.
?? Multi-Language Support
ULID supports nearly 50 languages, including JavaScript, Java, C++, Dart, Python, and .NET.
Also, binary representations are available for more than 15 languages, including C++, Dart, Go, JavaScript, and Python.
?? Future Focus
Based on many expert opinions on StackOverflow, there are no significant disadvantages or limitations to using ULID.
However, case insensitivity and 80-bit randomness are the main disadvantages developers notice in ULID. But its lexicographic sorting ability makes it unique among all the others.
? Examples for UUIDs and ULIDs
ULID (Universally Unique Lexicographically Sortable Identifier) examples:
A ULID is a 128-bit label. It is monotonically rising, sortable, and has millisecond precision.
01ES8M1EYQNCPG8P0ZQSYZ75RP
01ES8MBQAKTZWTGSZMZP48T0DA
01ES8MCA2N8DRJDW86XQRZPD96
01ES8MEVDTRN3H04J9FVWQ56B7
01ES8MH69Q8ET3HYTM88D5BT5T
01ES8MK0P4FGVPD9J3TAVGFCV0
UUID (Universally Unique Identifier) examples:
f47ac10b-58cc-4372-a567-0e02b2c3d479
d8e92261-1040-46c3-b85e-774d8a143a02
5f71fe9e-7385-4c42-89e9-61a8c24b25c5
92e26f80-0b6f-4cf4-b52a-5b3b7f5e1a49
b7e646d3-e618-4e75-9546-8fc081a4fb2b