DBアタマアカデミー

第1回 記憶装置のトレードオフとバッファの考え方―すべてをとることができないとき (2)

この記事を読むのに必要な時間:およそ 3 分

DBMSと記憶装置の関係

DBMSは重要なデータを保存することを第一の目的としたミドルウェアですから,記憶装置とは切っても切れない関係にあります。

DBMSがデータを保存する媒体は,ほぼ100%,ハードディスクです。ディスク以外の選択肢がまったくない,というわけではないのですが注1),ほとんどの場合においては,容量,コスト,パフォーマンスなどの総合的な観点から,ハードディスクが選択されています。

ハードディスクは,記憶装置の階層でいうと真ん中の二次記憶装置に分類されます。これはつまり,とても良いところがない代わりに,大きな欠点もない媒体ということです。データベースは,冒頭でも言ったように,ほとんどのシステムで利用されている汎用的なミドルウェアですから,平均的な媒体が選択されているのは自然なことです。

しかしそれは,DBMSがデータをディスク以外に持っていない,ということを意味するわけではありません。実は,通常稼動しているDBMSは,常にディスク以外の場所にもデータを持っています。それが一次記憶装置であるメモリです。

メモリはディスクに比べると記憶コストが高いため,1台のマシンに搭載できる量は多くありません。DBサーバの場合,せいぜい数Gバイト~数十Gバイト程度で,HDDに比べれば微々たる大きさです。このため,データベース内のデータすべてをメモリに載せることはできません。

それでも,DBMSが一部でもいいからデータをメモリに載せている理由は,パフォーマンス向上,つまりSQL文の実行速度を速くするためです。図2からわかるように,メモリは最も高速な一次記憶装置に該当します(メモリとディスクの性能差は,大雑把な数値でおよそ100万倍と言われています)。そのため,頻繁にアクセスされるデータをうまくメモリ上に保持しておくことができれば,たとえば同じSELECT文でも,ディスクからデータを読み出すことなく,メモリへのアクセスだけで処理を返すことが可能になるわけです図3)。

図3 メモリ上にデータがあれば高速に処理ができる

図3 メモリ上にデータがあれば高速に処理ができる

このようにパフォーマンスを向上させるために使われるメモリを,バッファbufferと呼びます。バッファとは「緩衝材」という意味です。ユーザとディスクとの間に割って入ることでSQL文のディスクアクセスを減らす役割を果たすわけですから,緩衝材という言葉はぴったりのイメージです。そのバッファにどのようなデータを,どの程度の期間載せておくか,といったことを管理する機能が,DBMSのバッファマネージャというわけです。

注1)
たとえば,インメモリデータベースは,名前のとおりメモリにデータを保持しますし,データのバックアップをテープなどのメディアに取ることは一般的な運用です。また最近では,SSDSolid State Driveというフラッシュメモリを利用した高速かつ永続性のある記憶装置も実用化されています(SSDは階層的には二次に位置します)。

メモリ上の2つのバッファ

DBMSがデータを保持するために使うメモリには,大きく以下の2種類あります。

  • データキャッシュ
  • ログバッファ

多くのDBMSが,この2つに該当するメモリ領域を確保しています。また,ユーザが用途に応じてサイズを変えることもできます。これらのメモリサイズを決めるパラメータを,Oracle,PostgreSQL,MySQLを例に整理しました表1)。

表1 DBMSのバッファメモリの制御パラメータ

  Oracle 11gR2PostgreSQL 8.5MySQL 5.5(InnoDB)




 ャ
 ッ

 ュ
名称データベースバッファキャッシュ共有バッファバッファプール
パラメータDB_CACHE_SIZEshared_buffersinnodb_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_BUFFERwal_buffersinnodb_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)。

図4 データベース更新時の処理

図4 データベース更新時の処理

単純にSQL実行時点でディスクを更新してしまえば簡単なのに,DBMSがそうせず,わざわざディスクの更新タイミングをずらしている理由は,これも結局パフォーマンスを良くしたいからです。つまり,ディスクは検索だけでなく更新にも時間のかかる記憶装置であるため,ディスクの更新が終わるまで待っていると,ユーザを長時間待たせることになるからです。そのため,一度メモリで更新情報を受けた時点で,ユーザには「終わった」と通知しているのです。

注2)
ログバッファを持たず,SQL実行時,ダイレクトにディスクを更新にいく方法を採用しているDBMSも,少数ですがあります。たとえば,MySQLでInnoDBエンジンを使用した場合がそうです。表1のMySQLの備考欄で「InnoDBエンジン使用時のみ有効」と限定しているのはそのためです。

メモリの性質がもたらすトレードオフ

先ほど,メモリの欠点は高価なので保持できるデータ量が少ないことだ,と言いましたが,もう少し補足すると,欠点はほかにもいくつかあります。特に大きいのは,メモリにはデータの永続性がないことです。OSの電源を落とせば,メモリ上に載っていたすべてのデータは消えてなくなります。この性質を揮発性と呼びます(中には電源供給がなくてもデータの失われないメモリもありますが,普通のサーバには使われません)。

それ以前に,DBMSを再起動すればバッファ上のデータはすべてクリアされてしまいます。だから,たとえメモリが非常に安価になったとしても,永続性がない以上,機能的にディスクの代替はできません。

揮発性の問題点

揮発性の一番困るところは,障害時にデータ不整合の原因となることです。データキャッシュであれば,障害によってメモリ上のデータが失われたとしても,オリジナルのデータはディスク上に残っているので,もう一度ディスクから読み出せば,データ不整合の問題は起きません。メモリが空っぽの状態であっても,最初のSELECT文はディスクを直接読みにいくため,時間がかかるだけです。

しかし,ログバッファ上に存在するデータが,もしディスク上のファイルへ反映される前に障害によって消えてしまった場合,そのデータは完全になくなり復旧できません。これは,ユーザが行ったはずの更新情報が消えるわけですから深刻です。

この問題は,DBMSが更新を非同期処理として行っている以上,必ず起きてしまいます。これを回避するため,DBMSはCOMMITのタイミングで必ず更新情報をログファイルへ書き込むことで,障害時のデータ整合性を担保するようにしています。逆に言うと,COMMIT時は同期処理のため,ここで遅延が発生する可能性があるのです注3)。ここに,データベースにおける2つ目のトレードオフがあります。

データベースの鉄則 2
データ整合性とパフォーマンスはトレードオフ。
 データの整合性パフォーマンス
同期処理×
非同期処理×
注3)
例外として,PostgreSQL の「非同期コミット」という機能があります。名前のとおり,コミットをユーザに終了通知する時点でも,バッファからディスクへ書き出さないという危険な機能で,データの信頼性を捨ててパフォーマンスを取りたい場合に,究極のトレードオフを実現します(デフォルトでは無効です)。
「非同期コミットによりもたらされる危険性は,データの破壊ではなくデータの損失です。データベースがクラッシュした場合,最後にフラッシュされた記録までWAL を再生することで復旧が行われます。このため,データベースは内部で一貫性を持った状態に復旧されますが,ディスクにフラッシュされていないトランザクションはすべてそこには反映されません。したがって,影響を受けるのは,最後に行われたいくつかのトランザクションの損失です」。
参照:PostgreSQL 8.4.4文書:第 28章信頼性とログ先行書き込み

著者プロフィール

ミック

SI企業に勤務するDBエンジニア。主にデータウェアハウス業務に従事している。自身のサイト「リレーショナル・データベースの世界」でデータベースとSQLについての技術情報を公開している。『Web+DB Press』で「SQLアタマアカデミー」を連載中。

著書:『SQL ゼロからはじめるデータベース操作』(翔泳社,2010)『達人に学ぶ SQL徹底指南書』(翔泳社,2008)訳書:J.セルコ『SQLパズル 第2版』(翔泳社,2007)

DBアタマアカデミー:サポートページ

コメント

コメントの記入