レベルエンター山本大のブログ

面白いプログラミング教育を若い人たちに

RDBのバッファキャッシュが固定長のDBでも90%以上ヒットする理由

拙著エントリー信じられないDB文化「固定長DB」でもあうんです。大規模コンシューマ向けサービスのRDB設計 - 山本大の日記についたコメントで、以下のようなものがあった。

個人的には「固定長にすることでのCPUコスト」よりも「固定長にすることでのI/Oコスト」、つまり記事にもあります「バッファキャッシュヒット率が低くなる。」ことによる性能劣化がとても気になるのですが。。。
その辺は、一括でドカンと支払われた初期投資費用で十分なサイズのメモリを積んで回避されたのでしょうか。

これに対しては、僕は技術の論理的な解答ではなく、運用実績から応えされていただこうと思う。


はじめに、データベースバッファキャッシュとは

Oralceのパフォーマンスチューニングの内、SGAと呼ばれるメモリ領域の中の「データベース・バッファ・キャッシュ」という領域を無視してチューニングすることはありえないと言っていいい。

SGAとは「System Global Area」の略でデータベースの高速なアクセスのためにデータを格納するメインメモリ内の領域である。
内訳として、共有プール・データベースバッファキャッシュ・REDOログバッファに分かれるが、
中でも「データベースバッファキャッシュ」は、
最もイメージしやすいキャッシュ(データキャッシュ)であり、チューニングの肝だ。


データベースバッファキャッシュとは、端的に言えば「検索結果を保存して、同じような検索が行われた時にメインメモリ上から素早く結果を返す仕組み」である。

Oracleのキャシュといえばデータベースバッファキャッシュと連想する人も多い。


固定長DBがバッファキャッシュヒット率に悪影響を及ぼすことは容易に想定できる。

それは簡単な話だ。

固定長DBであればSQLでヒットした検索結果に対して、余分な半角Spaceが沢山とれてくるためにバッファキャッシュにのる分量が減ってしまうのだ。
くどくどと説明する必要もないかもしれないが。1GBのキャッシュに、固定長で8000KBのレコードと可変長で平均2000KBのレコードのどちらか多く乗るかというと、明らかに後者であり、小学生でもわかる算数の問題だ。

しかしながら改めて言うが、僕らの固定長DBは、バッファキャッシュヒット率は、問題にならなかった。一般的な理想値を超えてヒットしていた。


固定長DBでスペースだらけのDBなのに、キャッシュに沢山データが乗るなんて理屈に合わない?でもそれが現実だった。

ヒントは、「物理メモリ量 対 ユーザのアクティビティ」である。


この話を理解してもらうために、まずデータベースバッファキャッシュの常識的な知識を共有したい。

データベースバッファキャッシュの常識値は90%

データベースバッファキャッシュのヒット率の目安は、90%である。

これ以上低くなると、パフォーマンス上懸念があると言える。

ヒット率は高ければ高いほど良いが、メインメモリ量とのトレードオフではある。

固定長DBの僕らのPJのバッファキャッシュヒット率は最低値でも96%あった。


これには、2つ理由がある。

第1の理由、Join禁止が効いている

JoinをしないSQLは、非常にシンプルなResultSetを返すSQLであり、ヒット率向上に貢献する。

第2の理由、安価なメインメモリで豊富なデータベースバッファキャッシュ領域を確保している。

僕らのDBは、SGAだけで8GBの領域を使っていた。メモリ使用量の詳細は後述するがユーザー動向から鑑みてジャブジャブと言っていいサイズであった。


では、バッファキャッシュサイズについて詳細を記載する。

バッファキャッシュに乗るデータサイズ

100万人のユーザを誇るPJではあった。需要予測の正確な話はできないがアクティブユーザはざっくり10%の予測であった。
運用が始まってから、需要予測をやや上回ったが、なかなか悪くない予測だったと思う。
10%と言われてもピンと来ないかもしれないが、これはなかなかアクティブなサービスであると言える数字だ。

幾つかのサービスを提供してきて、多種多様なサービスがあったが不思議とアクティブユーザは常に10%であった。
登録会員が100万人いても、10万人だけがアクティブというのは、僕はいくつものサービスで目安にしている。

現実として100万ユーザのアクティビティは8GBのバッファに乗るのか?

1人のユーザがアクティブに操作するデータ量というのはどういったものか。

例えば、このブログのエントリは2000文字、4000Byte程度である。

僕はこのエントリに1時間は掛かっている。

1ユーザが4000Byteを検索する、更新すると言うのは結構なByte数だといえる。*1


多くのサービスにおいて、1ユーザのアクティビティではこれほどのデータ量を扱わない。

そもそもDBの項目のうち全部が全部フリーテキストで1000文字の領域(OracleのCHARの最大サイズ(2000Byte))を持っているわけではない。

1000文字を持つカラムも確かに存在するが、
僕らのサービス200テーブル、20000カラムの内、
2000バイトのフリーテキスト欄は、たったの4カラムであった。

4カラムでも確かに存在しているといえばそうだ。
しかし、最も頻繁にアクセスされる領域の多くは、固定長10Byteとか50Byteである。

そして実績値として、1ユーザの1レコードは平均すると1KB程度だった


しかも、アクティブユーザが全員それだけの量を毎日更新するわけではない。
多くは同じデータの検索なのだ。


ということで、固定長(Space込み)で、多く見積もってアクティブなユーザのデータでキャッシュに乗っているサイズは、

1,000 Byte × 100,000ユーザ = 1GBであった。

見積りではなく運用実績である。


このサイズは8GBのバッファキャッシュに全部乗る。お釣りが7倍来る。


8BG のメモリがお幾らかといえば、今や1万円を切るお値段。


実績として、僕らのサービスにおいて固定長DBであってもバッファキャッシュヒットは96%を常に超えていた。

固定長DBが、バッファキャッシュヒット率に影響があったとは全く言えない状況だった。

結論として

Join禁止、固定長DBに賛否両論があるのは事実であり、僕もこれがバッドノウハウであることは重々承知だ。

しかしながら実績として、Join禁止だからこそバッファキャッシュのヒット率が高くなり、

固定長だからこそ、運用後のパフォーマンスの経年劣化が抑えられている。


余りにもこの構成の辻褄があっていることを、僕は否定できないでいる。

*1:さらに蛇足的な参考値として、僕の大学の卒論は1万文字を最低限とした。1万文字とは全角で20,000Byte(20KB)である。まともに書こうとすると1ヶ月以上は掛かった。