コンピュータがネットワークでガンガン繋がり始めたとき、シンプルだけど超重要な問題が出てきたんだ:どうやって、世界の全然違う場所にある2台のデバイスが、同じIDを作っちゃわないように保証する?
想像してみて:サーバーが2台、1台は東京、もう1台はベルリン。どっちも独立してオブジェクトのIDを作ってる。もし偶然同じIDを作っちゃったら、データがごっちゃになったり、上書きされたり、システムが落ちたりするかも。
そんなわけで、1990年代、分散システムやネットワークプロトコルが爆発的に発展してた時代、MicrosoftとOpen Software Foundation(OSF)の中でGUIDってアイデアが生まれたんだ。Globally Unique Identifier、つまりグローバルユニークIDってやつ。
GUID(ISO標準だとUUID、Universally Unique Identifier)は128ビットの数字で、例えばこんな感じ:
550e8400-e29b-41d4-a716-446655440000
これ、デジタルの指紋みたいなもんで、めっちゃ長くてランダムだから、IDがかぶる(コリジョン)確率はほぼゼロなんだ。
作り方は:
- 時間
- ランダムな数字
- デバイスのMACアドレス(古いバージョンの場合)
- あと暗号ハッシュ関数とか
なんでこれが大事だったかっていうと、GUIDがあれば中央サーバーも調整もなしでユニークIDが作れる。ロックも遅延もいらない。これが本当に助かったのは:
- 分散データベース
- ネットワークプロトコル
- ドキュメント管理システム
- もちろん今どきのAPI
PostgreSQLでのUUID
GUID/UUIDはどこでも使われてるよ。PostgreSQLからクラウドサービスまで。今のインターネットの裏方みたいな存在で、これがなかったら世界をこんな簡単に繋げなかった。
実際はめっちゃ長いランダムな数字(16バイト)で、16進数で書かれてる。ただのすごく長い整数だよ。
このデータ型は、システム全体で値のユニークさを保証したいときにピッタリ。ID生成サーバーに依存しなくていい。分散システムや、データがいろんなサーバーで作られるときに特に便利。
なんで単純にINTEGERじゃダメなの?
「UUIDなんていらなくない?IDは自動増分の数字でいいじゃん」って思うかも。ちょっと考えてみよう:
グローバルなユニーク性:もしDBが複数サーバーで動いてたら、INTEGERのユニーク性を保証するのは難しい。UUIDならこの問題は解決。
セキュリティとデータ隠し:UUIDは連番より予測しにくい。予測しやすいID(例:/users/1, /users/2)だと情報漏洩のリスクが上がるけど、UUIDなら安心。
分散システム:データがシステムのいろんな場所で作られるなら、自動増分INTEGERは同期が面倒。
もう一つのメリットは、UUIDは多くのプログラミング言語やストレージシステムで標準サポートされてること。
UUIDを使うメリット
主な良いところ:
- ユニーク性:データが違うサーバーやシステムで作られてもIDのユニークさが保証される。
- 柔軟性:主キー、外部キー、他の用途にも使える。
- スケーラビリティ:分散DBで扱いやすい。
でもUUIDにもデメリットはある:
- サイズ:
UUIDはメモリでINTEGER(4バイト)より大きい(16バイト)。 - 読みやすさ:数字より読みにくいし覚えにくい。
PostgreSQLでのUUID生成
組み込み関数gen_random_uuid()
PostgreSQL 13からは、gen_random_uuid()っていう組み込み関数でランダムなUUIDが作れる。この関数はユニークIDを返して、UUID型カラムの値に使えるよ。
例:
SELECT gen_random_uuid();
結果:
d17fc23b-22e5-4fcb-bf86-1b4c766d77b7
pgcrypto拡張が入ってるか確認しよう(PSQL 1-12用)
PostgreSQLの古いバージョン(13未満)だと、gen_random_uuid()はpgcrypto拡張を入れた後に使える。もし職場でこれを使えって言われたら、次のコマンドでなんとかなる:
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
これでランダムなUUIDが生成できるようになる。
UUIDを外部キーとして使う
UUIDはテーブル同士のリレーションを作るのにも便利。例えばユーザーテーブルがあるとする:
| id | name | |
|---|---|---|
| d17fc23b-22e5-4fcb-bf86-1b4c766d77b7 | Alice | alice@example.com |
| a1d3e15a-abc1-4b51-a320-2d4c859f7467 | Bob | bob@example.com |
| 3c524998-5c24-4e73-836d-a4c6bb3cafcd | Charlie | charlie@example.com |
ordersテーブルの作成
ordersテーブルを作って、user_idを外部キーにしてusersテーブルのidを参照するようにしよう。
| order_id | user_id | order_date |
|---|---|---|
| 1a5b7d9c-b1a2-4f8e-9e7a-0a1111111111 | d17fc23b-22e5-4fcb-bf86-1b4c766d77b7 | 2024-10-15 10:00:00 |
| 2b6c8e0d-c2b3-5a9f-af8b-1b2222222222 | a1d3e15a-abc1-4b51-a320-2d4c859f7467 | 2024-10-15 10:05:00 |
| 3c7d9f1e-d3c4-6baf-bc9c-2c3333333333 | 3c524998-5c24-4e73-836d-a4c6bb3cafcd | 2024-10-15 10:10:00 |
| 4d8eaf2f-e4d5-7cb0-cdab-3d4444444444 | d17fc23b-22e5-4fcb-bf86-1b4c766d77b7 | 2024-10-15 10:15:00 |
| 5e9fb030-f5e6-8dc1-debc-4e5555555555 | a1d3e15a-abc1-4b51-a320-2d4c859f7467 | 2024-10-15 10:20:00 |
user_idカラムはusersテーブルのidと繋がってて、ユーザーと注文のリレーションが作れる。
JOINでデータ取得
usersとordersテーブルのデータがどう繋がってるか見てみよう:
SELECT
u.id AS user_id,
u.name,
o.order_id,
o.order_date
FROM users u
JOIN orders o ON u.id = o.user_id;
結果:
| user_id | name | order_id | order_date |
|---|---|---|---|
| d17fc23b-22e5-4fcb-bf86-1b4c766d77b7 | Alice | a1d3e15a-abc1-4b51-a320-2d4c859f7467 | 2024-10-20 12:34:56 |
UUIDの主な使いどころ
ユーザーや注文のID:分散システムで、データがいろんなソースから来る場合。
API用のマーカー:UUIDはREST APIでエンティティの識別子としてよく使われる。
グローバルなデータ同期:例えば、データが複数サーバーから集まるとき。
よくあるミスや注意点
UUIDを手動で作ろうとする:エラーを避けるために、gen_random_uuid()みたいな組み込み関数を使った方がいい。
使いすぎ:スケールしないローカルテーブルとか、単純な自動増分INTEGERで十分な場合はUUIDを使わなくてOK。
サイズ:UUIDはサイズが大きいから、特にインデックスを貼るときはクエリのパフォーマンスに影響することもある。
GO TO FULL VERSION