独り言

プログラミングの講師をしています。新人研修で扱う技術の解説と個人の技術メモ、技術書の紹介など

【SQL】ビットマップインデックスの仕組み

ビットマップインデックスの仕組みについてのメモ。
Qiitaでも同じ記事書いてます。

そもそもインデックスとは

ビットマップインデックスの前にそもそもインデックスとは何かについて簡単に説明。
インデックスはざっくりいえばテーブルに対してのSQL(SELECT文)の処理速度を高速化するための仕組み。
日本語でいうと索引。専門書や辞書で後ろの方についている索引と同じイメージ。
辞書の場合、調べたい用語があるときに、1ページから順番に探していくよりも、先に索引で該当の用語が何ページにあるかを確認し、そのページを調べにいくほうが時短確実に時短。
その仕組みをDBMSで実現しているのがインデックス。

インデックスの種類

インデックスにはいくつかの種類がある。

・Bツリーインデックス
・ビットマップインデックス
・ハッシュインデックス

などなど。
DBMSにおいて単にインデックスというと、大抵の場合Bツリーインデックスのことを指す。
本の索引のイメージに近いのがBツリーインデックス。
名前の通り木構造になっていて、検索値を元に該当レコードが格納されているブロックのアドレスを取得する仕組み。
Bツリーインデックスについては詳しくは解説しているサイトや記事がたくさんあるので他を参照ください。

ビットマップインデックスとは

ビットマップインデックスは、検索に用いられるカラムに対して、その値とレコードとのビットマップを使ってレコードを検索するインデックス。
このインデックスが有効なのは検索するカラムのカーディナリティが低い(取りうる値の種類が少ない)場合。

例えば、「名前」「性別」「血液型」という情報を持つユーザーテーブルがあって、10万件のレコードがあったとする。
この場合、名前はほとんどの場合被らないので、レコード毎にほぼ違った値になる。
つまりカーディナリティが高い。
一方、性別は男と女の2種類の値しか取らないし、血液型はA、B、O、ABの4種類しかない。
つまりカーディナリティが低い。

この場合、名前で検索を行った際にはBツリーインデックスによる検索の方が明らかに効率が良いが、 性別や血液型で検索を行う場合、ビットマップインデックスの方が効率が良くなる可能性が高い。

サンプル

言葉だけの説明だと分かりにくいのでサンプルで。

名前、血液型、性別というカラムを持ったユーザーテーブルを考える。
以下の6件のレコードが登録されているとする。

ユーザー

ID 名前 血液型 性別
1 佐藤 A
2 鈴木 B
3 田中 O
4 高橋 A
5 伊藤 O
6 山本 AB

この時、血液型と性別に対するビットマップインデックスは以下のようになる。

血液型のビットマップ

A 1 0 0 1 0 0
B 0 1 0 0 0 0
O 0 0 1 0 1 0
AB 0 0 0 0 0 1
レコード番号 1 2 3 4 5 6

性別のビットマップ

1 0 1 0 1 0
0 1 0 1 0 1
レコード番号 1 2 3 4 5 6

レコード番号というのは、レコードを特定するための値だと思ってください。
Oracleでいうrowid、PostgreSQLでいうTIDみたいなもの。
ここではわかりやすくするためIDと一致するようにしています。
Markdownでのテーブルの性質上一番上の行が太字になっていますが、特に意味はありません。)

ここで例えば

select * from ユーザー where 血液型 = 'A';

というSQLを実行したとする。
ビットマップインデックスが使用される場合、血液型のビットマップのAのビット列を見る。
「100100」 ビット列はこうなっているので、1つ目のレコードと4つ目のレコードが該当する。
結果的に佐藤、高橋のレコードがヒットする。

このように、条件に合致するビット列を見て、値が1になっているレコードを取得するのがビットマップインデクスの仕組み。

ビットマップインデックスのもう一つのメリット

ビットマップインデックスにはもう一つ、OR検索でもインデックスが使用されるという特徴がある。
Bツリーインデックスの場合、性質上OR演算子があるとほとんどの場合インデックスは使用されないので、その点がBツリーインデックスと異なる点。

例えば

select * from ユーザー where 血液型 = 'O' or 性別 = '';

というSQLを実行したとする。

血液型Oに該当するビット列は
「001010」
性別が女に該当するビット列は
「010101」

それぞれのビット列の論理和を取ると
「011111」となる。
この結果、鈴木さんから山本さんまでの5レコードがヒットする。

同じ理屈でAND条件(論理積)も使えるが、AND条件の場合はBツリーインデックス有効なのでメリットというほどでもないかと。

DBMS毎のビットマップインデックスの仕様

Oracle

Oracleの場合、create index するときに「bitmap」のオプションを付けるとビットマップインデックスになる。

PostgreSQL

インデックス作成時には指定することはできないが、実行計画では「Bitmap Scan」が確認できる。
どうやらSQL実行時にワーキングメモリ内にビットマップを作成して検索速度を上げているらしい。

SQL Server

私が調べた限りサポートしてないっぽい。

他のDBMS製品

すみませんが未調査です。知る機会があれば、もしくは情報いただければ更新します。