6
头图

UUID is one of the most commonly used universal identifiers in software development. However, over the past few years, new alternatives have challenged its existence.

Among them, ULID is one of the leading competitors because it provides sortable unique IDs.

In this article, I'll discuss ULID's features with examples so you can better understand when to use it.


Learn about ULIDs and how to use them

ULID stands for Universal Unique Alphabetical Identifier. It has over 271K NPM downloads and 1.7K GitHub Stars per week.

You can easily install the ULID NPM library and use it in your project with the npm i ulid command.

import { ulid } from ‘ulid’;
ulid();

It has some amazing features and addresses some of the shortcomings of UUIDs. For example, when using UUIDs in relational databases, difficulties with data indexing can arise due to the lack of built-in sorting. In this case, you might be forced to include another property to make the data sortable.

Additionally, UUIDs have some common problems with randomness, efficiency, and generation, which ULIDs solve. So let's take a closer look at ULIDs.

Using both timestamps and randomness

When you use UUID to generate an ID, it will only consider randomness or timestamp, generating a long string of 36 characters.

However, ULID takes both randomness and timestamp into account to generate IDs and encodes them as 26 strings (128 bits).

// UUID示例
01FHZXHK8PTP9FVK99Z66GXQTX

The first 10 characters of the ULID represent the timestamp, and the second part of the ULID represents randomness. Both parts are base 32 encoded strings, represented using 48 and 80 bits, respectively.

For example, the decomposition of the above ULID looks like this:

01FHZXHK8PTP9FVK99Z66GXQTX
时间戳 (48 bits) - 01FHZXHK8P
随机数 (80 bits) - TP9FVK99Z66GXQTX
Note : ULIDs are encoded using Crockford's Base32 alphabet ( 0123456789ABCDEFGHJKMNPQRSTVWXYZ ). It does not include the I, L, O and U letters to avoid any unintended confusion.

UILD is sorted lexicographically

Dictionary sortability is one of the most prominent features of ULID.

As we already know, ULIDs can be sorted. This feature of ULID allows developers to easily manage database-related tasks such as sorting, partitioning, and indexing.

For example, you don't need to create an extra column to maintain record creation time. Instead, you can use the timestamp representation of the ULID to sort or divide the data based on creation time.

Note : The timestamp portion of the ULID is expressed in UNIX time (in milliseconds) and will not run out of space until 10889 AD.

High security for random numbers

Most random ID generators use unsafeMath.random() to generate IDs. However, ULID blocks the use of Math.random() by default and automatically determines the appropriate random number generator based on the situation.

For example, it will use crypto.getRandomValues in the browser and crypto.randomBytes in the Node environment.

However, if you want to use Math.random() in the ULID, you need to explicitly allow this permission.

import { factory, detectPrng } from 'ulid'

const random_number_gen = detectPrng(true) 
const ulid = factory(random_number_gen)
Note : You can also use your own pseudo-random number generator to generate the ULID.

Monotonic ULIDs and Seed Time

ULID allows you to get an ID with the same timestamp by passing a seed time. For example, if you want to create an ID with a timestamp of 2021–10–15, you need to pass the UNIX timestamp (in milliseconds) to the ulid() function.

ulid(1634263671000) // 01FJ0V986RA01G70YQ5Z0AMQE7

Among other things, ULIDs allow the creation of a series of IDs with increasing values. All you need to do is create a ulid object with monotonicFactory and pass the same time seed.

import { monotonicFactory } from ‘ulid’
const ulid = monotonicFactory()
console.log(ulid(100000)); // 00000031N0J7R2B57M8YG73J7M
console.log(ulid(100000)); // 00000031N0J7R2B57M8YG73J7N
console.log(ulid(100000)); // 00000031N0J7R2B57M8YG73J7P
console.log(ulid(100000)); // 00000031N0J7R2B57M8YG73J7Q
console.log(ulid(100000)); // 00000031N0J7R2B57M8YG73J7R

Multilingual support

ULID supports nearly 50 languages, including JavaScript, Java, C++, Dart, Python, and .NET.

Additionally, binary representations are available for over 15 languages, including C++, Dart, Go, JavaScript, and Python.

JavaScript module support

ULID can be easily used with all types of JavaScript modules, including ES6+, CommonJS, and AMD.

// TypeScript , ES6+ Modules
import { ulid } from ‘ulid’;
ulid();

// CommonJS
const ULID = require('ulid');
ULID.ulid();

// AMD
define(['ULID'] , function (ULID) {
  ULID.ulid()
});

// Browser
<script src="https://unpkg.com/ulid@2.3.0/dist/index.umd.js"></script>
<script>
    ULID.ulid()
</script>

Other features

  1. 1.21e+24 unique ULIDs can be generated per millisecond.
  2. ULID is URL safe because it does not use any special characters.
  3. Small Packet Size - 2.5 kB (minified), 1.2kB (GZipped).
  4. Download time is about 1ms – 10ms.
  5. Shorter than UUID.
  6. Compatible with UUID 128 format.

future focus

According to many expert opinions on StackOverflow, there are no obvious disadvantages or limitations to using ULIDs.

However, case insensitivity and 80-bit randomness are the main drawbacks developers have noticed in ULIDs. But its lexicographical ability makes it unique among all other products.

Also, if we consider the trend of ULID usage over the past year, we can see that it is on an upward trend. While there are far fewer downloads than UUIDs, it has gained over 150,000 users in the past year.

https://www.npmtrends.com/ulid

With all these features and my experience with UUIDs and ULIDs, it's a no-brainer for use cases that require sorting. So, don't hesitate to use ULID in your next project.


杭州程序员张张
11.8k 声望6.7k 粉丝

Web/Flutter/独立开发者/铲屎官