Manual:Database access/ja

From MediaWiki.org

Jump to: navigation, search

MediaWikiにおけるデータベースアクセスについての情報である。この記事はティム・スターリングが2006年1月に投稿した。

Contents

[edit] データベースレイアウト

MediaWikiのデータベースレイアウトについての情報、例えばテーブルとそれらの内容の説明などについては Manual:Database layout/ja[1]を参照。

[edit] API

読み込みのクエリのためには、通常は次のようなもので十分である:

$dbr =& wfGetDB( DB_SLAVE );
$res = $dbr->select( /* ...see docs... */ );
while ( $row = $dbr->fetchObject( $res ) ) {
	...
}
$dbr->freeResult( $res );

while文における代入演算子に注目する。

書き込みクエリのためには次のようなクエリが存在する:

$dbw =& wfGetDB( DB_MASTER );
$dbw->insert( /* ...see docs... */ );

データベースオブジェクトがスレーブ(リードオンリー)もしくはマスター(読み込み/書き込み)であるかを追跡することの手助けにするために、読み込みには$dbrを、書く込むために$dbwを使用する。スレーブを書き込む場合、世界は爆発する。正確に表現すると、スレーブに複製された時にユニークキーの衝突のせいでスレーブがマスター上で成功するその後の書き込みクエリが失敗するかもしれない。スレーブ上の複製は停止してデータベースを修正してオンラインに戻すために数時間かかるかもしれない。スレーブ上でmy.cnfに読み込みのみに設定することでこのシナリオを回避するが、悲惨な結末になった場合、可能な限りチェックをすることが望ましい。

生のSQLのためにquery()関数を提供するが、select()やinsert()といったラッパー関数がより便利である。これらはテーブルの接頭辞やエスケープといったことを考慮する。本当に独自のSQLクエリを作成する必要がある場合、tableName()とaddQuotes()についてのドキュメントを参照する。それら両方が必要である。

[edit] 基本的なクエリの最適化

DBクエリを書く必要のあるMediaWikiの開発者はある程度のデータベースとそれらに関連したパフォーマンス問題について理解しなければならない。許容できない遅い機能を含むパッチを受け取りはされない。QueryPageから由来する特別ページ以外インデックスされていないクエリはMediaWikiには歓迎されない。大きな数の行を検査するSQLクエリを含むコードを投稿する新しい開発者には共通の落とし穴がある。COUNT(*)がO(N)(無限大)で、テーブルを数えることはバケツでちっぽけなものを数えるようなものである。

[edit] 複製

もっとも巨大なMediaWikiがインストールされているWikipediaではマスターのMySQLサーバに書き込まれるスレーブMySQサーバのレプリケーションの巨大なセットを使用する。Wikipediaのために使われるコードを書きたいのであればこのセットアップに関連した問題を理解することは重要である。

与えられたタスクのために使用するベストなアルゴリズムはレプリケーションを使用するかに依存することがよくある。臆面もないWikipediaの中道主義のおかげで、レプリケーションフレンドリーなバーションをよく使用される。しかし、もし望むのであれば、レプリケーションが使用されているかチェックするために$wgLoadBalancer->getServerCount() > 1 を使用することが出来る。

[edit] 遅延

大きな書き込みクエリがマスターに送られる時に遅延が最初に起こる。マスターへの書き込みは同時並行に実行されが、それらがスレーブに複製されるときに連続で実行される。トランザクションがコミットされたときにマスターはbinlogにクエリを書き込む。スレーブはbinlogを記録してそれが現れると同時にクエリの実行を開始する。それらは書き込みクエリをしている間に読み込みを提供することが出来るが、binlogから何でも読み込みはしないので、それ以上は書き込み実行を行わない。長い時間に書き込みクエリが実行する場合、書き込みクエリが終了する時間の分だけスレーブはマスターよりも遅延することを意味する。

遅延は高い読み込み負荷によって悪化する。MediaWikiのロードバランサーは30秒以上遅延したときにスレーブに読み込みを送ることを停止する。負荷率が不正に設定される、もしくは一般的に負荷がかかりすぎる場合、スレーブが恒久的に約30秒の遅延を続けることにつながるかもしれない。

すべてのスレーブが30秒以上の遅延をした場合、MediaWikiはデータベースへの書き込みを停止する。すべての編集と他の書き込みの実行は拒絶され、エラーがユーザに返される。このことによってスレーブが追いつく機会が出来る。我々がこのメカニズムを持つ前、最近の編集のリビューが困難で、スレーブはいつも数分遅延した。

これに加えて、MediaWikiはユーザがwikiで起きたイベントを年代順に見ることができることを保証しようとする。ユーザーが次のリクエストからの一貫した描写を見ることが出来る限り、数秒の遅延は許容される。セッションでマスターのbinlogの位置を保存することで行われ、それぞれのリクエストのスタート時に、それからの読み込みをする前にスレーブがその位置に追いつくのを末。これがタイムアウトした場合、読み込みはともかく許可されるが、リクエストは"遅延スレーブモード"("lagged slave mode")と見なされる。遅延スレーブモードは$wgLoadBalancer->getLaggedSlaveMode()を呼び出すことでチェック出来る。現在のところ、唯一の実際に起こる結果はページのフッターに警告が表示される。

[edit] 遅延の回避

過剰な遅延を回避するためには、大きな列を書き込むクエリを分割する。一般的には一度に一つの列を書き込む。複数の列のINSERT ... SELECTクエリは最悪の規則違反で両方とも避けるべきである。かわりにselectを最初にしてからinsertを行う。

[edit] Working with lag

私達の最大限の努力にもかかわらず、低い遅延環境を保証することは現実的ではない。遅延は通常は1秒以下であるが、ときおり30秒まで到達することがある。スケーラビリティのために、マスターの負荷を低く保つことはとても重要なことなので、シンプルにすべてのクエリをマスターに送ることは答えではない。ですので最新のデータのために本当に正真正銘のニーズがあるのであれば、次のやり方を推奨する:

1) 連続した数字、もしくはタイムスタンプのためにマスターへの素早いクエリを行う

2) スレーブ上でフルクエリを動作させ、マスターから得たデータと一致するかチェックする

3) 層ではない場合、マスター上でフルクエリを動作させる


スレーブが遅延するたびにマスターを無力にすることを避けるために、このやり方を最大限に保つべきである。大抵の場合、スレーブから読み込みをしてユーザーが遅延に対応することを任せる。

[edit] Lock contention

Wikipedia(と他のwiki群)の高い書き込み率のおかげで、MediaWikiの開発社は長時間継続するロックを避けるために書き込みを構造化することにとても慎重であることが求められる。デフォルトでは、MediaWikiは最初のクエリでトランザクションを開き、出力が送られる前にコミットする。コミットまでクエリが行われるまでロックが保たれる。ですので、書いたクエリをする前に可能な限り多くの処理によってロックの時間を減らすことが出来る。データベースへのアクセスをしないUpdateの実行は$wgPostCommitUpdateListにオブジェクトを追加することによるコミットの後まで遅延することが出来る。

このやり方は十分ではないが、独自のトランザクションにクエリの小さなグループを入れるために必要になる。次の構文を使用する:

$dbw =& wfGetDB( DB_MASTER );
$dbw->immediateBegin();
/* Do queries */
$dbw->immediateCommit();

begin()とcommit()と呼ばれる関数があるが、それらはあなたが望んだように動作しない。それらは使用しない。

ロッキングリード(例えばUPDATE句のため)の使用は推奨されません。それらはInnoDBでは実装が貧弱で恒常的なデッドロックエラーを引き起こす。ロックコンテンションでwikiを活動不能にすることも驚くほど簡単である。それらを使用しなければならない場合、そらえがoffになることを可能にする$wgAntiLockFlagsで新しいフラグを定義する。私達はWikimediaクラスター上でほぼ確実にそれをする必要があるからである。

ロッキングリードの代わりに、UPDATEのWHERE句で適切な条件を使用するか、INSERTとIGNOREの組み合わせでユニークインデックスを使用することによって、存在のチェックを書き込みクエリに結びつける。それから、クエリが成功したかどうか影響を受けた列のカウントを使用する。