Manual:Database access/ja
Contents |
This article provides an overview of database access and general database issues in MediaWiki.
データベースレイアウト [edit]
MediaWikiのデータベースレイアウトについての情報、例えばテーブルとそれらの内容の説明などについては Manual:Database layout/ja と maintenance/tables.sql を参照。
Logging into MySQL [edit]
In LocalSettings.php you’ll find your wiki’s MySQL password and username, for example:
## Database settings $wgDBtype = "mysql"; $wgDBserver = "localhost"; $wgDBname = yourwebsite $wgDBuser = yourwebsite $wgDBpassword = LongPassword
With PuTTY (a telnet/ssh client), login by entering the following:
mysql -u <$wgDBuser> -p --database=<$wgDBname>
Replacing <$wgDBuser>
and <$wgDBname>
with the LocalSettings.php
information. You will then be prompted for your password $wgDBpassword
after which you will see the mysql>
prompt.
Database Abstraction Layer [edit]
MediaWiki provides a database abstraction layer. Unless you are working on the abstraction layer, you should never directly call PHP's database functions (such as mysql_query()
or pg_send_query()
.)
The abstraction layer is accessed by using the wfGetDB()
function. For more detailed documentation on wfGetDB()
, see the entry on wfGetDB()
in the GlobalFunctions.php file reference.
Typically, wfGetDB()
is called with a single parameter, which can be the DB_SLAVE
(for read queries) or DB_MASTER
(for write queries and read queries that need to have absolutely newest information) constant. The distinction between master and slave is important in a multi-database environment, such as Wikimedia. This function will return you a Database
object that you can use to access the database. See the #Wrapper functions section below for what you can do with this Database object.
読み込みのクエリのためには、通常は次のようなもので十分である:
$dbr = wfGetDB( DB_SLAVE ); $res = $dbr->select( /* ...see docs... */ ); foreach( $res as $row ) { ... }
書き込みクエリのためには次のようなクエリが存在する:
$dbw = wfGetDB( DB_MASTER ); $dbw->insert( /* ...see docs... */ );
データベースオブジェクトがスレーブ(リードオンリー)もしくはマスター(読み込み/書き込み)であるかを追跡することの手助けにするために、読み込みには$dbrを、書く込むために$dbwを使用する。スレーブを書き込む場合、世界は爆発する。正確に表現すると、スレーブに複製された時にユニークキーの衝突のせいでスレーブがマスター上で成功するその後の書き込みクエリが失敗するかもしれない。スレーブ上の複製は停止してデータベースを修正してオンラインに戻すために数時間かかるかもしれない。スレーブ上でmy.cnfに読み込みのみに設定することでこのシナリオを回避するが、悲惨な結末になった場合、可能な限りチェックをすることが望ましい。
Wrapper functions [edit]
生のSQLのためにquery()関数を提供するが、select()やinsert()といったラッパー関数がより便利である。これらは、ある条件下でテーブルの接頭辞やエスケープといったことを考慮できる。本当に独自のSQLクエリを作成する必要がある場合、tableName()とaddQuotes()についてのドキュメントを参照する。それら両方が必要である。
Another important reason to use the high level methods rather than constructing your own queries is to ensure that your code will run properly regardless of the database type. Currently there is MySQL and reasonable support for SQLite and PostgreSQL, also somewhat limited for Oracle and DB2, but there could be other databases in the future such as MSSQL or Firebird.
In the following, the available wrapper functions are listed. For a detailed description of the parameters of the wrapper functions, please refer to class DatabaseBase's docs. Particularly see DatabaseBase::select
for an explanation of the $table
, $vars
, $conds
, $fname
, $options
, and $join_conds
parameters that are used by many of the other wrapper functions.
function select( $table, $vars, $conds = '', $fname = 'Database::select', $options = array() ); function selectRow( $table, $vars, $conds = '', $fname = 'Database::select', $options = array() ); function insert( $table, $a, $fname = 'Database::insert', $options = array() ); function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = 'Database::insertSelect', $insertOptions = array(), $selectOptions = array() ); function update( $table, $values, $conds, $fname = 'Database::update', $options = array() ); function delete( $table, $conds, $fname = 'Database::delete' ); function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = 'Database::deleteJoin' ); function buildLike(/*...*/);
Wrapper function: select() [edit]
The select() function provides the MediaWiki interface for a SELECT statement. The components of the SELECT statement are coded as parameters of the select() function. An example is
$dbr = wfGetDB( DB_SLAVE ); $res = $dbr->select( 'category', // $table array( 'cat_title', 'cat_pages' ), // $vars (columns of the table) 'cat_pages > 0', // $conds __METHOD__, // $fname = 'Database::select', array( 'ORDER BY' => 'cat_title ASC' ) // $options = array() );
This example corresponds to the query
SELECT cat_title, cat_pages FROM category WHERE cat_pages > 0 ORDER BY cat_title ASC
Arguments are either single values (such as 'category' and 'cat_pages > 0') or arrays, if more than one value is passed for an argument position (such as array('cat_pages > 0', $myNextCond)). If you pass in strings, you must manually use DatabaseBase::addQuotes() on your values as you construct the string, as the wrapper will not do this for you. The array construction for $conds is somewhat limited; it can only do equality relationships (i.e. WHERE key = 'value').
You can access individual rows of the result using a foreach loop. Once you have a row object, you can use the ->
operator to access a specific field. A full example might be
$dbr = wfGetDB( DB_SLAVE ); $res = $dbr->select( 'category', // $table array( 'cat_title', 'cat_pages' ), // $vars (columns of the table) 'cat_pages > 0', // $conds __METHOD__, // $fname = 'Database::select', array( 'ORDER BY' => 'cat_title ASC' ) // $options = array() ); $output = ''; foreach( $res as $row ) { $output .= 'Catgeory ' . $row->cat_title . ' contains ' . $row->cat_pages . " entries.\n"; }
Which will put an alphabetical list of categories with how many entries each category has in the variable $output.
基本的なクエリの最適化 [edit]
DBクエリを書く必要のあるMediaWikiの開発者はある程度のデータベースとそれらに関連したパフォーマンス問題について理解しなければならない。許容できない遅い機能を含むパッチを受け取りはされない。QueryPageから由来する特別ページ以外インデックスされていないクエリはMediaWikiには歓迎されない。大きな数の行を検査するSQLクエリを含むコードを投稿する新しい開発者には共通の落とし穴がある。COUNT(*)がO(N)(無限大)で、テーブルを数えることはバケツでちっぽけなものを数えるようなものである。
複製 [edit]
もっとも巨大なMediaWikiがインストールされているWikipediaではマスターのMySQLサーバに書き込まれるスレーブMySQLサーバのレプリケーションの巨大なセットを使用する。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を行う。
Working with lag [edit]
私達の最大限の努力にもかかわらず、低い遅延環境を保証することは現実的ではない。複製の遅延は通常は1秒以下であるが、ときおり30秒まで到達することがある。スケーラビリティのために、マスターの負荷を低く保つことはとても重要なことなので、シンプルにすべてのクエリをマスターに送ることは答えではない。ですので最新のデータのために本当に正真正銘のニーズがあるのであれば、次のやり方を推奨する:
- 連続した数字、もしくはタイムスタンプのためにマスターへの素早いクエリを行う
- スレーブ上でフルクエリを動作させ、マスターから得たデータと一致するかチェックする
- 層ではない場合、マスター上でフルクエリを動作させる
スレーブが遅延するたびにマスターを無力にすることを避けるために、このやり方を最大限に保つべきである。大抵の場合、スレーブから読み込みをしてユーザーが遅延に対応することを任せる。
Lock contention [edit]
Wikipedia(と他のwiki群)の高い書き込み率のおかげで、MediaWikiの開発社は長時間継続するロックを避けるために書き込みを構造化することにとても慎重であることが求められる。デフォルトでは、MediaWikiは最初のクエリでトランザクションを開き、出力が送られる前にコミットする。コミットまでクエリが行われるまでロックが保たれる。ですので、書いたクエリをする前に可能な限り多くの処理によってロックの時間を減らすことが出来る。データベースへのアクセスをしないUpdateの実行は $wgPostCommitUpdateList にオブジェクトを追加することによるコミットの後まで遅延することが出来る。
このやり方は十分ではないが、独自のトランザクションにクエリの小さなグループを入れるために必要になる。次の構文を使用する:
$dbw = wfGetDB( DB_MASTER ); $dbw->begin(); /* Do queries */ $dbw->commit();
Use of locking reads (e.g. the FOR UPDATE clause) is not advised. They are poorly implemented in InnoDB and will cause regular deadlock errors.
ロックコンテンションでwikiを活動不能にすることも驚くほど簡単である。それらを使用しなければならない場合、それがoffになることを可能にする $wgAntiLockFlags で新しいフラグを定義する。私達はWikimediaクラスター上でほぼ確実にそれをする必要があるからである。
ロッキングリードの代わりに、UPDATEのWHERE句で適切な条件を使用するか、INSERTとIGNOREの組み合わせでユニークインデックスを使用することによって、存在のチェックを書き込みクエリに結びつける。それから、クエリが成功したかどうか影響を受けた列のカウントを使用する。
データベースのスキーマ [edit]
Don't forget about indexes when designing databases, things may work smoothly on your test wiki with a dozen of pages, but will bring a real wiki to a halt. See above for details.
For naming conventions, see Manual:Coding conventions/Database.
SQLiteとの互換性 [edit]
When writing MySQL table definitions or upgrade patches, it is important to remember that SQLite shares MySQL's schema, but that works only if definitions are written in a specific way:
- Primary keys must be declared within main table declaration, but normal keys should be added separately with CREATE INDEX:
Wrong | Right |
---|---|
CREATE TABLE /*_*/foo ( foo_id INT NOT NULL AUTO_INCREMENT, foo_text VARCHAR(256), PRIMARY KEY(foo_id), KEY(foo_text) ); |
CREATE TABLE /*_*/foo ( foo_id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, foo_text VARCHAR(256) ); CREATE INDEX /*i*/foo_text ON /*_*/foo (foo_text); |
However, primary keys spanning over more than one field should be included in the main table definition:
CREATE TABLE /*_*/foo ( foo_id INT NOT NULL, foo_text VARCHAR(256), PRIMARY KEY(foo_id, foo_text) ); CREATE INDEX /*i*/foo_text ON /*_*/foo (foo_text);
- Don't add more than one column per statement:
Wrong | Right |
---|---|
ALTER TABLE /*_*/foo ADD foo_bar BLOB, ADD foo_baz INT; |
ALTER TABLE /*_*/foo ADD foo_bar BLOB; ALTER TABLE /*_*/foo ADD foo_baz INT; |
You can run basic compatibility checks with php sqlite.php --check-syntax filename.sql, or, if you need to test an update patch, php sqlite.php --check-syntax tables.sql filename.sql, assuming that you're in $IP/maintenance/.
関連項目 [edit]
- Manual:ORMTable
- Manual:Hooks/LoadExtensionSchemaUpdates — If an extension requires changes to the database when MediaWiki is updated, that can be done with this hook. Users can then update their wiki by running update.php.
![]() |
エンジン: MySQL – PostgreSQL /en – SQLite – IBM DB2 /en 技術文書: スキーマ (テーブル) – アクセス |
---|
言語: | English • 日本語 |
---|