uuidv7 | rfc 9562 | 時系列ソート可能

> uuidv7 generator <

// 時系列順でソート可能なUUID(RFC 9562、2024年)— 48ビットUnixミリ秒タイムスタンプ + 74ビットのランダム値、ローカルで生成

// UUIDV7をデコード / 検査

下に任意のUUIDを貼り付けると、埋め込まれたタイムスタンプ、バージョン、バリアントを抽出します。v7で動作し、入力が別のUUIDバージョンの場合はそれを報告します。

タイムスタンプ(ミリ秒): // デコードされたフィールドがここに表示されます
ISO 8601 UTC: // デコードされたフィールドがここに表示されます
経過時間: // デコードされたフィールドがここに表示されます
バージョン: // デコードされたフィールドがここに表示されます
バリアント: // デコードされたフィールドがここに表示されます
rand_a(12ビット): // デコードされたフィールドがここに表示されます
rand_b(62ビット): // デコードされたフィールドがここに表示されます
[RFC 9562]

仕様準拠のUUIDv7

RFC 9562(2024年5月)の時系列順UUID形式を実装:48ビットUnixミリ秒タイムスタンプ、4ビットのバージョン7、12ビットのrand_a、2ビットのバリアント、62ビットのrand_b。仕様のテストベクトルに対して検証済みです。

[SORTABLE]

時系列のソート順

数値順または辞書順でソートすると挿入順が得られます — B木の主キー、時系列インデックス、ログの相関付けに最適です。ランダムなv4のページ分割や断片化したインデックスはもう発生しません。

[MONOTONIC]

同一ミリ秒内の単調性

複数のUUIDが1ミリ秒内に収まる場合、出力がそのミリ秒内でソートされた状態を保つよう、ランダム部分が厳密にインクリメントされます。お好みであれば、通常のRFC 9562 方式1(ランダム充填)にするためオフにできます。

[DECODER]

任意のUUIDv7を検査

v7のUUIDを貼り付けると、デコーダーが埋め込まれたタイムスタンプ(ミリ秒 + ISO 8601)、バージョンビット、バリアントビット、rand_a / rand_bフィールドを抽出します。デバッグ、ログの調査、監査証跡に役立ちます。

// UUIDV7について

UUIDv7の仕組み:

UUIDv7はunix_ts_ms (48) | ver (4) | rand_a (12) | var (2) | rand_b (62)として構成された128ビットの識別子です。最初の48ビットはミリ秒単位の現在のUnixタイムスタンプ(ビッグエンディアン)で、これによりUUIDは時間的に単調増加します。次の4ビットはバージョンをエンコードし(2進数0111 = 10進数7)、そのためハイフンで区切られた3番目のグループは常に数字7で始まります。rand_aはサブミリ秒の解像度または追加のエントロピーとして使用される12ビットのランダム値です。2ビットのバリアントフィールドは10に固定されています(そのため4番目のグループの最初の文字は常に89abのいずれかです)。残りの62ビット(rand_b)は生成のたびにcrypto.getRandomValues()から取得されます。UUIDあたりの総エントロピーは74ビットのランダム値で、現実的な生成レートに対して衝突耐性があります。

例:

018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a55 (ts=2024-05-08T15:00:00.000Z)

標準と参考資料:

  • >RFC 9562 — Universally Unique IDentifiers (UUIDs)、2024年5月(RFC 4122を廃止し、v6/v7/v8を定義)
  • >RFC 4122 — 2005年の元のUUID仕様(v1〜v5については依然として権威ある資料)
  • >Web Crypto API(W3C)— 74ビットのランダム値に使用されるcrypto.getRandomValues() CSPRNG
  • >PostgreSQLのgen_random_uuid()の挙動(デフォルトはv4、pgcrypto拡張 / フォークではv7)
  • >draft-peabody-dispatch-new-uuid-format — RFC 9562となった元のv6/v7/v8提案

UUIDv7を使う理由:

  • >挿入の局所性を持つデータベースの主キー — ページは詰まったまま保たれ、インデックスは断片化しません
  • >シーケンス + UUIDのペアを置き換え:1つの列でソート可能かつグローバルに一意
  • >書き込みの多いテーブルのv4主キーを置き換え、ランダム挿入によるページ分割の問題を解決します
  • >別個のタイムスタンプ列を必要とせず、作成時刻でソートされるログ相関ID
  • >複数の書き込み元がコーディネーターなしで順序付きIDを必要とする分散システム
  • >時系列順が自然なインデックスとなるイベントソーシング / 追記専用ログ
  • >プレフィックスのソートが挿入順と一致するS3 / オブジェクトストレージのキー
  • >標準形式が望ましい場合にSnowflake / KSUID / ULIDを置き換え

関連ツール:

// 出力例

今すぐ生成された単一のUUIDv7

config: count=1, format=standard, timestamp=current

output:

018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a55

同一ミリ秒内で単調性を持つ一括バッチ

config: count=5, format=standard, monotonic=on

output:

018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a55
018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a56
018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a57
018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a58
018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a59

カスタムの過去のタイムスタンプ — マイグレーションのバックフィル

config: count=1, timestamp=custom, value=1577836800000 (2020-01-01)

output:

016f5e66-e800-7c9a-b403-2f1d4a7c91ee

URL / ファイル名向けのコンパクト形式

config: count=1, format=no-hyphens

output:

018f5c2e90c07a4fb6ee8dfe3c2b0a55

デコードされたUUIDv7 — 埋め込まれたフィールドを検査

config: input = 018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a55

output:

ts_ms = 1715180400000
ISO   = 2024-05-08T15:00:00.000Z
ver   = 7
var   = 10 (RFC 4122)
rand_a = 0xa4f
rand_b = 0x36ee8dfe3c2b0a55

// 自分で実装する

RFC 9562が2024年に登場して以来、ほとんどの言語にはUUIDv7用の標準ライブラリのヘルパーか、単一ファイルの実装があります。以下は、ブラウザJS、Node.js、Go、Python、PostgreSQLで、コードベースにそのまま組み込める標準的なレシピです。

[JavaScript]

純粋なブラウザ/Node JSでのUUIDv7 — RFC 9562のレイアウト

function uuidv7() {
  const ts = BigInt(Date.now());
  const rand = new Uint8Array(10);
  crypto.getRandomValues(rand);
  const b = new Uint8Array(16);
  b[0] = Number((ts >> 40n) & 0xFFn);
  b[1] = Number((ts >> 32n) & 0xFFn);
  b[2] = Number((ts >> 24n) & 0xFFn);
  b[3] = Number((ts >> 16n) & 0xFFn);
  b[4] = Number((ts >> 8n)  & 0xFFn);
  b[5] = Number( ts          & 0xFFn);
  b[6] = (rand[0] & 0x0F) | 0x70;   // version = 7
  b[7] =  rand[1];
  b[8] = (rand[2] & 0x3F) | 0x80;   // variant = RFC 4122
  for (let i = 9; i < 16; i++) b[i] = rand[i - 6];
  const h = Array.from(b, x => x.toString(16).padStart(2,'0')).join('');
  return `${h.slice(0,8)}-${h.slice(8,12)}-${h.slice(12,16)}-${h.slice(16,20)}-${h.slice(20)}`;
}

// usage:
const id = uuidv7();
console.log(id);   // -> '018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a55'

// generate a batch and verify chronological sort:
const batch = Array.from({ length: 5 }, uuidv7);
batch.sort();      // sorts by creation time without a separate timestamp
[Node.js]

Node.js v26.1.0以降 — 組み込みのcrypto.randomUUIDv7()

import { randomUUIDv7 } from 'node:crypto';

// Added in Node.js v26.1.0 (2026-05-07).
// Returns an RFC 9562 version 7 UUID. The first 48 bits encode a
// millisecond Unix timestamp; the rest is CSPRNG output.
const id = randomUUIDv7();
// -> '018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a55'

// The CSPRNG output is cached for performance: Node generates enough
// entropy upfront for the next ~128 UUIDs. Pass disableEntropyCache: true
// when you need each call to draw fresh randomness, e.g. directly after
// a fork() or in tight high-security loops.
const freshId = randomUUIDv7({ disableEntropyCache: true });

// IMPORTANT: per the Node docs, "the embedded timestamp relies on a
// non-monotonic clock and is not guaranteed to be strictly increasing."
// If two UUIDs land in the same millisecond OR the system clock steps
// backwards, the IDs may not sort in creation order. Use a userland
// monotonic-v7 library (e.g. uuidv7 on npm) when strict ordering matters.
[Go]

Go — google/uuid v1.6以降がUUIDv7をネイティブに提供

package main

import (
    "fmt"
    "github.com/google/uuid"
)

func newID() uuid.UUID {
    id, err := uuid.NewV7()
    if err != nil {
        panic(err)
    }
    return id
}

func main() {
    id := newID()
    fmt.Println(id.String())  // 018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a55
    fmt.Println(id.Time())    // embedded creation timestamp
}
[Python]

Python 3.13以降 — 標準ライブラリのuuid.uuid7()

import uuid

# Python 3.13 added uuid7() and uuid8() to the standard library.
uid = uuid.uuid7()
print(uid)            # 018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a55
print(uid.version)    # 7

# For older Python use the 'uuid7' or 'uuid-utils' PyPI packages, or:
# from uuid_extensions import uuid7
[PostgreSQL]

PostgreSQL 18以降 — ネイティブのuuidv7()関数

-- PG 18 ships uuidv7() natively. Use it as a primary key default:
CREATE TABLE events (
    id   uuid PRIMARY KEY DEFAULT uuidv7(),
    body jsonb,
    ts   timestamptz NOT NULL DEFAULT now()
);

-- For older Postgres, install the pg_uuidv7 extension or generate client-side.
-- Migration tip: keep INDEX on (id) only — v7 already sorts chronologically,
-- so a separate (created_at) index is usually redundant.

// よくある落とし穴

> UUIDv7は作成時刻を漏らす — 不透明なトークンには使わないこと

すべてのv7の最初の48ビットはUnixミリ秒タイムスタンプです。UUIDを見た人は誰でも、レコードがいつ作成されたか、IDが発行されるレート、そして(複数のサンプルがあれば)サーバーの時計を概算できます。内部のデータベース主キーやログIDには問題ありませんが、パスワードリセットURL、共有リンク、セッションID、招待コードには、代わりにUUIDv4またはCSPRNGで生成されたランダムな文字列を使用してください。

> 時計の逆行は単調性を壊す

システムの時計が逆行すると(NTPステップ、VMの移行、うるう秒)、素朴に生成されたv7 UUIDは、以前に発行されたIDよりもにソートされる値を生成する可能性があります。RFC 9562 §6.2は方式2(単調ランダム)を推奨しています:最後に生成されたUUIDを追跡し、タイムスタンプが進まない場合は代わりにランダム部分をインクリメントします。このツールの単調トグルはまさにそれを行います。

> v7はセキュリティが重要な文脈ではv4の単純な代替にはならない

ランダムに見えるID(v4)は、しばしば誤ってベアラートークンとして使われます:「UUIDを知っていれば、その行を読める」。v7はランダムビットが74ビットしかなく、予測可能なタイムスタンプのプレフィックスを持つため、依然として衝突耐性はありますが、v4の122ビットのランダム値よりもはるかに予測不可能性が低くなります。何かをケイパビリティとして扱う場合は、v7の主キーを再利用するのではなく、別個の暗号トークンを生成してください。

> 文字列比較は小文字でハイフン付きの形式でのみ機能する

v7の時系列ソートは、正規の36文字の小文字文字列の辞書順比較、または基になる16バイト値のバイト単位の比較に依存します。大文字と小文字を混在させたり、途中でハイフンを取り除いたり、一部の行を{...}で囲み他をそのまま保存したりすると、いずれも作成時刻と一致しないソート順になります。列ごとに1つの正規形式を選び、CHECK制約で強制してください。

> 48ビットのタイムスタンプは西暦10889年に折り返す

2^48ミリ秒はUnixエポックからおよそ8,925年後で、タイムスタンプフィールドは10889年8月2日UTCにオーバーフローします。コードが埋め込まれたタイムスタンプが妥当な範囲にあると主張する場合は、1世紀後には誤りとなる近未来の年をハードコードするのではなく、上限を281,474,976,710,655ミリ秒(48ビット符号なし整数が保持できる最大値)に設定してください。

>> よくある質問

Q: UUIDv7とは何ですか?

A: UUIDv7はRFC 9562(2024年5月)で定義された128ビットの識別子で、48ビットのUnixミリ秒タイムスタンプを74ビットのランダム性と標準のバージョン + バリアントビットと組み合わせます。その結果、作成時刻で時系列順にもソートされる、グローバルに一意なIDが得られます — 両方の利点を兼ね備えています。これは、各挿入が異なるB木のページに着地して大規模な書き込み増幅を引き起こす、ランダムなUUIDv4主キーによるデータベースインデックスの問題を解決するために特別に設計されました。

Q: UUIDv7はUUIDv4とどう違いますか?

A: UUIDv4は完全にランダム(122ビットのランダム値)で、バージョン + バリアントフィールド以外の構造を持ちません。1マイクロ秒違いで生成された2つのv4は大きく異なる位置にソートされ、予測不可能性には優れていますがB木インデックスには最悪です。UUIDv7は48ビットのUnixミリ秒タイムスタンプを先頭に置くため、時間的に近接して生成されたv7はストレージ上でも近くにソートされます。トレードオフ:v7は作成時刻を明らかにし、ランダムビットが122ではなく74ビットしかありません — 依然として衝突耐性はありますが、秘密トークンには不向きです。

Q: UUIDv4からUUIDv7に移行すべきですか?

A: 書き込みの多いテーブルの内部データベース主キーには — はい、ほぼ常に。v4が引き起こすインデックスの断片化は、特にInnoDBのクラスタ化インデックスを持つPostgreSQLやMySQLでは、実際のクエリ自体のコストを上回ることがあります。ユーザーに公開される、URLに埋め込まれる、またはケイパビリティとして扱われるトークン(「UUIDを知っていればリソースにアクセスできる」)には、v4を維持するか別個のランダムトークンを使ってください — v7は作成時刻を漏らします。一般的なパターンは、内部結合用のv7主キーに加えて、公開URL向けに別個のランダムなslugを用いることです。

Q: UUIDv7はULID、KSUID、Snowflakeと同じですか?

A: 概念的には似ています — 4つすべて時系列順のIDです — がビットレイアウトとエンコーディングは異なります。ULIDはCrockford base32と異なるミリ秒レイアウトを使用します。KSUIDは32ビットのタイムスタンプ + 128ビットのペイロードを27文字のbase62としてエンコードします。SnowflakeはワーカーIDを持つ64ビットのTwitter固有のIDです。UUIDv7はIETF標準の同等物であり、正規の8-4-4-4-12の16進形式でエンコードされるため、あらゆるUUIDライブラリ、データベースのUUID型、JDBC/ODBCドライバーがすでに理解しています。2025年以降に新規で始めるなら、v7を選んでください — それが標準です。

Q: 衝突する前に1ミリ秒あたり何個のUUIDv7を生成できますか?

A: 単一のミリ秒内では、一意性を提供するのは74ビットのランダム値(rand_aの12ビット + rand_bの62ビット)だけです。誕生日衝突の境界によれば、同じミリ秒内で50%の衝突確率に達する前におよそ2^37 ≈ 1,370億個のv7を生成できます — どんな現実的なシステムでも実質的に無制限です。ミリ秒をまたぐとタイムスタンプ自体が区別するため、システムの存続期間にわたるグローバルな衝突リスクは無視できます。単調トグルがオンの場合、ミリ秒内の衝突は構造上ゼロです(ランダム部分がインクリメントされます)。

Q: UUIDv7からタイムスタンプを抽出できますか?

A: はい — タイムスタンプは最初の48ビットで、ハイフンを除いた最初の12文字の16進数です。最初の3つのハイフングループ(8 + 4 = 12 hex digits)を連結し、16進整数として解析すると、ミリ秒単位のUnixタイムスタンプが得られます。これを対話的に行うには上記のデコーダーを使うか、コードでは:parseInt(uuid.replace(/-/g,'').slice(0,12), 16)。結果はUUIDが生成されたミリ秒の瞬間で、その時点のシステム時計に基づく精度です。

Q: UUIDv7のバージョン桁はどのように見えますか?

A: ハイフンで区切られた3番目のグループは常に数字7で始まります。例:018f5c2e-90c0-7a4f-b6ee-8dfe3c2b0a55。4番目のグループは常に89abのいずれかで始まります — これは上位2ビットが10(RFC 4122バリアント)である場合の4つの可能な値です。バージョン桁が7でない場合、そのUUIDは別のバージョン(一般的にランダム用の4、または時間ベースのv1用の1)であり、時系列順にソートされません。

Q: PostgreSQL / MySQLはUUIDv7をネイティブにサポートしていますか?

A: PostgreSQL 18(2025年9月リリース)はuuidv7()を組み込み関数として提供します。それ以前のバージョンはpg_uuidv7拡張またはクライアント側生成が必要です。MySQL 8.xにはネイティブのv7はありません;UUID_TO_BIN(uuid, 1)ヘルパー(ソート順のためにタイムスタンプのバイトを入れ替える)はv1にのみ使用し、v7はクライアント側で生成してください。SQL Server、Oracle、SQLiteはいずれもv7のクライアント側生成を前提とします。ほとんどのORM(Hibernate、Entity Framework、Sequelize、Prisma、SQLAlchemy)は現在、v7をネイティブまたはプラグイン経由でサポートしています。

Q: 単調オプションはRFC 9562に準拠していますか?

A: はい。RFC 9562 §6.2は、ミリ秒内の順序を保証する3つの方式を明示的に記述しています:(1) ランダム充填、(2) 単調ランダム — タイムスタンプが進まないときにカウンターをインクリメント、(3) サブミリ秒のタイムスタンプ精度。このツールの単調チェックボックスは方式2を実装します:2つのUUIDが同じ48ビットタイムスタンプを共有する場合、2つ目のランダム部分は新たに引かれるのではなくprevious_random + 1に設定されます。これにより、完全に仕様準拠を保ちながら、バッチ生成で厳密な順序が保証されます。

Q: 私のデータはどこかに送信されますか?

A: いいえ。UUIDv7のすべての生成、デコード、ソート、コピー、ダウンロードは、純粋なJavaScriptとして完全にブラウザ内で行われます。生成、デコード、コピー、ダウンロードをクリックしても、ページはネットワークリクエストを一切行いません — ブラウザのDevToolsのネットワークタブで確認できます。タイムスタンプ、カスタムシード、生成されたUUID、デコードされた出力はログに記録しません。ページ自体はCDNから配信される静的HTMLなので、入力がインフラに到達することはありません。これにより、本番のデータベースID、内部ログ、その他サードパーティサーバーを通したくないデータに対して安全に使用できます。

Q: UUIDv7をJava / .NET / Rustで使えますか?

A: はい。Javaにはcom.fasterxml.uuid:java-uuid-generator 5.0以降があり、JDK 21はライブラリのヘルパー経由で利用できます。.NET 9はBCLにGuid.CreateVersion7()を提供します。Rustはv7機能フラグの下でuuidクレートのUuid::now_v7()を使用します。他のほとんどのエコシステム(Elixir、Ruby、PHP、Swift、Kotlin)にもよくメンテナンスされたパッケージがあります — 言語のパッケージレジストリで「uuidv7」または「uuid v7」を検索してください。ワイヤー形式はすべてで同一なので、Goで生成されたv7は、変換なしにJavaScriptで解析してタイムスタンプを抽出できます。

Q: Node.jsには組み込みのUUIDv7ジェネレーターがありますか?

A: はい — Node.js v26.1.0(2026年5月7日リリース)以降、標準ライブラリはcrypto.randomUUIDv7([options])を公開しています。import { randomUUIDv7 } from 'node:crypto'としてインポートし、引数なしで呼び出すとRFC 9562 v7 UUID文字列が得られます。サポートされる唯一のオプションはdisableEntropyCache(ブール値、デフォルトfalse)です。デフォルトでは、Nodeは次の約128個のUUIDを生成するのに十分なランダムデータを事前にキャッシュします。これはcrypto.randomUUID()で使われるのと同じ最適化です。公式ドキュメントの重要な注意事項:「埋め込まれたタイムスタンプは非単調な時計に依存しており、厳密に増加することは保証されない」。言い換えると、同じミリ秒内で生成された2つのUUID、またはNTPステップ / VM時計の後方へのずれをまたいで作成されたUUIDは、作成順にソートされない可能性があります。ミリ秒内で厳密な単調性が必要な場合は、RFC 9562 §6.2 方式2に従うuserland実装(npmのuuidv7パッケージ、または単調トグルをオンにしたこのツール)を使用してください。

Q: UUIDv7をデータベースに効率的に保存するには?

A: 36文字の文字列ではなく、16バイトのバイナリ型として保存してください — 1行あたり20バイト節約でき、インデックスサイズが半減し、より高速な比較パスが得られます。PostgreSQLのuuid型、MySQLのBINARY(16)、SQL Serverのuniqueidentifier、SQLiteのBLOBを使用してください。強制したい場合は、バージョンのニブルが7と等しいというCHECK制約を追加します。v7では行が作成時刻で自然にクラスタ化されるため、通常は(uuid_pk)のカバリングインデックスで十分です — 時系列クエリのために別個の(created_at)インデックスは不要です。

// OTHER LANGUAGES