0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

表紙

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) として保存すると、ストレージの使用量を削減できます。
CREATE TABLE users (
    id BINARY(16) PRIMARY KEY,
    name VARCHAR(255)
);

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

  • 物理的な主キーとして AUTO_INCREMENT(自動増分)を使用し、UUID は論理的な識別子として利用する。
CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    uuid CHAR(36) UNIQUE,
    name VARCHAR(255)
);

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

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

まとめ

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

    • インデックスの効率が低く、挿入・検索のパフォーマンスが悪化する。
    • データを更新すると、インデックスが頻繁に再構築され、パフォーマンスが低下する。
  • 文字列型の主キーが低速な理由

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

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

私たちはLeapcell、バックエンド・プロジェクトのホスティングの最適解です。

Leapcell

Leapcellは、Webホスティング、非同期タスク、Redis向けの次世代サーバーレスプラットフォームです:

複数言語サポート

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

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

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

比類のないコスト効率

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

洗練された開発者体験

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

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

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

ドキュメントで詳細を確認!

Try Leapcell

Xでフォローする:@LeapcellHQ


ブログでこの記事を読む

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?