Javaでの開発プロジェクトに携わっていると、「IDにはUUIDを使いましょう」という話をよく耳にします。私自身、以前は大規模なマイクロサービスの開発で、どのバージョンのUUIDを選択すべきか頭を悩ませた経験があります。特に、データベースのパフォーマンスに与える影響を考慮せず安易に導入してしまい、後から修正に苦労したこともありました。
この記事は、過去の私と同じように、
- JavaでUUIDをどうやって生成すればいいか知りたい
- UUIDの種類がたくさんあって、どれを使えばいいか分からない
- UUIDを使うとパフォーマンスが落ちると聞いたけど、本当?
といった悩みを持つJava開発者の方に向けて書いています。
この記事を最後まで読めば、JavaにおけるUUIDの基本的な使い方から、実務で役立つ実践的な活用例、そして思わぬ落とし穴を避けるための注意点まで、体系的に理解できます。自信を持ってUUIDを使いこなし、より安全で効率的な開発を進められるようになりましょう。
UUIDとは?Javaで使われる理由をわかりやすく解説

Java開発で頻繁に利用されるUUIDについて、その基本から見ていきましょう。なぜ多くのシステムでUUIDが採用されるのか、その理由を明らかにします。
UUIDの基本概念と仕組み
UUIDとは「Universally Unique Identifier」の略で、日本語では「汎用一意識別子」と訳されます。その名の通り、世界中でほぼ一意(ユニーク)になるように設計された128ビットの識別子です。
123e4567-e89b-12d3-a456-426614174000
上記のような32桁の16進数と4つのハイフンで構成される文字列として表現されるのが一般的です。UUIDは、タイムスタンプやコンピューターのMACアドレス、ランダムな数値などを組み合わせて生成されるため、異なる場所や時間で生成しても、重複する可能性が天文学的に低くなる仕組みになっています。
UUIDと通常のID(連番ID)の違い
システムで使われるIDには、データベースが自動で採番する「連番ID」(1, 2, 3...)もあります。UUIDと連番IDには、それぞれメリットとデメリットが存在します。
項目 | UUID | 連番ID |
一意性 | 非常に高い(ほぼ重複しない) | テーブル内でのみ一意 |
生成場所 | アプリケーション側で生成可能 | データベース側で生成 |
推測 | 困難 | 容易(次のIDが予測できる) |
パフォーマンス | 書き込み時に劣る場合がある | 書き込み時に高速 |
データサイズ | 大きい(128ビット) | 小さい(64ビットなど) |
このように、UUIDはどこでも生成できて推測されにくいという強みを持つ一方で、データサイズやパフォーマンス面では連番IDに劣る部分があります。
なぜJavaでUUIDがよく使われるのか
では、なぜJava開発、特に近年のWebアプリケーション開発でUUIDが好んで使われるのでしょうか。主な理由は3つあります。
- 分散システムとの相性:複数のサーバーが協調して動作するマイクロサービスや分散システムでは、中央でIDを管理する仕組みがボトルネックになりがちです。UUIDなら各サーバーが独立してIDを生成できるため、IDの採番待ちが発生せず、システム全体のスケーラビリティが向上します。
- セキュリティの向上:連番IDは「/users/1」「/users/2」のように、次のIDが簡単に推測できてしまいます。これにより、他のユーザーの情報を不正に閲覧されるリスクが高まります。UUIDを使えばIDが推測不可能になるため、セキュリティが向上します。
- オフラインでのデータ生成:クライアントサイド(例:スマートフォンアプリ)でデータを生成し、後からサーバーに送信するようなケースでも、UUIDは活躍します。クライアント側でユニークなIDを事前に発行できるため、通信が発生する前にデータを確定させられます。
これらの理由から、多くのJavaプロジェクトでUUIDが採用されているのです。
JavaでUUIDを生成する方法

JavaでUUIDを扱うのは非常に簡単です。標準ライブラリにjava.util.UUID
クラスが用意されているため、特別なライブラリを追加する必要はありません。
UUID.randomUUID()の使い方とサンプルコード
最も一般的に使われるのが、ランダムなUUID(バージョン4)を生成するrandomUUID()
メソッドです。使い方はきわめてシンプルです。
import java.util.UUID;
public class UuidExample {
public static void main(String[] args) {
// UUIDを生成
UUID uuid = UUID.randomUUID();
// 生成したUUIDを文字列として出力
System.out.println(uuid.toString());
// 出力例: 550e8400-e29b-41d4-a716-446655440000
}
}
このメソッドを呼び出すだけで、暗号学的に安全な乱数ジェネレーターを使って、ほぼ重複しないUUIDを簡単に取得できます。
固定値を使いたいときのUUID.fromString()の使い方
既存のUUID文字列からUUIDオブジェクトを生成したい場合は、fromString()
メソッドを使います。データベースから取得したID文字列を扱う際などによく利用されます。
import java.util.UUID;
public class UuidFromStringExample {
public static void main(String[] args) {
String uuidString = "123e4567-e89b-12d3-a456-426614174000";
// 文字列からUUIDオブジェクトを生成
UUID uuid = UUID.fromString(uuidString);
System.out.println(uuid);
// 出力: 123e4567-e89b-12d3-a456-426614174000
}
}
ただし、指定する文字列がUUIDのフォーマットに準拠していない場合、IllegalArgumentException
という例外が発生するため注意が必要です。
UUIDの文字列変換(toString)と利用例
UUIDオブジェクトをデータベースに保存したり、APIのレスポンスとして返したりする際には、文字列への変換が必要です。これにはtoString()
メソッドを使います。
UUID uuid = UUID.randomUUID();
String uuidStr = uuid.toString(); // "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"形式
もしハイフンが不要な場合は、String
クラスのreplace()
メソッドを使って削除できます。
String uuidWithoutHyphen = uuid.toString().replace("-", "");
// "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"形式
URLのパスやリクエストパラメータとして使う場合など、用途に応じてハイフンの有無を選択すると良いでしょう。
UUIDの種類とバージョンの違い

一言でUUIDといっても、実はいくつかの「バージョン」が存在します。バージョンによって生成方法や特性が異なるため、用途に合ったものを選択することが重要です。
UUIDv1(時刻ベース)とUUIDv4(ランダム生成)の違い
特に有名なのがバージョン1(UUIDv1)とバージョン4(UUIDv4)です。
- UUIDv1 (時刻ベース):生成した時刻(タイムスタンプ)と、コンピューターのネットワークカードに固有のMACアドレスを基に生成されます。
- メリット: 時刻情報を基にしているため、大まかにソート可能です。生成順序が重要な場合に役立ちます。
- デメリット: MACアドレスが含まれるため、どのコンピューターで生成されたか特定されるプライバシー上のリスクがあります。また、同じ時刻に同じコンピューターで複数生成すると重複の可能性があります(実際にはクロックシーケンスで回避されます)。
- UUIDv4 (ランダム生成):時刻などの情報を使わず、完全にランダムな数値から生成されます。JavaのUUID.randomUUID()で生成されるのが、このバージョン4です。
- メリット: 生成元に関する情報を含まないため、プライバシー面で安全です。最もシンプルで広く使われています。
- デメリット: ランダムなため、ソートができません。データベースのインデックス効率が悪くなる原因にもなります。
Javaで扱えるUUIDバージョンは?
Javaの標準ライブラリjava.util.UUID
では、主にバージョン3とバージョン4の生成をサポートしています。
- バージョン4:
UUID.randomUUID()
で生成できます。 - バージョン3/5:
UUID.nameUUIDFromBytes()
で生成できます。これは、名前(文字列など)を基に一貫したUUIDを生成する方法で、同じ名前からは必ず同じUUIDが生成されます。
標準ライブラリにはバージョン1を直接生成するメソッドは用意されていません。もしv1を使いたい場合は、java-uuid-generator (JUG)
などの外部ライブラリを利用する必要があります。
UUIDv7など新しい仕様との関係
近年、UUIDv7という新しいバージョンが注目されています。これはUUIDv1とUUIDv4の「良いとこ取り」をしたような仕様です。
UUIDv7は、先頭部分がタイムスタンプ(時刻情報)になっており、残りの部分がランダムな値で構成されています。これにより、以下のメリットが生まれます。
- ソート可能: 時刻情報を持つため、IDで並べ替えができます。
- パフォーマンス向上: データベースのインデックスにおいて、v4のようなランダムな値よりも効率的にデータを格納できるため、書き込みパフォーマンスの向上が期待できます。
- プライバシー保護: v1と違い、MACアドレスを含まないため安全です。
JavaでUUIDv7を利用するには、現時点ではuuid-creator
などのライブラリを使うのが一般的です。データベースの主キーにUUIDの採用を検討している場合、UUIDv7は非常に有力な選択肢となるでしょう。
UUIDを使う際の注意点と落とし穴

便利に見えるUUIDですが、何も考えずに使うと思わぬ問題に直面することがあります。ここでは、JavaUUIDを利用する上での3つの注意点を解説します。
重複の可能性は本当にゼロ?
結論から言うと、重複の可能性はゼロではありませんが、実用上はゼロと考えて問題ありません。
UUIDv4のバリエーション総数は$2^{122}5.3 \times 10^{36}$という想像もつかないほどの数です。たとえ1秒間に10億個のUUIDを100年間生成し続けても、重複が発生する確率は極めて低いとされています。一般的なWebアプリケーションの規模で重複を心配する必要はまずないでしょう。
UUIDが長すぎるときの短縮方法
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
という36文字の形式は、URLに含めると長すぎると感じることがあるかもしれません。その場合、いくつかの短縮方法が考えられます。
- ハイフンを削除する: 32文字になります。
- Base64エンコードする: UUIDは128ビット(16バイト)のデータなので、Base64エンコードすると、より短い文字列(約22文字)で表現できます。
ただし、短縮すると元のUUIDに戻す処理が必要になったり、人が目で見て識別しにくくなったりするデメリットもあるため、用途をよく考えて利用しましょう。
データベースでUUIDを使うときのパフォーマンス問題
これはUUIDを利用する上で最も注意すべき点です。特に、データベースの主キー(Primary Key)にUUIDv4を使うと、深刻なパフォーマンス問題を引き起こす可能性があります。
- インデックスの断片化:連番IDの場合、新しいデータはインデックスの末尾に追記されていくだけなので効率的です。しかし、完全にランダムなUUIDv4を主キーにすると、データの挿入場所がインデックスのあちこちに分散してしまいます。これによりインデックスの断片化が発生し、新しいデータを挿入するたびに大規模なインデックスの再構築が必要になり、書き込み性能が著しく低下します。
- データサイズ:UUIDは16バイトのデータです。一方、連番IDでよく使われるBIGINTは8バイトです。主キーのデータサイズが大きいと、インデックス全体のサイズも大きくなり、メモリをより多く消費し、検索性能にも影響を与えます。
対策としては、前述したUUIDv7のようなソート可能なUUIDを利用するのが最も効果的です。ソート可能なUUIDであれば、データが時系列に沿ってインデックスに格納されるため、連番IDに近いパフォーマンスを維持できます。
UUIDの活用例と実践コード

理論だけでなく、実際にJavaでUUIDがどのように活用されているのか、具体的なコード例を交えて紹介します。
API開発でのUUID活用パターン
REST APIでリソースを識別するIDとしてUUIDを使うのは、非常に一般的なパターンです。
// GET /users/123e4567-e89b-12d3-a456-426614174000 のようなリクエストを想定
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{userId}")
public ResponseEntity<User> getUserById(@PathVariable("userId") UUID userId) {
User user = userService.findById(userId);
return ResponseEntity.ok(user);
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User newUser) {
// 新規作成時にUUIDを採番する
newUser.setId(UUID.randomUUID());
User createdUser = userService.save(newUser);
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}
}
IDにUUIDを使うことで、URLから他のユーザーIDを推測されるのを防ぎ、セキュリティを高める効果があります。
ファイル名やセッションIDに使う例
ユーザーがアップロードしたファイルの名前が他のユーザーと重複しないように、ファイル名にUUIDを付与するのも良い方法です。
// 元のファイル名: profile.jpg
// UUID: a1b2c3d4-....
// 保存するファイル名: a1b2c3d4-....-profile.jpg
String originalFilename = "profile.jpg";
String uniqueFilename = UUID.randomUUID().toString() + "-" + originalFilename;
// このユニークなファイル名でサーバーに保存する
同様に、WebアプリケーションのセッションIDのように、一時的でユニークな識別子が必要な場合にもUUIDは最適です。
UUIDを使ったユニークキー設計のポイント
データベースの主キーにUUIDを使う場合は、パフォーマンスへの影響を常に意識する必要があります。
- データ型:UUIDをVARCHAR(36)のような文字列型で保存するのは避けましょう。ストレージ効率が悪く、インデックス性能も低いです。多くのデータベースではUUID専用の型や、BINARY(16)のようなバイナリ型が用意されています。これらを利用するのがベストです。
- バージョンの選択:前述の通り、書き込み性能が重要視されるテーブルでは、UUIDv4よりもUUIDv7のようなソート可能なUUIDの採用を強く推奨します。
これらのポイントを押さえることで、UUIDのメリットを享受しつつ、パフォーマンスの劣化を最小限に抑えられます。
まとめ:UUIDを理解すればJava開発がもっと楽になる
この記事では、JavaにおけるUUIDの基本から実践的な使い方、そして注意点までを網羅的に解説しました。最後に、重要なポイントを振り返りましょう。
UUIDのメリットを再確認
- 世界で一つだけ: システムの規模や場所を問わず、ほぼ重複しないIDを生成できます。
- どこでも生成可能: データベースに頼らず、アプリケーション側でIDを払い出せます。
- 高いセキュリティ: IDがランダムなため、第三者による推測が困難です。
シーン別の使い分けまとめ
- 外部に公開するID: セキュリティが重要なAPIのエンドポイントなどでは、UUIDv4が最適です。
- DBの主キー: 書き込みパフォーマンスが求められる場合は、UUIDv7などのソート可能なUUIDを検討しましょう。
- 小規模な内部システム: 必ずしもUUIDにこだわる必要はなく、従来の連番IDが適している場合もあります。
UUID関連のライブラリ・ツール紹介
Java標準ライブラリで不足を感じた場合は、以下のライブラリが役立ちます。
- java-uuid-generator (JUG):UUIDv1を含む、さまざまなバージョンのUUIDを生成できる老舗ライブラリです。
- uuid-creator:注目されているUUIDv7を簡単に生成できるライブラリで、新しいプロジェクトでの採用におすすめです。
UUIDは、現代のJavaアプリケーション開発において非常に強力なツールです。その特性と注意点を正しく理解し、適材適所で活用することで、あなたの開発プロジェクトはより堅牢でスケーラブルなものになるでしょう。