メインコンテンツへスキップ

VillageSQL is a drop-in replacement for MySQL with extensions.

All examples on this page work on VillageSQL. Install Now →
InnoDB は MySQL のデフォルトのストレージエンジンです。InnoDB がデータを保存する仕組みを理解すると、一部のクエリが速く、他のクエリが速くない理由が分かります。特に、主キーの選択が重要な理由、範囲スキャンがランダムな検索に勝る理由、および大きなテーブルのインデックスが書き込みのパフォーマンスを損なうことがある理由です。

ページ: 保存の単位

InnoDB はすべてをページに保存します。ページはデフォルトで 16KB の固定サイズの塊です。ページは、InnoDB がディスクから読み、ディスクに書く最小の単位です。1 行を読むとき、InnoDB はその行を含む 16KB のページ全体を読み込みます。 これには実際的な影響があります。
  • 連続した行を読むクエリ(範囲スキャン、順序付けられたデータのフルテーブルスキャン)は、各ページを 1 回読み込み、そこから多くの行を処理します
  • ランダムな行を読むクエリ(大きなテーブルに対するセカンダリインデックス経由)は、行ごとに新しいページを読み込み、それぞれが別のディスク読み取りになる可能性があります

クラスタ化インデックス

InnoDB はテーブルの行をクラスタ化インデックスに保存します。これは、葉ノードが実際の行データを含み、主キーで物理的にソートされた B ツリーです。別々の「テーブルファイル」と「主キーのインデックスファイル」はなく、両者は同じ構造です。
このテーブルの行は、ディスク上に id 順で物理的に保存されます。WHERE id BETWEEN 1000 AND 2000 のようなクエリは、連続した範囲のページを読むため高速です。id = 1、次に id = 500000、次に id = 250 の行を取得するクエリは、物理的に離れた 3 つのページを読みます。 主キーを定義しない場合、InnoDB は隠れた 6 バイトの行 ID を作り、それをクラスタ化インデックスのキーとして使います。これは最適化の機会を無駄にします。

セカンダリインデックス

すべてのセカンダリインデックスは別の B ツリーです。葉ノードは行全体を保存せず、インデックス付きの列の値と主キーを保存します。セカンダリインデックスに含まれない列を取得するために、InnoDB は行の検索を行います。セカンダリインデックスの葉から主キーを取り出し、クラスタ化インデックスから行全体を取得します。
この 2 段階の処理が、カバリングインデックスが重要である理由です。セカンダリインデックスがクエリに必要なすべての列を含んでいれば、行の検索は省略されます。MySQL のカバリングインデックスを参照してください。 行の検索のコストは、大きな主キーが高くつく理由でもあります。主キーが INT(4 バイト)ではなく UUID 文字列(36 バイト)の場合、すべてのセカンダリインデックスはエントリごとに 4 バイトではなく 36 バイトを保存し、行の検索はより深い B ツリーをたどります。

バッファプール

InnoDB はページをメモリ内のバッファプールにキャッシュします。ディスクから読まれたページはバッファプールに読み込まれ、メモリが他の用途に必要になるまでそこに残ります。同じページのその後の読み取りは RAM から提供されます。
専用のデータベースサーバーでは、innodb_buffer_pool_size を利用できる RAM の 70〜80% に設定します。これは読み取りの多いワークロードで最も効果の大きい設定変更です。ワーキングセットのより多くがメモリに収まるほど、ディスクの読み取りは減ります。

行フォーマット

InnoDB は 4 つの行フォーマットをサポートしています。デフォルトは DYNAMIC です。 約 8KB という数字は、デフォルトの 16KB ページのおよそ半分であり、InnoDB が可変長の列データをページ外に移す境界です。そのため、大きな VARCHAR/TEXT/BLOB 列が少数ある行は、上限に当たることなくそれをはるかに超えて大きくなれます。しかし、ページ外に移らずインラインに保存される列、たとえば多数の固定幅の CHAR 列は、InnoDB 自身の行サイズ制限に対して引き続きすべて数えられ、それに当たると ERROR 1118 (42000): Row size too large (> 8126). Changing some columns to TEXT or BLOB may help. が発生します。MySQL の別の、より高い行サイズ制限は BLOB/TEXT のオーバーフローページを除いて 65,535 バイトで、ROW_FORMAT にかかわらず適用されます。どちらの制限も実在し、どの列が大きく、それがページ外に移れるかによって、どちらに先に当たるかは変わります。 DYNAMIC フォーマットで VARCHAR、TEXT、または BLOB 列がページをオーバーフローすると、値全体がオーバーフローページに保存され、メインの行には 20 バイトのポインタだけが残ります。COMPACT は代わりに先頭の 768 バイトをインラインに保存します。DYNAMIC の方式は、長い値があるときにメインの行を小さく保つため、ページあたりに収まる行が増え、B ツリーはより浅いままになります。

よくある質問

主キーの選択がパフォーマンスにこれほど影響するのはなぜですか?

クラスタ化インデックスが行を主キーで物理的に並べるためです。ランダムな位置への挿入(UUID の主キーなど)はページ分割を引き起こします。InnoDB は順序を維持するために、満杯のページを分割してデータを移動しなければなりません。連続した主キー(AUTO_INCREMENT、UUID v7)は末尾に挿入され、分割を避けます。ページ分割はテーブルスペースを断片化し、時間とともに書き込みと読み取りの両方を遅くします。

バッファプールが小さすぎるとどうなりますか?

InnoDB はディスクからページを頻繁に読まなければなりません。Innodb_buffer_pool_reads が増え続けるのが見えます。サーバーは I/O に縛られ、RAM からならマイクロ秒で済むクエリがディスクからだとミリ秒かかります。読み取りの多いワークロードでは、ワーキングセットを保持できるバッファプールは、ほぼどのインデックスの最適化よりも価値があります。

InnoDB と MyISAM の違いは何ですか?

InnoDB はトランザクション、外部キー、行レベルのロック、およびクラッシュリカバリをサポートしています。MyISAM はこれらのどれもサポートしていません。特定の読み取り専用の一括ワークロードでは速いですが、ほとんどのアプリケーションには不向きです。InnoDB は MySQL のデフォルトです。特別な理由がない限り、InnoDB を使ってください。

トラブルシューティング

VillageSQL は同じ InnoDB エンジンを同梱しているため、このページのすべての設定はそのまま適用されます。VillageSQL をインストールすると、InnoDB をそのままに、カスタムの型、関数、およびインデックスのための拡張機能フレームワークが手に入ります。

関連項目