UUIDとULIDの違いと種類を解説【ULID=ソート可能なUUID?】|東京のWEB制作会社・ホームページ制作会社|株式会社GIG

UUIDとULIDの違いと種類を解説【ULID=ソート可能なUUID?】

2023-05-19 制作・開発

UUID(Universally Unique Identifier)と ULID(Universally Unique Lexicographically Sortable Identifier)は、両方ともユニークな識別子を生成するために使用される技術です。

UUIDにはいくつかのバージョンがあり、この記事では、UUID v4、UUID v7、およびULIDについて説明し、それらの比較を行います。

弊社GIGは、ナショナルクライアントからスタートアップまで、Webコンサルティング、UI/UXデザイン、システム開発など、DX支援をおこなうデジタルコンサルティング企業です。データベース設計やWeb制作、DX支援のご相談はいつでもご連絡ください。 
■実績紹介

■お問い合わせはこちら

UUID・ULIDとは?

UUID v4

UUID v4は、ランダムな値に基づいて生成される128ビットの識別子です。これは、生成するたびに完全にランダムな値が生成されるため、非常に高いユニーク性を持ちます。

衝突可能性は0ではないものの、現実的には衝突しないため安全に使用できる識別子になります。

UUID v4は、大量の一意な識別子を生成する必要がある場合に広く使用されています。

UUID v7

UUID v7は、UUID v4と同様に128ビットのランダムな識別子です。

UUID v7は、現在の時刻を表すタイムスタンプに基づいて生成されます。このタイムスタンプには、秒単位の精度だけでなく、ナノ秒単位の精度も含まれます。これにより、UUID v7は、生成時刻の順序が保証され、生成時刻を基準にソートすることができます。

現在v7は正式採用されておらず、IETFではdraftになっています。

ULID

ULIDとは、UUIDと同様にランダムな識別子です。UUID v4とは違い、先頭48ビットにタイムスタンプを置くことにより、ソート可能となっています。1ミリ秒単位でソート順が保証されており、同一の生成器であれば2^80までソート順が保証されます。

【識別子フォーマット】

UUID(Ver.4)
2ec40df6-7587-42ee-bf83-ef97a3e46556
UUID(Ver.7)
018422b2-4843-7a62-935b-b4e65649de3e
ULID
01GWKR0SQ0KD0QZ3SHEEAM8YDJ


LaravelでULIDを識別子として使おう

Laravelを使用したアプリケーションで、識別子にULIDを使用する手順を紹介したいと思います。

1. サポートバージョン

バージョン9.30.1でULIDが正式にサポートが開始されています。

また、バージョン9.31.0では改善プルリクエストがマージされているので、Laravelのバージョンは9.31.0以上で構築することをおすすめします。

9.30.1
9.31.0

今回実行するバージョン情報は以下のとおりです。

・Laravel 10.5.1
・PHP 8.2.4
・Docker Desktop 4.17.0
・macOS Ventura 13.1

2. マイグレーションファイルを編集する

公式リファレンスのgetting startedを参照し、Docker上でアプリケーションを立ち上げます。アプリケーションを立ち上げたら、お好きなエディタでファイルを編集します。

今回はusersテーブルのプライマリキーにULIDを使用する想定でマイグレーションファイルを変更します。

自動生成されている、database/migrations/2014_10_12_000000_create_users_table.phpを編集します。

【database/migrations/2014_10_12_000000_create_users_table.php】

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
   /**
    * Run the migrations.
    */
   public function up(): void
   {
       Schema::create('users', function (Blueprint $table) {
           $table->ulid('id')->primary; // ulidに変更
           $table->string('name');
           $table->timestamps();
       });
   }

   /**
    * Reverse the migrations.
    */
   public function down(): void
   {
       Schema::dropIfExists('users');
   }
};

また、ULIDを使用する場合はHasUlidsトレイトが必要となるので、Userモデルに追記を行います。

参考:Laravel 10.x Eloquentの準備

【app/Models/User.php】

<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Illuminate\Database\Eloquent\Concerns\HasUlids; // 追加

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable, HasUlids; // 追加

    public $table = 'users';
    protected $primaryKey = 'id';

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
    ];
}

プロジェクトルートでマイグレーションを実行します。実行後のusersテーブルは以下のような構造になっています。

3. ユーザーを作成してULIDが使われているか確認

簡単にテストをしたいので、routeファイルでuserを作成し、作成したユーザーを出力してみます。

【routes/web.php】

<?php

use Illuminate\Support\Facades\Route;
use App\Models\User;

Route::get('/', function () {
    $user = new User();
    $user->name = 'foo';
    $user->save();
    dd($user->toArray());
});

http://localhost/ にアクセスするとユーザーが作成され、ユーザー情報が出力されます。実際にidはULIDの形式になっています。

array:4 [▼ // routes/web.php:21
  "name" => "foo"
  "id" => "01gx5n77bjp9s1byj5s2k9mtts"
  "updated_at" => "2023-04-04T08:13:03.000000Z"
  "created_at" => "2023-04-04T08:13:03.000000Z"
]

4. ULIDで並び替えてみる

複数ユーザーを作成した後に、idを降順でorderByして取得してみます。

【routes/web.php】

<?php

use Illuminate\Support\Facades\Route;
use App\Models\User;

Route::get('/', function () {
    $users = User::orderBy('id', 'desc')->get();
    dd($users->toArray());
});

http://localhost/ にアクセスするとユーザーが作成した日時の降順となっています。

array:5 [▼ // routes/web.php:8
  0 => array:4 [▼
    "id" => "01gx5nyfhz3yq8yepe5tvr5cmh"
    "name" => "foo"
    "created_at" => "2023-04-04T08:25:45.000000Z"
    "updated_at" => "2023-04-04T08:25:45.000000Z"
  ]
  1 => array:4 [▼
    "id" => "01gx5n9yamcwkgef1nakk2z9ff"
    "name" => "foo"
    "created_at" => "2023-04-04T08:14:32.000000Z"
    "updated_at" => "2023-04-04T08:14:32.000000Z"
  ]
  2 => array:4 [▼
    "id" => "01gx5n9x4ptwdy4p3nv6yhepyh"
    "name" => "foo"
    "created_at" => "2023-04-04T08:14:31.000000Z"
    "updated_at" => "2023-04-04T08:14:31.000000Z"
  ]
  3 => array:4 [▼
    "id" => "01gx5n9vk6cmt45nmhc3r6bz00"
    "name" => "foo"
    "created_at" => "2023-04-04T08:14:29.000000Z"
    "updated_at" => "2023-04-04T08:14:29.000000Z"
  ]
  4 => array:4 [▼
    "id" => "01gx5n77bjp9s1byj5s2k9mtts"
    "name" => "foo"
    "created_at" => "2023-04-04T08:13:03.000000Z"
    "updated_at" => "2023-04-04T08:13:03.000000Z"
  ]

サンプルリポジトリを作成したので、よければ参考にしていただければと思います。]

5. 備考

Laravel10.xでは文字列ヘルパであるStr::orderedUuidで生成されるUUIDのデフォルトバージョンが7になる想定 (*1) でした。

Laravel10.x以前のordereduuidで生成されるUUIDはv4ベースです。ソートできないのは不便、ということで、Timestamp-first COMB Codec (*2) によりソート可能になっています。(*3) COMB Codecはuuid v7では使用されておらず非推奨となっています。

UUID v4ベースのTimestamp-first COMB Codecで生成したUUIDと、v7のUUIDが混在した場合、ソートに問題がある (*4) ということでデフォルトバージョンが7になるプルリクエストはリジェクトされるという経緯がありました。

この問題が解決されない限り、デフォルトでバージョン7が採用されることはないとは思いますが、新規でUUIDを識別子に使用したい場合はUUID v7もしくはULIDを採用することをおすすめします。Laravel公式ではまだサポートされていないので、パッケージで対応する必要があります。

ramsey/uuidのUUIDv7についてのドキュメント

まとめ

UUIDやULIDを識別子に使うことに関して様々なメリットデメリット (*5) はありますが、状況によっては有用な手段だとは思います。

みなさまもUUIDやULIDを識別子に使ってみてはいかがでしょうか?

■注釈
*1 https://github.com/laravel/framework/pull/44210
*2 https://uuid.ramsey.dev/en/stable/customize/timestamp-first-comb-codec.html&nbsp;
*3 https://www.informit.com/articles/article.aspx?p=25862&nbsp;
*4 https://github.com/laravel/framework/pull/44210#issuecomment-1373376209
*5 https://zenn.dev/praha/articles/3c84e3818891c3

■参考文献 
https://kakakakakku.hatenablog.com/entry/2022/10/31/082041
https://convto.hatenablog.com/entry/2022/05/29/121124
https://ja.wikipedia.org/wiki/UUID
https://iret.media/45658
https://zenn.dev/nshiro/articles/07b1e4834b9214

■株式会社GIG
お問い合わせはこちら
採用応募はこちら(GIG公式サイト)
採用応募はこちら(Wantedly)


WebやDXの課題、お気軽にご相談ください。

庄子肇

バックエンドエンジニア。宮城大学事業構想学部デザイン情報学科を卒業後、ベンチャー企業でエンジニアとして常駐先のシステム開発やサイト制作の経験を積んだ後、2019年10月にGIGにジョイン。