DBMSと記憶装置の関係
DBMSは重要なデータを保存することを第一の目的としたミドルウェアですから、記憶装置とは切っても切れない関係にあります。
DBMSがデータを保存する媒体は、ほぼ100%、ハードディスクです。ディスク以外の選択肢がまったくない、というわけではないのですが[1]、ほとんどの場合においては、容量、コスト、パフォーマンスなどの総合的な観点から、ハードディスクが選択されています。
ハードディスクは、記憶装置の階層でいうと真ん中の二次記憶装置に分類されます。これはつまり、とても良いところがない代わりに、大きな欠点もない媒体ということです。データベースは、冒頭でも言ったように、ほとんどのシステムで利用されている汎用的なミドルウェアですから、平均的な媒体が選択されているのは自然なことです。
しかしそれは、DBMSがデータをディスク以外に持っていない、ということを意味するわけではありません。実は、通常稼動しているDBMSは、常にディスク以外の場所にもデータを持っています。それが一次記憶装置であるメモリです。
メモリはディスクに比べると記憶コストが高いため、1台のマシンに搭載できる量は多くありません。DBサーバの場合、せいぜい数Gバイト~数十Gバイト程度で、HDDに比べれば微々たる大きさです。このため、データベース内のデータすべてをメモリに載せることはできません。
それでも、DBMSが一部でもいいからデータをメモリに載せている理由は、パフォーマンス向上、つまりSQL文の実行速度を速くするためです。図2からわかるように、メモリは最も高速な一次記憶装置に該当します(メモリとディスクの性能差は、大雑把な数値でおよそ100万倍と言われています)。そのため、頻繁にアクセスされるデータをうまくメモリ上に保持しておくことができれば、たとえば同じSELECT文でも、ディスクからデータを読み出すことなく、メモリへのアクセスだけで処理を返すことが可能になるわけです(図3)。
このようにパフォーマンスを向上させるために使われるメモリを、バッファ(buffer)と呼びます。バッファとは「緩衝材」という意味です。ユーザとディスクとの間に割って入ることでSQL文のディスクアクセスを減らす役割を果たすわけですから、緩衝材という言葉はぴったりのイメージです。そのバッファにどのようなデータを、どの程度の期間載せておくか、といったことを管理する機能が、DBMSのバッファマネージャというわけです。
メモリ上の2つのバッファ
DBMSがデータを保持するために使うメモリには、大きく以下の2種類あります。
多くのDBMSが、この2つに該当するメモリ領域を確保しています。また、ユーザが用途に応じてサイズを変えることもできます。これらのメモリサイズを決めるパラメータを、Oracle、PostgreSQL、MySQLを例に整理しました(表1)。
表1 DBMSのバッファメモリの制御パラメータ
| | Oracle 11gR2 | PostgreSQL 8.5 | MySQL 5.5(InnoDB) |
デ | タ キ ャ ッ シ ュ |
名称 | データベースバッファキャッシュ | 共有バッファ | バッファプール |
パラメータ | DB_CACHE_SIZE | shared_buffers | innodb_buffer_pool_size |
初期値 | 4Mバイト×CPU数×グラニュルサイズ(SGA_TARGETが設定されていない場合は48Mバイト) | 8Mバイト | 128Mバイト |
設定値の 確認コマンド | SELECT value FROM v$parameter WHERE name = 'db_cache_size'; | show shared_buffers; | SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; |
備考 | SGA内部に確保される | | |
ロ グ バ ッ フ ァ |
名称 | REDOログバッファ | トランザクションログバッファ | ログバッファ |
パラメータ | LOG_BUFFER | wal_buffers | innodb_log_buffer_size |
初期値 | 512Kバイト、または128Kバイト×CPU_COUNTのいずれか大きいほう | 64Kバイト | 8Mバイト |
設定値の 確認コマンド | SELECT value FROM v$parameter WHERE name = 'log_buffer'; | show wal_buffers; | SHOW VARIABLES LIKE 'innodb_log_buffer_size'; |
備考 | SGA内部に確保される | | InnoDBエンジン使用時のみ有効 |
- データキャッシュ
データキャッシュは、まさにディスクにあるデータの一部を保持するためのメモリ領域なので、わかりやすいでしょう。これまで解説してきたのは、こちらのバッファです。もし、あなたが実行したSELECT文で選択したいデータが、運良くすべてこのデータキャッシュの中に存在した場合、ディスクからデータを読み出すことなく処理が実行されるので、たいへん高速なレスポンスが期待できます。
反対に、運悪くこのバッファ上にデータが見つからなかった場合は、はるばるディスクからデータを読み出してくることになるので、レスポンスが遅くなります。ディスクに触る者は不幸になるのです。
- ログバッファ
ログバッファは更新処理(INSERT、DELETE、UPDATE)の実行に関係します。実はDBMSは、こうした更新SQLをユーザから受け取ったとき、すぐにディスク上のデータを変更しているわけではありません。一度このログバッファ上に変更情報を溜めて、ユーザに処理終了を通知してしまいます。ディスクへの更新は、あとでゆっくりとやっています(ゆっくりといっても、通常は数秒間隔ですが)。
このようにデータベースの更新処理は、SQL実行タイミングとディスクへの更新タイミングにズレがある非同期処理です(注2:図4)。
単純にSQL実行時点でディスクを更新してしまえば簡単なのに、DBMSがそうせず、わざわざディスクの更新タイミングをずらしている理由は、これも結局パフォーマンスを良くしたいからです。つまり、ディスクは検索だけでなく更新にも時間のかかる記憶装置であるため、ディスクの更新が終わるまで待っていると、ユーザを長時間待たせることになるからです。そのため、一度メモリで更新情報を受けた時点で、ユーザには「終わった」と通知しているのです。
メモリの性質がもたらすトレードオフ
先ほど、メモリの欠点は高価なので保持できるデータ量が少ないことだ、と言いましたが、もう少し補足すると、欠点はほかにもいくつかあります。特に大きいのは、メモリにはデータの永続性がないことです。OSの電源を落とせば、メモリ上に載っていたすべてのデータは消えてなくなります。この性質を揮発性と呼びます(中には電源供給がなくてもデータの失われないメモリもありますが、普通のサーバには使われません)。
それ以前に、DBMSを再起動すればバッファ上のデータはすべてクリアされてしまいます。だから、たとえメモリが非常に安価になったとしても、永続性がない以上、機能的にディスクの代替はできません。
揮発性の問題点
揮発性の一番困るところは、障害時にデータ不整合の原因となることです。データキャッシュであれば、障害によってメモリ上のデータが失われたとしても、オリジナルのデータはディスク上に残っているので、もう一度ディスクから読み出せば、データ不整合の問題は起きません。メモリが空っぽの状態であっても、最初のSELECT文はディスクを直接読みにいくため、時間がかかるだけです。
しかし、ログバッファ上に存在するデータが、もしディスク上のファイルへ反映される前に障害によって消えてしまった場合、そのデータは完全になくなり復旧できません。これは、ユーザが行ったはずの更新情報が消えるわけですから深刻です。
この問題は、DBMSが更新を非同期処理として行っている以上、必ず起きてしまいます。これを回避するため、DBMSはCOMMITのタイミングで必ず更新情報をログファイルへ書き込むことで、障害時のデータ整合性を担保するようにしています。逆に言うと、COMMIT時は同期処理のため、ここで遅延が発生する可能性があるのです[3]。ここに、データベースにおける2つ目のトレードオフがあります。
データベースの鉄則 2
データ整合性とパフォーマンスはトレードオフ。
| データの整合性 | パフォーマンス |
同期処理 | ○ | × |
非同期処理 | × | ○ |