データベースのレコードサイズ

Reddit経由でhttp://d.hatena.ne.jp/kazuhooku/20080210/1202624721を見かけて思ったことなのだけれど、元記事の主張とは微妙にテーマがずれている内容だと思うので、独立した記事として考えてもらえればと。
ここ2ヶ月位、しつこくデータベースアクセスの高速化に取り組んで分かったのは、データベースのレコードサイズはアクセス速度にかなり決定的な影響を与えるらしいということ。
レコードサイズがアクセス速度に影響を与えるメカニズムは、こういうことだと推測している。データベースは、ディスク上に格納されたレコードをメモリに読み込む際に、効率のために、いくつかのレコードをまとめて固定長のブロック単位で読み込む。その際、レコードサイズが小さければ、1ブロックに格納できるレコード数が増加するため、少ない数のブロックをフェッチするだけで必要なレコードを読み込むことができる。そのため、レコードサイズが小さい程、アクセス速度は向上する。
この法則が有効に適用できるケースは、

  1. 1つの値と他の値を関連付けるようなテーブルの場合と、
  2. 多くのカラムを持つテーブルに対して、一度に全てのカラムを使わず、サブセットだけを使う場合の

2種類がある。
1つ目のケースは、こういうケースだ。

出版社:
  出版社コード (主キー、インデックス)
  出版社名 (インデックス)
  住所
  …

書籍:
  書籍コード (主キー、インデックス)
  書名
  著者
  出版社コード (インデックス)
  価格
  …

書籍ー書店ー関連:
  書店コード (インデックス)
  書籍コード (インデックス)

書店:
  書店コード (主キー、インデックス)
  書店名
  住所
  …

ここで、出版社名からその出版社の書籍を取り扱っている書店名のリストを得る、という操作が頻繁にあるとする。その場合、

出版社名ー(出版社)ー出版社コード
出版社コードー(書籍)ー書籍コード
書籍コードー(書籍ー書店ー関連)ー書店コード
書店コードー(書店)ー書店名

という順に関連をたどって出版社名から書店名にアクセスする。ここで、書籍テーブルにアクセスする部分で、出版社コードに付けられたインデックスを用いて高速にアクセスすることができる。しかし、そこから書籍コードを得る部分では、書籍テーブルのレコードへのアクセスが必要になる。書籍テーブルのレコードサイズが大きければ大きい程、この部分でアクセスするデータ量が増えるため、ディスクアクセスの効率もキャッシュのヒット率も低下する。
この場合、書籍コードと出版社コードだけを取り出して、関連テーブルとして抽出すると、効率が改善する。

書籍ー出版社ー関連:
  書籍コード (インデックス)
  出版社コード (インデックス)

2つ目のケースは、正規化されていない(あるいは、非正規化された)テーブルで見られる。中には数百カラムも持つお化けテーブルを見ることもある。この場合、本当に必要な数カラムを取得するためだけに、数百カラムのデータをディスクから取得する必要があり、極めて効率が悪い。
さらに、このようなお化けテーブルがシステムの中心的なテーブルに含まれていると、システム全体の効率の低下を招くことがある。というのは、中心的なテーブルは単独で利用されるだけではなく、1つ目のケースのようにテーブル間の関連のハブとしても機能していることが多いため、2重で効率に悪影響を与えるためだ。
職場でのヒアリングによると、お化けテーブルが出現する1つの原因は、

  • テーブル構造をわかりやすくするという観点と、
  • 更新処理を簡単にするという観点が

あるようだ。
1つ目の観点は、テーブルの設計をExcelなどで管理して、その情報を以ってサブシステム間のインターフェースにしているような手法を取っている場合、特に重要な理由になるようだ。
2つ目の観点は、COBOLのような表現力が弱い言語を使っている場合、高度に正規化された複雑なテーブル構造の更新処理が嫌われる傾向にあるように思う。