MySQLでUUIDを主キーに使用することの暗黒面

[![表紙](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/5ca4fb6b-5538-43f1-907f-282e9e34a871.png)](https://leapcell.io/?lc_t=q_mysqluuid)

MySQL では、大規模なテーブルで UUID を主キーとして使用すると、特にデータの挿入や更新時にパフォーマンスの問題が発生する可能性があります。以下では、その詳細な理由と、なぜデータの更新がインデックスの再構築を引き起こすのか、また文字列型の主キーが低速である理由を分析します。

## UUID を主キーとして使用する際の問題

### UUID の特性

- UUID は 128 ビットの文字列であり、通常 36 文字で表されます。（例：550e8400-e29b-41d4-a716-446655440000）
- UUID はグローバルに一意であり、分散システムでの識別子として適しています。

### UUID を主キーとして使用する際の欠点

#### インデックスの効率が低い

- **インデックスサイズ**：

  - UUID は文字列型であり、36 バイトを占有します。一方、整数型の主キー（例：BIGINT）は 8 バイトしか占有しません。
  - インデックスサイズが大きくなると、ストレージの使用量が増え、クエリの効率が低下します。

- **インデックスの分裂**：
  - UUID はランダムな値であり、データを挿入する際にインデックスツリーが頻繁に分裂し、バランス調整が必要になります。これによりパフォーマンスが低下します。

#### 挿入パフォーマンスが低い

- **ランダム性**：
  - UUID は無秩序な値であり、新しいデータの挿入位置がランダムになるため、インデックスツリーが頻繁に再編成されます。
- **ページ分裂**：
  - InnoDB ストレージエンジンでは B+ ツリーをインデックスとして使用しています。ランダムな挿入はページ分裂を引き起こし、ディスク I/O が増加します。

#### クエリパフォーマンスが低い

- **比較処理の非効率性**：

  - 文字列比較は整数比較よりも処理コストが高く、大規模なテーブルではクエリのパフォーマンスが著しく低下します。

- **インデックススキャンの範囲が広がる**：
  - UUID はサイズが大きいため、インデックススキャン時にスキャン範囲が広がり、クエリの効率が低下します。

## データの更新がインデックスの再構築を引き起こす理由

### インデックスの役割

- インデックスはクエリのパフォーマンスを向上させるために作成されるデータ構造（例：B+ ツリー）です。
- データが更新されると、インデックスも一貫性を維持するために更新されます。

### データ更新時のインデックスへの影響

- **主キーの更新**：

  - 主キーの値を変更すると、MySQL は古いインデックスレコードを削除し、新しいインデックスレコードを挿入する必要があります。
  - これによりインデックスツリーの調整が必要になり、ディスク I/O が増加します。

- **主キー以外のカラムの更新**：
  - もし更新対象のカラムがインデックス（ユニークインデックスや通常のインデックス）に含まれている場合、そのインデックスも更新される必要があります。
  - これにより、インデックスツリーの再構築が発生します。

### UUID 主キーの追加コスト

- UUID は無秩序なため、主キーを更新した際に、新しい値がインデックスツリーの異なる位置に挿入され、インデックスの再構築が頻繁に発生します。
- 順序のある主キー（例：AUTO_INCREMENT）と比較すると、UUID を使用する場合の更新コストは高くなります。

## 文字列型主キーが低速である理由

### ストレージの占有量が大きい

- 文字列型の主キー（UUID など）は、整数型の主キーよりもストレージを多く消費します。
- インデックスサイズが大きいほど、クエリ時のディスク I/O が増加し、パフォーマンスが低下します。

### 比較処理の効率が低い

- 文字列の比較は、整数の比較よりも処理コストが高いため、特に大規模なテーブルではクエリ性能が著しく低下します。
- 例えば、`WHERE id = '550e8400-e29b-41d4-a716-446655440000'` の処理は、 `WHERE id = 12345` よりも非効率的です。

### インデックスの分裂

- 文字列型の主キーは通常無秩序であり、新しいデータを挿入すると、インデックスツリーが頻繁に分裂・再バランスされ、パフォーマンスに影響を与えます。

## UUID 主キーのパフォーマンスを最適化する方法

### 順序付き UUID の使用

- 順序付き UUID（例：UUIDv7）を使用すると、インデックスの分裂やページ分裂を減少させることができます。
- 順序付き UUID は、タイムスタンプを基に生成されるため、挿入時の順序を維持しやすくなります。

### UUID をバイナリ形式で格納する

- UUID を `BINARY(16)` として保存すると、ストレージの使用量を削減できます。

```sql
CREATE TABLE users (
    id BINARY(16) PRIMARY KEY,
    name VARCHAR(255)
);
```

### AUTO_INCREMENT 主キー + UUID の組み合わせ

- 物理的な主キーとして AUTO_INCREMENT（自動増分）を使用し、UUID は論理的な識別子として利用する。

```sql
CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    uuid CHAR(36) UNIQUE,
    name VARCHAR(255)
);
```

### パーティションテーブルの利用

- 大規模なテーブルをパーティション分割することで、単一のインデックスツリーのサイズを縮小し、クエリ性能を向上させる。

## まとめ

- **UUID を主キーにすると発生する問題点**

  - インデックスの効率が低く、挿入・検索のパフォーマンスが悪化する。
  - データを更新すると、インデックスが頻繁に再構築され、パフォーマンスが低下する。

- **文字列型の主キーが低速な理由**

  - ストレージの使用量が多く、比較処理が遅い。
  - インデックスの分裂が頻繁に発生し、パフォーマンスが低下する。

- **最適化の提案**
  - 順序付き UUID やバイナリ形式での保存を検討する。
  - AUTO_INCREMENT を主キーとして使用し、UUID は論理キーとして活用する。
  - 大規模なテーブルではパーティション分割を活用する。

---

### [私たちはLeapcell、バックエンド・プロジェクトのホスティングの最適解です。](https://leapcell.io/?lc_t=q_mysqluuid)

[![Leapcell](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/0dd3d5e6-3e2f-4735-a1ec-4ab7ab6e00f5.png)](https://leapcell.io/?lc_t=q_mysqluuid)

[Leapcell](https://leapcell.io/?lc_t=q_mysqluuid)は、Webホスティング、非同期タスク、Redis向けの次世代サーバーレスプラットフォームです：

**複数言語サポート**

- Node.js、Python、Go、Rustで開発できます。

**無制限のプロジェクトデプロイ**

- 使用量に応じて料金を支払い、リクエストがなければ料金は発生しません。

**比類のないコスト効率**

- 使用量に応じた支払い、アイドル時間は課金されません。
- 例: $25で6.94Mリクエスト、平均応答時間60ms。

**洗練された開発者体験**

- 直感的なUIで簡単に設定できます。
- 完全自動化されたCI/CDパイプラインとGitOps統合。
- 実行可能なインサイトのためのリアルタイムのメトリクスとログ。

**簡単なスケーラビリティと高パフォーマンス**

- 高い同時実行性を容易に処理するためのオートスケーリング。
- ゼロ運用オーバーヘッド — 構築に集中できます。

[ドキュメントで詳細を確認！](https://docs.leapcell.io/)

[![Try Leapcell](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/bc124830-613f-43f7-9be3-0b27adfb83e6.png)](https://leapcell.io/?lc_t=q_mysqluuid)

Xでフォローする：[@LeapcellHQ](https://x.com/LeapcellHQ)

---

[ブログでこの記事を読む](https://leapcell.io/blog/uuid-as-primary-key-in-mysql-can-hurt-performance)