ULID生成器 - オンラインで無料のユニークでソート可能なIDを作成
26文字の辞書順にソート可能な一意の識別子を瞬時に作成する無料のオンラインULID生成ツールです。クロックフォードBase32エンコーディングとタイムスタンプを組み合わせ、データベースの主キーやAPI、分散システムでの利用に適しており、生成したIDはそのままコピーできます。
ULIDジェネレーター
ドキュメンテーション
ULIDとは何か?
ULID(Universally Unique Lexicographically Sortable Identifier、汎用一意辞書順ソート可能識別子)は、データベースのレコードや分散システム内のイベントを識別するために使われる26文字のコードです。現在時刻とランダムデータを組み合わせるため、ULIDの一覧をアルファベット順に並べると、それぞれが作成された時刻順にもなります。ULIDジェネレーターは、これらのコードを生成するツールです。
ULIDの構造
ULIDは、区切り文字なしで連続して書かれる2つの部分で構成されます。
- タイムスタンプ(10 文字):1970年1月1日(Unixエポック)からの経過ミリ秒数で、48 ビット整数として格納されます。
- ランダム性(16文字):80ビットのランダムデータ。同じミリ秒に作成された2つのULIDがほぼ確実に異なるものになるように付加されます。
どちらの部分も、CrockfordのBase32アルファベットを使って表記されます。これは、数字 0-9 とアルファベットA-Zから、文字I、L、O、Uを除いたものです。IとLは数字の 1 と、Oは数字の 0 と見間違えやすい文字です。Uは、意図せず不適切な単語ができるのを避けるために除かれています。その結果、短くて声に出して読みやすく、URLにそのまま入れても安全な文字列になります。
ULIDの計算方法
ULIDジェネレーターは、3つの手順で識別子を生成します。
- 現在時刻を読み取る:Unixエポックからの経過ミリ秒を読み取ります。これは48ビットの数値です。
- 80ビットのランダムデータを生成する:通常は暗号学的に安全な乱数生成器から生成し、10個のランダムバイトを得ます。
- 2つの部分を別々にエンコードする:CrockfordのBase32アルファベットを使い、48ビットのタイムスタンプを10文字に、80ビットのランダム値を16文字に変換します。エンコードした2つの文字列を連結して、最終的な26文字のULIDを作ります。
エンコードでは、数値を一度に5ビットずつ読み取ります。これは、Base32の各文字が32個の可能な値(2^5 = 32)のいずれかを表すためです。10文字では最大50ビットを格納できるため、48ビットのタイムスタンプには十分な容量があります。16文字ではちょうど80ビットを格納でき、ランダム性の部分と一致するため余りはありません。
計算例
ULIDジェネレーターがタイムスタンプ1712345678901(エポックからの経過ミリ秒)で動作し、ランダムバイト[12, 240, 88, 3, 199, 45, 6, 231, 128, 17]を生成したとします。
手順1 – タイムスタンプをエンコードする。 1712345678901を32で繰り返し割り、毎回余りを取り、最後の桁から最初の桁へ逆順に読み取ります(これは標準的な基数変換です)。これにより、10文字の文字列01HTQW311Nが得られます。
手順2 – ランダム性をエンコードする。 10個のランダムバイトを80ビットのストリームにまとめ、5ビットずつ読み取り、各5ビットのまとまりをBase32文字に変換します。これにより、16文字の文字列1KR5G0Y75M3EF00Hが得られます。
手順3 – 部分を連結する。 最終的なULIDは次のとおりです。
101HTQW311N 1KR5G0Y75M3EF00H
2空白なしで記述すると、01HTQW311N1KR5G0Y75M3EF00Hで、合計26文字です。
ULIDとUUIDの比較
| 特徴 | ULID | UUID(バージョン 4) |
|---|---|---|
| 長さ | 26 文字 | 36 文字(ハイフンを含む) |
| 作成時刻順に並べ替え可能 | はい | いいえ |
| エンコード方式 | Crockford Base32 | 十六進数 |
| タイムスタンプを含む | はい(48 ビット) | いいえ |
| エスケープなしでURLに安全に使用できる | はい | はい |
どちらの形式も、中央機関が番号を割り当てなくても、システム間で一意となる識別子を生成することを目的としています。実用上の主な違いはソート方法です。ULIDは先頭にタイムスタンプを含むため、ULIDをプレーンテキストとして並べ替えると、時刻順にも並びます。ランダムUUIDにはこの性質がありません。UUIDの各部分がランダムだからです。
主な用途
- データベースの主キー:ULIDを使うと、データベースは新しい行を、挿入されたおおよその順序でインデックス化できます。多くのデータベースエンジンでは、完全にランダムなキーよりも効率的に処理できます。
- 分散システム:個別のサーバーが中央のカウンターと調整せずにそれぞれULIDを生成でき、その結果もほぼ確実に一意になります。
- イベントログ:ULIDは時刻順に並ぶため、イベントIDの一覧を大まかなタイムラインとしても利用できます。
- APIとファイルの識別子:26文字の固定長でURLに安全な形式なので、ウェブアドレスやファイル名で問題なく使用できます。
関連する識別子形式には、ソート用にタイムスタンプもエンコードするKSUIDや、Twitterなどで使われ、タイムスタンプ、マシン識別子、カウンターを組み合わせるSnowflake IDがあります。
コード例
以下の各例では、ULID仕様で説明されているのと同じ方法でULIDを構築します。48ビットのタイムスタンプを10個のCrockford Base32文字としてエンコードし、80ビットのランダム性をさらに16文字としてエンコードして、両者を連結します。
JavaScript
1const ENCODING_CHARS = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
2
3function encodeULID(time, randomBytes) {
4 // Timestamp: 48 bits -> 10 characters
5 let timestampStr = '';
6 let t = time;
7 for (let i = 0; i < 10; i++) {
8 timestampStr = ENCODING_CHARS[t % 32] + timestampStr;
9 t = Math.floor(t / 32);
10 }
11
12 // Randomness: 80 bits -> 16 characters
13 let result = timestampStr;
14 let bits = 0;
15 let bitCount = 0;
16 for (let i = 0; i < 10; i++) {
17 bits = (bits << 8) | randomBytes[i];
18 bitCount += 8;
19 while (bitCount >= 5) {
20 bitCount -= 5;
21 result += ENCODING_CHARS[(bits >> bitCount) & 0x1f];
22 }
23 }
24 return result;
25}
26
27function generateULID() {
28 const randomBytes = crypto.getRandomValues(new Uint8Array(10));
29 return encodeULID(Date.now(), randomBytes);
30}
31
32console.log(generateULID());
33Python
1import os
2import time
3
4ENCODING_CHARS = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
5
6def encode_ulid(time_ms, random_bytes):
7 # Timestamp: 48 bits -> 10 characters
8 chars = []
9 t = time_ms
10 for _ in range(10):
11 chars.append(ENCODING_CHARS[t % 32])
12 t //= 32
13 timestamp_part = "".join(reversed(chars))
14
15 # Randomness: 80 bits -> 16 characters
16 bits = 0
17 bit_count = 0
18 random_part = []
19 for byte in random_bytes:
20 bits = (bits << 8) | byte
21 bit_count += 8
22 while bit_count >= 5:
23 bit_count -= 5
24 random_part.append(ENCODING_CHARS[(bits >> bit_count) & 0x1F])
25
26 return timestamp_part + "".join(random_part)
27
28def generate_ulid():
29 return encode_ulid(int(time.time() * 1000), os.urandom(10))
30
31print(generate_ulid())
32Java
1import java.security.SecureRandom;
2import java.time.Instant;
3
4public class ULIDGenerator {
5 private static final SecureRandom random = new SecureRandom();
6 private static final char[] ENCODING_CHARS =
7 "0123456789ABCDEFGHJKMNPQRSTVWXYZ".toCharArray();
8
9 public static String generateULID() {
10 long timestamp = Instant.now().toEpochMilli();
11 byte[] randomBytes = new byte[10];
12 random.nextBytes(randomBytes);
13
14 // Timestamp: 48 bits -> 10 characters
15 char[] timestampChars = new char[10];
16 long t = timestamp;
17 for (int i = 9; i >= 0; i--) {
18 timestampChars[i] = ENCODING_CHARS[(int) (t % 32)];
19 t /= 32;
20 }
21
22 // Randomness: 80 bits -> 16 characters
23 StringBuilder result = new StringBuilder(new String(timestampChars));
24 long bits = 0;
25 int bitCount = 0;
26 for (byte b : randomBytes) {
27 bits = (bits << 8) | (b & 0xFF);
28 bitCount += 8;
29 while (bitCount >= 5) {
30 bitCount -= 5;
31 result.append(ENCODING_CHARS[(int) ((bits >> bitCount) & 0x1F)]);
32 }
33 }
34 return result.toString();
35 }
36
37 public static void main(String[] args) {
38 System.out.println(generateULID());
39 }
40}
41よくある質問
ULIDは何に使われますか? ULIDは、作成時刻によるソートも役立つ場合に、データベースの行、APIリソース、またはログに記録されたイベントの一意な識別子として使われます。
ULIDの長さはどれくらいですか? ULIDは常に26文字です。タイムスタンプに10文字、ランダム部分に16文字を使います。
ULIDはどのエンコード方式を使いますか? ULIDはCrockfordのBase32アルファベットを使います。これは数字 0-9 とアルファベットA-Zから、読み間違いや意図しない単語を避けるためにI、L、O、Uを除いたものです。
ULIDはUUIDとどう違いますか? ULIDは作成時刻順に並び、長さは26文字です。ランダムUUID(バージョン4)は時刻順に並ばず、ハイフンを含めて36文字です。
2つのULIDが衝突する可能性はどのくらいですか? 80ビットのランダム性により、衝突は極めて起こりにくくなっています。同じミリ秒以内におよそ1.3兆個のULIDを生成すると、いずれか2つが一致する確率が50%に達します。
インターネット接続なしでULIDを生成できますか? はい。ULIDにはローカルのクロックと乱数生成器だけが必要なので、完全にオフラインで作成できます。
参考文献
- ULID project. Universally Unique Lexicographically Sortable Identifier. ULID specification — 48 ビットのミリ秒タイムスタンプ、80 ビットのランダムデータ、10 + 16 文字の標準形式、Crockford Base32アルファベット。
- Douglas Crockford. Base 32. Crockford's Base32 — 32 個の記号からなるアルファベットと、I、L、O、Uが除外される理由。