部分文字列インデックスによるストレージ効率化
長めの文字列カラム(例:email)に対して、先頭数文字のみを対象にインデックスを構築することで、インデックスサイズを削減し、ページあたりのエントリ数を増加させることができます。
ALTER TABLE accounts ADD INDEX idx_login_prefix(login_id(8));
この例では、login_id の先頭8バイトのみがB+ツリーに格納され、メモリとディスク使用量を抑えると同時に、範囲スキャンや等値検索の高速化にも寄与します。ただし、選択率(cardinality)が低下しないよう、十分なユニーク性を持つ長さを選定する必要があります。
カバリングインデックスによる回表回避
以下のようなスキーマを想定します:
CREATE TABLE orders (
order_id BIGINT PRIMARY KEY AUTO_INCREMENT,
customer_id INT NOT NULL,
status TINYINT NOT NULL,
created_at DATETIME,
INDEX idx_cust_status (customer_id, status)
);
このとき、次のようなクエリはカバリングインデックスを活用できます:
SELECT customer_id, status FROM orders WHERE customer_id = 12345 AND status = 2;
インデックス idx_cust_status のリーフノードには customer_id と status の両方の値が格納されているため、聚簇インデックス(主キー)への追加アクセス(=回表)を回避でき、I/O負荷を大幅に低減します。
連続した主キーによるページ分割最小化
主キーとしてAUTO_INCREMENTを採用すると、新規行の挿入は常にB+ツリーの右端に追加されます。これにより:
- 既存データの再配置が不要
- ページ分割(page split)の発生頻度が抑制される
- ディスク上の物理配置がより連続的になり、範囲スキャン性能が向上
一方、UUIDやランダムな数値を主キーに用いると、挿入位置が不定となり、頻繁なページ再編成や内部断片化を引き起こす可能性があります。
インデックス無効化を招く代表的なパターン
- 前方一致不可なLIKE式:
WHERE name LIKE '%son'やWHERE name LIKE '%mi%'は、B+ツリーの左から順に並ぶ性質を活かせないため、フルテーブルスキャンに陥ります。 - カラムに対する関数適用:
WHERE UPPER(email) = 'ADMIN@EXAMPLE.COM'は、インデックスに格納された生の値と一致しないため、インデックスが使われません。代わりに生成列+インデックスや、大文字小文字を無視する照合順序(collation)の利用を検討します。 - 複合インデックスの最左原則違反:
INDEX (a, b, c)に対してWHERE b = 5だけではインデックスは使用されません。条件にaが含まれていない限り、ツリーの探索起点を特定できないためです。 - OR句の非均一なインデックス利用:
WHERE indexed_col = 1 OR non_indexed_col = 'X'の場合、MySQLのクエリプランナーは、一部の条件でしかインデックスを使えないことを理由に、全件スキャンを選択することがあります。このようなケースでは、UNION ALLによる分割クエリや、補助インデックスの追加を検討します。