MySQL バージョン 3.23.6 より、3 種類の基本テーブル形式(ISAM、HEAP、MyISAM)を選択できるようになりました。これより新しい MySQL のバージョンでは、コンパイルの方法に応じて追加のテーブル型(InnoDB または BDB)をサポートしています。1 つのデータベースに異なる型のテーブルを収容することができます。
新しいテーブルを作成するときに、そのテーブルの型を MySQL に通知できます。通常、デフォルトのテーブル型は MyISAM です。
MySQL では、テーブル定義とカラム定義を保持する `.frm' ファイルが必ず作成されます。テーブルのインデックスとデータは、テーブル型に応じて、このファイル以外の 1 つ以上のファイルに格納されます。
コンパイルまたはアクティブ化されていないテーブル型を使用しようとすると、MySQL によってそのテーブル型の代わりに MyISAM 型のテーブルが作成されます。この動作は、さまざまなテーブル型をサポートする MySQL サーバ間でテーブルをコピーする場合に便利です(ほとんどの場合、マスタサーバは安全性を高めるためにトランザクションストレージエンジンをサポートし、スレーブサーバは処理速度を高めるために非トランザクションストレージエンジンのみをサポートしています)。
MySQL の初心者は、このテーブル型の自動変更に戸惑うかもしれません。この点については、バージョン 4.1 で新しいクライアント/サーバプロトコルに警告を導入し、テーブル型が自動変更される際に警告を生成する方法で対応する予定です。
ALTER TABLE ステートメントを使用すれば、テーブルを別の型に変換できます。 See section 6.5.4 ALTER TABLE 構文。
MySQL では 2 種類のテーブルをサポートしていることに注意してください。1 つはトランザクションセーフのテーブル(InnoDB と BDB)、もう 1 つは非トランザクションセーフのテーブル(HEAP、ISAM、MERGE、MyISAM)です。
トランザクションセーフのテーブル(TST)には次の利点があります。
COMMIT コマンドで一括して受け付けられる。
ROLLBACK を実行して変更を無効にできる(オートコミットモードで実行していない場合)。
InnoDB テーブルを使用するには、少なくとも innodb_data_file_path 起動オプションを使用する必要があることに注意してください。 See section 7.5.3 InnoDB 起動オプション。
非トランザクションセーフのテーブル(NTST)には次の利点があります。
同じステートメントで TST テーブルと NTST テーブルを組み合わせると、両方の利点を活かすことができます。
MyISAM テーブル
MyISAM は、MySQL バージョン 3.23 でのデフォルトのテーブル型です。この型は ISAM コードに基づいており、多数の便利な拡張機能を備えています。
インデックスは `.MYI'(MYIndex)拡張子の付いたファイルに、データは `.MYD'(MYData)拡張子の付いたファイルにそれぞれ格納されます。MyISAM テーブルは、myisamchk ユーティリティで検査および修復することができます。See section 4.5.6.7 myisamchk を使用したクラッシュのリカバリ。 また、MyISAM テーブルを myisampack で圧縮することで、使用する領域を大幅に削減できます。 See section 4.8.4 myisampack(MySQL 圧縮読み取り専用テーブルジェネレータ)。
MyISAM の新機能は次のとおりです。
MyISAM ファイルには、テーブルが正しく閉じられたかどうかを示すフラグがある。mysqld を起動する際に --myisam-recover を指定すると、MyISAM テーブルを開く際にそのテーブルが正しく閉じられたかどうかが自動的に検査され、必要であれば修復される。
INSERT できる(同時挿入)。空きブロックは、大量のデータを含んだ可変長レコードに含まれるデータの長さが短くなるような更新をした場合、またはレコードを削除した場合に発生する。すべての空きブロックを使い切ると、それ以後の挿入は再び同時挿入になる。
AUTO_INCREMENT カラムを内部処理している。MyISAM では、このカラムが INSERT/UPDATE で自動更新される。AUTO_INCREMENT の値は、myisamchk でリセットできる。これによって AUTO_INCREMENT カラムの処理が速くなる(最低でも 10%)。また、以前の ISAM のように古い番号が再使用されない。マルチパートキーの最後の項目に AUTO_INCREMENT が定義されている場合は、以前の動作が引き続き有効となることに注意する。
AUTO_INCREMENT カラムを使用しているときなど)、分割されて上位ノードにキーが 1 つだけ含まれるようになる。これによって、キーツリーでの領域利用率が向上する。
BLOB カラムと TEXT カラムにインデックスを作成できる。
NULL 値が許可される。この場合、1 キー当たり 0 〜 1 バイトが使用される。
myisamchk を再コンパイルしなくても 64 まで拡大できる。
myisamchk を --update-state 付きで実行すると、テーブルが検査済みとしてマークされる。
myisamchk --fast は、このマークがないテーブルのみを検査する。
myisamchk -a は、キーの各部分(ISAM のようにキー全体だけではなく)に関する統計情報を格納する。
myisampack で、BLOB および VARCHAR カラムをパックできる。
CREATE TABLE の DATA/INDEX DIRECTORY="path" オプションを使用)。 See section 6.5.3 CREATE TABLE 構文。
MyISAM は、MySQL で近い将来使用可能となる次の機能もサポートしています。
VARCHAR 型のサポート。VARCHAR カラムは、長さを示す 2 バイトの値で始まる。
VARCHAR を使用するテーブルには、固定長と可変長のレコードを収容できる。
VARCHAR および CHAR は 64K まで可能。すべてのキーセグメントには、それぞれ独自の言語定義がある。これによって、MySQL ではカラムごとに言語定義を変えられる。
UNIQUE インデックスとして使用できる。これによって、テーブル内のカラムの任意の組み合わせを UNIQUE インデックスにすることができる(ただし、計算された UNIQUE インデックスでの検索は不可能)。
通常、インデックスファイルは ISAM よりも MyISAM の方がはるかに小さいので注意してください。つまり、MyISAM は一般に ISAM よりも少ないシステムリソースを使用しますが、圧縮されたインデックスへデータを挿入する際により多くの CPU 時間を必要とします。
次に示す mysqld のオプションを使用して、MyISAM テーブルの動作を変更することができます。 See section 4.6.8.4 SHOW VARIABLES。
| オプション | 説明 |
--myisam-recover=# | クラッシュしたテーブルの自動リカバリ。 |
-O myisam_sort_buffer_size=# | テーブルをリカバリする際に使用されるバッファ。 |
--delay-key-write=ALL | すべての MyISAM テーブルに対して、書き込み間でキーバッファをフラッシュしない。 |
-O myisam_max_extra_sort_file_size=# | 速度は遅くても安全なキーキャッシュインデックス作成方法をどの時点で使用するかを MySQL が判断できるようにする。注意: このパラメータを指定する単位として、4.0.3 より前はメガバイト、このバージョンからはバイトを使用する。 |
-O myisam_max_sort_file_size=# | テンポラリファイルがこの値を超えた場合に、作成されたインデックスに対して高速なソートインデックス方法を使用しない。注意: このパラメータを指定する単位として、4.0.3 より前はメガバイト、このバージョンからはバイトを使用する。 |
-O bulk_insert_buffer_size=# | バルク挿入の最適化で使用されるツリーキャッシュのサイズ。注意: これはスレッド当たりの制限値。 |
--myisam-recover=# を指定して mysqld を起動すると、自動リカバリがアクティブ化されます。See section 4.1.1 mysqld コマンドラインオプション。 テーブルが開く際に検査されます。検査の内容は、テーブルにクラッシュのマークが付いているかどうか、またはテーブルのオープンカウント変数が 0 ではなく、かつ --skip-external-locking で実行しているかどうかです。上記のどちらかが当てはまる場合は、次の処理が行われます。
myisam-recover のオプションとして FORCE を指定しなかった場合に、直前に完了したステートメントからすべてのレコードをリカバリできないときは、自動修復が中止され、エラーファイルに次のエラーメッセージが書き込まれます。
Error: Couldn't repair table: test.g00pages
FORCE オプションを指定していた場合は、上記のメッセージの代わりに次の警告がエラーファイルに書き込まれます。
Warning: Found 344 of 354 rows when repairing ./test/g00pages
注意: BACKUP オプションを指定して自動リカバリを実行する場合は、`tablename-datetime.BAK' のような名前のファイルをデータベースディレクトリからバックアップメディアに自動的に移動する cron スクリプトを用意する必要があることに注意してください。
See section 4.1.1 mysqld コマンドラインオプション。
MySQL ではさまざまなインデックスをサポートしていますが、一般に使用されるのは ISAM または MyISAM です。これらは B ツリーインデックスを使用します。このインデックスファイルのサイズは、すべてのキーについて (キー長+4)/0.67 を計算し、それを合計することで大まかに算出できます(これは、すべてのキーがソートされた順に挿入され、かつキーが一切圧縮されないという、最悪のケースを想定しています)。
文字列インデックスでは空白が圧縮されます。インデックスの最初の部分が文字列の場合は、プリフィックスも圧縮されます。文字列カラムに含まれる後続の空白が長い場合、またはそのカラムが VARCHAR カラムであるためにその長さがフルに使用されることがない場合は、空白の圧縮によってインデックスファイルが上記の数値よりも小さくなります。プリフィックスの圧縮は、文字列で始まるキーで使用されます。同一のプリフィックスを持つ文字列が多数存在する場合は、プリフィックスの圧縮が役立ちます。
MyISAM テーブルでは、テーブル作成時に PACK_KEYS=1 を指定することで、数値のプリフィックスを圧縮することもできます。この機能は、数値が上位バイトから順に格納される場合に、同一のプリフィックスを持つ整数キーが多数あるときに役立ちます。
MyISAM テーブル形式
MyISAM は、3 種類のテーブル型をサポートします。そのうち 2 つは、使用しているカラムの型に応じて自動的に選択されます。3 番目の圧縮テーブルは、myisampack ツールによってのみ作成されます。
BLOB 値を持たないテーブルを CREATE または ALTER する際に、ROW_FORMAT=# テーブルオプションを使用してテーブル形式を強制的に DYNAMIC または FIXED に設定できます。将来的には、ALTER TABLE に ROW_FORMAT=compressed | default を指定することで、テーブルを圧縮/展開できるようになります。 See section 6.5.3 CREATE TABLE 構文。
これはデフォルトの形式です。この形式は、テーブルに VARCHAR、BLOB、または TEXT 型のカラムが含まれていない場合に使用されます。
この形式は最も単純かつ最も安全です。また、ディスク上の形式としては最も高速です。速度が速いのは、ディスク上で簡単にデータを検出できるためです。検索の対象がインデックスと静的形式を使用している場合、操作はきわめて単純です。単にレコードの番号にレコードの長さを掛けるだけです。
また、テーブルをスキャンする際にも、1 回のディスク読み取りで一定数のレコードを簡単に読み取ることができます。
固定サイズの MyISAM ファイルへ書き込んでいるときにコンピュータがクラッシュした場合の保全性も証明されています。この場合、myisamchk によって各レコードの開始位置と終了位置が簡単に割り出されます。したがって、通常は、部分的に書き込まれたレコードを除くすべてのレコードを回復できます。MySQL ではすべてのインデックスをいつでも再構築できることに注意してください。
CHAR、NUMERIC、DECIMAL の各カラムは、そのカラム長までの残りの部分が空白で埋められる。
myisamchk で再編成する必要がない。ただし、多数のレコードを削除したために空いたディスク領域をオペレーティングシステムに戻す場合を除く。
この形式は、テーブルに VARCHAR、BLOB、または TEXT カラムが含まれている場合、あるいはテーブルが ROW_FORMAT=dynamic で作成された場合に使用されます。
この形式は少し複雑です。各レコードにそれぞれの長さを記録したヘッダが必要となるからです。1 つのレコードが、更新によって長くなったために、複数の場所に存在することになる可能性もあります。
OPTIMIZE table または myisamchk を使用して、テーブルをデフラグメント化することができます。VARCHAR または BLOB カラムと同じテーブル内に、頻繁にアクセス/変更する静的データがある場合は、フラグメント化を回避するために動的なカラムを他のテーブルに移動するとよいでしょう。
'')で、どの数値カラムがゼロであるかを示すビットマップが付いている(NULL 値を含んだカラムとは異なる)。文字列カラムで後続の空白を取り除いた後の長さがゼロになった場合、または数値カラムの値がゼロである場合は、それらのカラムがビットマップでマークされ、ディスクに保存されない。空白でない文字列は、長さが記録されたバイトに文字列の内容を付加して保存される。
myisamchk -r をときどき実行して、パフォーマンスを高める必要がある。一部の統計情報には、myisamchk -ei tbl_name を使用する。
3 + (フィールド数 + 7) / 8 + (char カラムの数) + 数値カラムをパックしたサイズ + 文字列の長さ + (NULL カラムの数 + 7) / 8各リンクには 6 バイトのペナルティがある。動的レコードは、更新によってレコードが拡張されるたびにリンクされる。新しいリンクは少なくともそれぞれ 20 バイトあるため、次回の拡張は同じリンクで行われると考えられる。そうでない場合は、新たなリンクが発生する。リンクの数は、
myisamchk -ed でチェックできる。すべてのリンクを削除するには、myisamchk -r を使用する。
これは、オプションの myisampack ツール(ISAM テーブルでは pack_isam)で生成される読み取り専用テーブルです。
GPL 版になる前のものも含めて、いずれも myisampack で圧縮されたテーブルを読み取ることができる。
0 の数値は 1 ビットで格納する。
BIGINT カラム(8 バイト)のすべての値が 0 〜 255 の範囲内にある場合は、このカラムを TINYINT カラム(1 バイト)として格納する。
ENUM に変換する。
myisamchk で圧縮を解除できる。
MyISAM テーブルの問題MySQL がデータの格納に使用するファイル形式は広範な検査を受けていますが、データベーステーブルの破損を招きかねない状況は常に存在します。
MyISAM テーブルが破損した場合MyISAM は信頼性の高いテーブル形式ですが(テーブルに対するすべての変更は SQL ステートメントから制御が戻る前に書き込まれます)、それでも以下の状況が発生した場合はテーブルが破損するおそれがあります。
mysqld プロセスが強制終了された場合。
テーブルが破損すると、一般に次のような現象が見られます。
Incorrect key file for table: '...'. Try to repair it というエラーが表示される。
テーブルが破損していないかどうかは、CHECK TABLE コマンドで確認できます。 See section 4.5.4 CHECK TABLE 構文。
破損したテーブルは、REPAIR TABLE で修復できます。 See section 4.5.5 REPAIR TABLE 構文。
また、mysqld が稼働していないときに、myisamchk コマンドを使ってテーブルを修復することもできます。 myisamchk syntax。
テーブルが大きく破損している場合は、原因を突き止める必要があります。 See section A.4.1 MySQL が何度もクラッシュする場合に行うこと。
この場合に最も重要なのは、mysqld が強制終了されたときにテーブルが破損したのかを確認することです(これは、mysqld エラーファイルに restarted mysqld という行が最近記録されたかどうかをチェックすることで簡単に検証できます)。これが該当しない場合は、その破損のテストケースを作成してみる必要があります。 See section E.1.6 テーブルが破損した場合にテストケースを作成する。
各 MyISAM `.MYI' ファイルのヘッダには、テーブルが適切に閉じられているかをチェックするためのカウンタがあります。
CHECK TABLE または myisamchk から次の警告が返されることがあります。
# clients is using or hasn't closed the table properly
これは、このカウンタがずれていることを意味します。テーブルの破損を意味しているわけではありませんが、少なくともテーブルを検査して問題がないことを確認する必要があります。
カウンタの仕組みは次のとおりです。
FLUSH によって、またはテーブルキャッシュに空きがないために、テーブルの最後のインスタンスが閉じられると、それまでにテーブルが一箇所でも更新されていればカウンタが減少する。
つまり、カウンタがずれる可能性があるのは、次の場合に限られます。
MyISAM テーブルが LOCK および FLUSH TABLES を使わずにコピーされた。
mysqld が使用していたテーブルで、第三者が myisamchk --recover または myisamchk --update-state を実行した。
mysqld サーバが使用しているテーブルに対し、別の mysqld サーバが REPAIR または CHECK を実行した。この場合、CHECK は実行しても問題ない(ただし他のサーバから警告を受ける)が、REPAIR は避ける必要がある。現時点では、REPAIR を実行するとデータファイルが新しいファイルで置換され、それが他のサーバに通知されないからである。
MERGE テーブル
MERGE テーブルは、MySQL バージョン 3.23.25 で新たに導入されました。コードはまだガンマ版ですが、比較的安定しているはずです。
MERGE テーブル(MRG_MyISAM テーブルとも呼ばれます)は、1 つのテーブルとして使用できる同一の MyISAM テーブルの集合です。テーブルの集合には、SELECT、DELETE、UPDATE のみを実行できます。MERGE テーブルに対して DROP を実行すると、MERGE の仕様のみが破棄されます。
WHERE なしで DELETE FROM merge_table を使用すると、テーブルに対するマッピングのみが消去され、マップされたテーブルの内容は削除されないことに注意してください(これは 4.1 で修正する予定です)。
同一のテーブルとは、すべてのテーブルが同一のカラムおよびキー情報で作成されていることを意味します。カラムのパック方法が異なるテーブル、保持するカラムがまったく同じでないテーブル、あるいはキーの順序が異なるテーブルはマージできません。ただし、一部のテーブルは myisampack で圧縮できます。 See section 4.8.4 myisampack(MySQL 圧縮読み取り専用テーブルジェネレータ)。
MERGE テーブルを作成すると、`.frm' テーブル定義ファイルおよび `.MRG' テーブルリストファイルが作成されます。`.MRG' には、1 つのインデックスファイルとして使用される複数のインデックスファイル(`.MYI' ファイル)のリストのみが含まれています。4.1.1 より前のバージョンでは、使用されるすべてのテーブルを MERGE テーブルと同じデータベースに配置する必要がありました。
今のところ、MERGE テーブルにマップするテーブルに対しては、SELECT、UPDATE、DELETE の各特権が必要です。
MERGE テーブルには、次のような効果があります。
myisampack で圧縮した後に、MERGE を作成してそれらを 1 つのファイルとして使用することができる。
MERGE テーブルを作成すれば、大きなテーブルを使用する場合に比べてはるかに処理が速くなる(RAID を使用した場合も同じ効果が得られる)。
MERGE を使用することができる。多数のさまざまな MERGE テーブルをアクティブにすることもできる。この場合、ファイルが重複していてもかまわない。
MERGE ファイルにマップされたファイルを個別に修復する方が簡単である。
MERGE テーブルは、個々のテーブルのインデックスを使用する。MERGE テーブル独自のインデックスを保持する必要はない。このため、MERGE テーブルの集合の作成または再マップにはほとんど時間がかからない。MERGE テーブルを作成するときは、キー定義を指定する必要があることに注意する。
MERGE テーブルを作成した方がよい。この方が時間がかからず、ディスク領域も大幅に節約できる。
MERGE を使用するだけで、テーブルのエイリアス/シノニムを作成できる。これによってパフォーマンスが特に影響を受けることはない(読み取りのたびに数回の間接的な呼び出しと memcpy() の呼び出しが発生するのみ)。
MERGE テーブルの欠点は次のとおりです。
MERGE テーブルには同一の MyISAM テーブルしか使用できない。
REPLACE を使用できない。
MERGE テーブルはより多くのファイル記述子を使用する。10 個のテーブルをマップする MERGE テーブルを 10 人のユーザが使用している場合は、10 × 10 + 10 個のファイル記述子を使用することになる(10 人のユーザに 10 個ずつのデータファイルと、10 個の共有インデックスファイル)。
MERGE ストレージエンジンは構成要素であるべてのテーブルに対して読み取りを発行して、指定されたキーに最も一致するテーブルをチェックする。その後に "read-next" を実行すると、MERGE ストレージエンジンは読み取りバッファを検索して次のキーを検出する。1 つのキーバッファがすべて使われるまでストレージエンジンは次のキーブロックを読み取らない。このため、eq_ref 検索では MERGE キーの処理にかなり時間がかかるが、ref 検索ではそれほど時間がかからない。 See section 5.2.1 EXPLAIN 構文(SELECT に関する情報の取得)。
MERGE テーブルによってマップされているテーブルには、DROP TABLE、ALTER TABLE、WHERE 節なしの DELETE FROM table_name、REPAIR TABLE、TRUNCATE TABLE、OPTIMIZE TABLE、または ANALYZE TABLE を実行できない。これを実行すると、MERGE テーブルが元のテーブルを参照するおそれがあり、予期しない結果を得ることがある。この問題を最も簡単に回避するには、FLUSH TABLES コマンドを発行して "開いている" MERGE テーブルを残さないようにする。
MERGE テーブルを作成するときに、1 つにまとめたいテーブルを UNION=(list-of-tables) で指定する必要があります。オプションとして、MERGE テーブルへの挿入が UNION リスト内の最初のテーブルと最後のテーブルのどちらで行われるかを、INSERT_METHOD で指定できます。INSERT_METHOD を指定しなかった場合、または NO を指定した場合は、MERGE テーブルに対するすべての INSERT コマンドでエラーが返されます。
次の例は、MERGE テーブルの使い方を示しています。
CREATE TABLE t1 (a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, message CHAR(20));
CREATE TABLE t2 (a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, message CHAR(20));
INSERT INTO t1 (message) VALUES ("Testing"),("table"),("t1");
INSERT INTO t2 (message) VALUES ("Testing"),("table"),("t2");
CREATE TABLE total (a INT NOT NULL AUTO_INCREMENT, message CHAR(20), KEY(a))
TYPE=MERGE UNION=(t1,t2) INSERT_METHOD=LAST;
SELECT * FROM total;
total テーブルではキーが一意にならないため、このテーブルでは UNIQUE または PRIMARY KEY を作成していないことに注意してください。
MySQL サーバの外部から直接 `.MRG' ファイルを操作することもできます。
shell> cd /mysql-data-directory/current-database shell> ls -1 t1.MYI t2.MYI > total.MRG shell> mysqladmin flush-tables
これで次のような操作を実行できるようになります。
mysql> SELECT * FROM total; +---+---------+ | a | message | +---+---------+ | 1 | Testing | | 2 | table | | 3 | t1 | | 1 | Testing | | 2 | table | | 3 | t2 | +---+---------+
注意: a カラムは、PRIMARY KEY として宣言されていますが、実際には一意ではないことに注意してください。MERGE テーブルでは、これを構成する一連の MyISAM テーブル全体にわたる一意性を確保できないからです。
MERGE テーブルをマップし直すには、次のいずれかの操作を実行します。
DROP を実行し、テーブルを再作成する。
ALTER TABLE table_name UNION=(...) を使用する。
MERGE テーブルとこれを構成するすべてのテーブルに対して FLUSH TABLE を発行することで、ストレージエンジンが新しい定義ファイルを読み取るようにする。
MERGE テーブルの問題
次に挙げるのは、MERGE テーブルに関する既知の問題です。
MERGE テーブルでは、テーブル全体で UNIQUE 制約を保持できない。INSERT を実行すると、INSERT_METHOD=xxx に応じてデータが最初または最後のテーブルに挿入される。挿入先の MyISAM テーブルではそのデータが一意であることが保証されるが、このテーブルは他の MyISAM テーブルについてまったく関知しない。
WHERE なしで DELETE FROM merge_table を使用すると、テーブルに対するマッピングのみが消去され、マップされたテーブルの内容は一切削除されない。
MERGE テーブルで使用されているテーブルに対して RENAME TABLE を実行すると、テーブルが破損するおそれがある。これは MySQL 4.1.x で修正される予定。
MERGE 型のテーブルを作成するときに、その構成要素となるテーブルの型に互換性があるか、またはそのテーブル自体が存在するかがチェックされない。MERGE テーブルが使用される際に、マップされたテーブル間でレコード長が等しいかどうかが MySQL によって簡単にチェックされるが、これでは完全な検証にならない。
このような方法で MERGE をテーブルを使用していると、高い確率で未知の問題が発生する。
ALTER TABLE を使用して MERGE テーブルで使用されているテーブルに UNIQUE インデックスを追加した後に、再び ALTER TABLE を使用して MERGE テーブルに通常のインデックスを追加すると、以前の一意でないキーがテーブルに存在していた場合に、テーブルに対するキーの順序が変わる。これは、重複キーをできるだけ早く検出できるように、ALTER TABLE が UNIQUE キーを通常のキーの前に配置するためである。
MERGE テーブルが使用しているテーブルに対して DROP TABLE を実行できない。これは、MERGE ストレージエンジンが実行するテーブルマッピングを MySQL の上位レイヤが認識しないためである。Windows では開いているファイルを破棄できないため、対象のテーブルを破棄する前に、FLUSH TABLES ですべての MERGE テーブルをフラッシュするか、MERGE テーブルを破棄する必要がある。これについては、ビューを導入するときに修正する。
ISAM テーブル
MySQL バージョン 5.0 では、ISAM テーブル型が廃止されます。MySQL 4.1 では、このテーブル型がソースに含まれていてもコンパイルできません。このテーブルハンドラの実装としては MyISAM の方が優れているため、できるだけ早く ISAM テーブルを MyISAM テーブルに変換する必要があります。
ISAM は、B ツリーインデックスを使用します。インデックスは `.ISM' 拡張子の付いたファイルに保存され、データは `.ISD' 拡張子の付いたファイルに保存されます。ISAM テーブルは、isamchk ユーティリティで検査および修復することができます。 See section 4.5.6.7 myisamchk を使用したクラッシュのリカバリ。
ISAM には、次の機能と特性があります。
MyISAM テーブルに当てはまることは、ほとんどの場合 ISAM テーブルにも当てはまります。See section 7.1 MyISAM テーブル。MyISAM テーブルとの主な相違点は次のとおりです。
ISAM テーブルでは、OS/プラットフォームをまたがってバイナリを移植できない。
myisampack ではなく pack_isam が使用される。
ISAM テーブルを MyISAM テーブルに変換して mysqlcheck などのユーティリティを使用できるようにするには、ALTER TABLE ステートメントを使用します。
mysql> ALTER TABLE tbl_name TYPE = MYISAM;
組み込み式の MySQL では、ISAM テーブルがサポートされません。
HEAP テーブル
HEAP テーブルは、ハッシュインデックスを使用し、メモリに格納されます。これによって処理は速くなりますが、MySQL がクラッシュすると、このテーブルに格納されたすべてのデータが失われます。HEAP は、テンポラリテーブルとして非常に便利です。
MySQL の内部 HEAP テーブルは、オーバフローエリアなしの 100% 動的ハッシュを使用します。フリーリスト用の余分な領域は必要ありません。また、HEAP テーブルでは、ハッシュテーブルで一般に見られる削除 + 挿入に伴う問題も起こりません。
mysql> CREATE TABLE test TYPE=HEAP SELECT ip,SUM(downloads) AS down
-> FROM log_table GROUP BY ip;
mysql> SELECT COUNT(ip),AVG(down) FROM test;
mysql> DROP TABLE test;
次に、HEAP テーブルを使用する際の考慮事項を示します。
CREATE ステートメントには必ず MAX_ROWS を指定する。
= と <=> しか使用できない(その代わりきわめて高速)。
HEAP テーブルでは、レコードの検索にキー全体を使用するしかない。一方 MyISAM テーブルでは、キーの任意のプリフィックスを使ってレコードを検索できる。
HEAP テーブルは、固定レコード長形式を使用する。
HEAP は、BLOB カラムと TEXT カラムをサポートしない。
HEAP は、AUTO_INCREMENT カラムをサポートしない。
HEAP が NULL カラムのインデックスをサポートしない。
HEAP テーブルでは一意でないキーを使用できる(ハッシュテーブルではあまり使用されない)。
HEAP テーブルは、他のテーブルと同様にすべてのクライアント間で共有される。
ORDER BY を実行できない)。
HEAP テーブルのデータは、小さなブロックで割り当てられる。テーブルは 100% 動的である(挿入時)。オーバフローエリアも余分なキースペースも必要ない。削除されたレコードはリンクされたリストに保存され、新しいデータをテーブルに挿入する際に再使用される。
HEAP テーブルに対して十分な追加メモリが必要。
DELETE FROM heap_table、TRUNCATE heap_table、または DROP TABLE heap_table を実行する必要がある。
MyISAM テーブルを HEAP テーブルに変更すると、この点が一部のクエリに影響する可能性がある。
max_heap_table_size より大きなサイズの HEAP テーブルは作成できないようになっている。
HEAP テーブルで 1 つのレコードに必要なメモリは、次のように計算します。
SUM_OVER_ALL_KEYS(キーの最大長 + sizeof(char*) * 2) + ALIGN(レコードの長さ+1, sizeof(char*))
sizeof(char*) は、32 ビットマシンでは 4、64 ビットマシンでは 8 です。
InnoDB テーブル
InnoDB は、MySQL における、コミット、ロールバック、クラッシュリカバリの各機能を備えたトランザクションセーフ(ACID 準拠)のストレージエンジンです。InnoDB は、行レベルでロックを行い、SELECT ステートメントで Oracle 式の非ロックの読み取り一貫性(consistent read)を実現します。これらの機能によって、マルチユーザでの並行性とパフォーマンスが向上します。InnoDB ではロックエスカレーションが不要です。InnoDB での行レベルロックはわずかなスペースしか使用しないからです。InnoDB は、MySQL で FOREIGN KEY 制約を最初にサポートしたストレージエンジンです。
InnoDB は、大容量のデータを処理する際に最大限のパフォーマンスを実現するように設計されています。その CPU 効率は、おそらく他のディスクベースのリレーショナルデータベースエンジンのどれよりも優れています。
InnoDB は、高いパフォーマンスを必要とする多くの大規模データベースサイトで実際に使用されています。有名なインターネットニュースサイトの Slashdot.org は、InnoDB 上で稼働しています。Mytrix, Inc. では、1 TB を超えるデータを InnoDB に格納し、別のサイトでは InnoDB で 1 秒間に平均 800 件の挿入/更新を処理しています。
技術的には、InnoDB は MySQL のデータベースバックエンドです。InnoDB は、データとインデックスをキャッシュするための専用のバッファプールをメインメモリに持っています。InnoDB のテーブルとインデックスはテーブルスペースに格納されます。これは、複数のファイル(またはローデバイス)で構成されている場合があります。この点は、各テーブルを個別のファイルに格納する MyISAM テーブルなどとは異なっています。InnoDB テーブルは、ファイルサイズが 2 GB に制限されているオペレーティングシステム上でも、任意のサイズにすることができます。
InnoDB に関する最新情報は、http://www.innodb.com/ で参照できます。InnoDB マニュアルの最新版もこのサイトに収録されています。
InnoDB は、MySQL と同じ GNU GPL License Version 2(1991 年 6 月付け)の下でリリースされています。MySQL/InnoDB を配布する場合に、アプリケーションが GPL ライセンスの制約を満たしていないときは、https://order.mysql.com/?sub=pg&pg_no=1 から MySQL Pro の商用ライセンスを購入する必要があります。
MySQL バージョン 4.0 より、InnoDB はデフォルトで MySQL に組み込まれています。次の情報は、3.23 シリーズだけに該当します。
InnoDB テーブルは、3.23.34a より MySQL ソースディストリビューションに含まれるようになりました。 ただし 3.23 シリーズでは、InnoDB は MySQL-Max バイナリディストリビューションのみに組み込まれています。Windows の場合は、-Max バイナリが標準のバイナリディストリビューションに含まれています。
InnoDB をサポートする MySQL のバイナリバージョンをダウンロードした場合は、MySQL のマニュアルに従って MySQL のバイナリバージョンをインストールしてください。すでに MySQL-3.23 をインストールしている場合に MySQL-Max を最も簡単にインストールするには、サーバの実行ファイル `mysqld' を、-Max ディストリビューションの実行ファイルで置き換えます。MySQL と MySQL-Max は、サーバの実行ファイルの名前のみが異なります。 See section 2.2.12 MySQL バイナリディストリビューションのインストール。 See section 4.8.5 mysqld-max(拡張 mysqld サーバ)。
InnoDB をサポートする MySQL をコンパイルするには、MySQL-3.23.34a 以降のバージョンを http://www.mysql.com/ からダウンロードし、--with-innodb オプションで MySQL をコンフィギャします。MySQL ソースディストリビューションのインストールについては、MySQL のマニュアルを参照してください。 See section 2.3 MySQL ソースディストリビューションのインストール。
cd /path/to/source/of/mysql-3.23.37 ./configure --with-innodb
MySQL-Max-3.23 で InnoDB テーブルを使用するには、オプション設定ファイル `my.cnf'(Windows の場合は `my.ini' でも可)の [mysqld] セクションで設定パラメータを指定しなければなりません。
3.23 では、少なくとも innodb_data_file_path でデータファイルの名前とサイズを指定する必要があります。`my.cnf' で innodb_data_home_dir を指定しない場合は、デフォルトで MySQL の datadir にこれらのファイルが作成されます。innodb_data_home_dir を空の文字列として指定すれば、innodb_data_file_path でデータファイルへの絶対パスを指定できます。
これを最小限の方法で変更するには、[mysqld] セクションに次の行を追加します。
innodb_data_file_path=ibdata:30M
ただし、高いパフォーマンスを得るには、推奨された方法でオプションを指定するのが最善です。 See section 7.5.3 InnoDB 起動オプション。
MySQL バージョン 3.23 で InnoDB テーブルを有効にする方法については、section 7.5.2 MySQL バージョン 3.23 での InnoDB を参照してください。
MySQL-4.0 では、InnoDB テーブルを有効にするために特に何かをする必要はありません。
MySQL-4.0 および MySQL-4.1 のデフォルトの動作として、MySQL の datadir に自動拡張する 10 MB の `ibdata1' ファイルが 1 つと、5 MB の `ib_logfile' ログファイルが 2 つ作成されます(MySQL-4.0.0 および 4.0.1 のデータファイルは 64 MB で、自動拡張しません)。
注意: 高いパフォーマンスを得るには、この後の例に示されている InnoDB の各種パラメータを明示的に設定する必要があります。
InnoDB テーブルを使用しない場合は、MySQL オプション設定ファイルに skip-innodb オプションを指定して InnoDB を動作させないようにできます。
[mysqld] skip-innodb
バージョン 3.23.50 および 4.0.2 より、innodb_data_file_path 行の最後のデータファイルを、自動拡張ファイルとして指定できるようになりました。その場合の innodb_data_file_path の構文は次のとおりです。
innodb_data_file_path = データファイルのパス:サイズ;データファイルのパス:サイズ;... ... ;データファイルのパス:サイズ[:autoextend[:max:サイズ]]
最後のデータファイルに autoextend オプションを指定すると、テーブルスペースに空きがなくなった場合に、InnoDB が最後のデータファイルを拡張します。1 回の増分は 8 MB です。次に例を示します。
innodb_data_home_dir = innodb_data_file_path = /ibdata/ibdata1:100M:autoextend # # 注意: innodb_data_home_dir に空文字を指定した場合、 # innodb_data_file_path に与えるパスは絶対パスになる
この例では、InnoDB に対し、初期サイズが 100 MB のデータファイルを 1 つだけ作成し、スペースが足りなくなった場合に 8 MB 単位で拡張するように指定しています。ディスクがいっぱいになった場合は、たとえば別のディスクに新たなデータファイルを追加することもできます。その場合は、autoextend が指定されている `ibdata1' のサイズを確認し、そのサイズが 1,024 × 1,024 バイト(= 1 MB)の倍数になるように丸めた値を計算し、計算で得られた値を innodb_data_file_path の `ibdata1' のサイズとして明示的に指定する必要があります。これで、新たなデータファイルを `ibdata1' の後ろに追加できます。
innodb_data_home_dir = innodb_data_file_path = /ibdata/ibdata1:988M;/disk2/ibdata2:50M:autoextend # # 注意: この例では最初 100M の ibdata1 が、 # 888M (1024 * 1024の倍数) だけ拡張されていたので、988M の指定になっている。
ファイルシステムの最大ファイルサイズが 2 GB である場合は注意が必要です。InnoDB は、OS の最大ファイルサイズを考慮しません。このようなファイルシステムでは、データファイルの最大サイズを指定する必要があります。
innodb_data_home_dir = innodb_data_file_path = /ibdata/ibdata1:100M:autoextend:max:2000M
単純な `my.cnf' の例。128 MB の RAM と 1 つのハードディスクを搭載したコンピュータを使用しているとします。次に示すのは、InnoDB の `my.cnf' または `my.ini' における設定パラメータの例です。この例では、MySQL-Max-3.23.50 以降、または MySQL-4.0.2 以降が稼働していることを想定しています。この例は、Unix と Windows の両方で、ユーザが InnoDB データファイルとログファイルを複数のディスクに分散させない場合に適しています。この設定パラメータによって、自動拡張するデータファイル `ibdata1' および 2 つの InnoDB ログファイル `ib_logfile0' と `ib_logfile1' が、MySQL の datadir(一般には `/mysql/data')に作成されます。
また、アーカイブされた小さな InnoDB ログファイル `ib_arch_log_0000000000' も datadir に作成されます。
[mysqld] # You can write your other MySQL server options here # ... # Datafile(s) must be able to # hold your data and indexes. # Make sure you have enough # free disk space. innodb_data_file_path = ibdata1:10M:autoextend # Set buffer pool size to # 50 - 80 % of your computer's # memory innodb_buffer_pool_size=70M innodb_additional_mem_pool_size=10M # Set the log file size to about # 25 % of the buffer pool size innodb_log_file_size=20M innodb_log_buffer_size=8M # Set ..flush_log_at_trx_commit # to 0 if you can afford losing # some last transactions innodb_flush_log_at_trx_commit=1
datadir でファイルを作成する権限が MySQL サーバにあるかどうかを確認してください。
一部のファイルシステムでは、データファイルを 2 GB 未満にする必要があることに注意してください。 ログファイルをすべて合わせたサイズは、4 GB 未満でなければなりません。また、データファイルをすべて合わせたサイズは、10 MB 以上でなければなりません。
InnoDB データベースを初めて作成するときには、コマンドプロンプトから MySQL サーバを起動するのが最善です。 InnoDB によってデータベースの作成に関する情報が画面に出力されるので、処理の経過を確認できます。 画面出力の例については、次のセクションを参照してください。 たとえば、Windows では次のようにして `mysqld-max.exe' を起動することにより、MySQL サーバはコンソールを閉じなくなります。
your-path-to-mysqld\mysqld-max --console
Windows では、`my.cnf' または `my.ini' をどこに配置すればいいでしょうか。 Windows での規則は次のとおりです。
SET コマンドを使用すれば、%WINDIR% の値を出力できる。
Unix では、どこでオプションを指定すればいいでしょうか。 Unix では、`mysqld' が次のファイル(存在する場合)から次の順序でオプションを読み取ります。
--defaults-extra-file=... で指定されたファイル
`COMPILATION_DATADIR' は MySQL データディレクトリで、`mysqld' がコンパイルされたときに ./configure オプションとして指定されたものです(一般にはバイナリインストール用の `/usr/local/mysql/data'、またはソースインストール用の`/usr/local/var')。
`mysqld' がその `my.cnf' または `my.ini' をどこから読み取るかがわからない場合は、サーバへの最初のコマンドラインオプションとしてパスを指定できます(mysqld --defaults-file=your_path_to_my_cnf)。
InnoDB は、データファイルへのディレクトリパスを決定する場合に、innodb_data_home_dir に定義されたパスのテキストを innodb_data_file_path 内のデータファイル名またはパスのテキストと結合し、必要に応じて間にスラッシュまたはバックスラッシュを挿入します。`my.cnf' でキーワード innodb_data_home_dir がまったく指定されていない場合は、MySQL の datadir を意味する 'ドット' ディレクトリ `./' がデフォルトで使用されます。
高度な `my.cnf' の例。2 GB の RAM と 3 つの 60 GB ハードディスクを搭載した Linux コンピュータを使用しているとします。ハードディスクのディレクトリパスは、それぞれ `/'、`/dr2'、`/dr3' です。次に示すのは、InnoDB の `my.cnf' における設定パラメータの例です。
注意: ディレクトリは InnoDB によって作成されないので、各自で作成する必要があります。データおよびロググループのホームディレクトリを作成するには、Unix または MS-DOS の mkdir コマンドを使用します。
[mysqld] # You can write your other MySQL server options here # ... innodb_data_home_dir = # Datafiles must be able to # hold your data and indexes innodb_data_file_path = /ibdata/ibdata1:2000M;/dr2/ibdata/ibdata2:2000M:autoextend # Set buffer pool size to # 50 - 80 % of your computer's # memory, but make sure on Linux # x86 total memory usage is # < 2 GB innodb_buffer_pool_size=1G innodb_additional_mem_pool_size=20M innodb_log_group_home_dir = /dr3/iblogs # .._log_arch_dir must be the same # as .._log_group_home_dir innodb_log_arch_dir = /dr3/iblogs innodb_log_files_in_group=3 # Set the log file size to about # 15 % of the buffer pool size innodb_log_file_size=150M innodb_log_buffer_size=8M # Set ..flush_log_at_trx_commit to # 0 if you can afford losing # some last transactions innodb_flush_log_at_trx_commit=1 innodb_lock_wait_timeout=50 #innodb_flush_method=fdatasync #innodb_thread_concurrency=5
2 つのデータファイルを異なるディスクに配置したことに注意してください。 InnoDB は、データファイルによって形成されるテーブルスペースをボトムアップ式に埋めていきます。場合によっては、すべてのデータを同じ物理ディスクに配置しない方がパフォーマンスが良くなります。ログファイルをデータとは別のディスクに配置すると、ほとんどの場合パフォーマンスは良くなります。 ローデバイス をデータファイルとして使用することもできます。一部の Unix では、この方法で I/O の処理速度が向上します。`my.cnf' でこのようなデータファイルを指定する方法については、マニュアルで section 7.5.13.1 ディスク I/O に関するセクションを参照してください。
警告: Linux x86 では、メモリ使用率の設定を高くし過ぎないように注意してください。glibc はプロセスヒープがスレッドスタックよりも大きくなることを許可しており、その場合にサーバがクラッシュします。次の計算式を見てください。
innodb_buffer_pool_size + key_buffer_size + max_connections * (sort_buffer_size + read_buffer_size) + max_connections * 2 MB
この値が、2 GB に近いか、2 GB を超えていると危険です。各スレッドはスタックを使用し(通常は 2 MB。ただし MySQL AB バイナリでは 256 KB のみ)、最悪の場合、sort_buffer_size + read_buffer_size の大きさの追加メモリも使用します。
他の `mysqld' サーバパラメータはどのように調整すればいいでしょうか。: ほとんどのユーザに適した一般的な値は次のとおりです。
skip-locking max_connections=200 read_buffer_size=1M sort_buffer_size=1M # Set key_buffer_size to 5 - 50% # of your RAM depending on how # much you use MyISAM tables, but # keep key_buffer_size + InnoDB # buffer pool size < 80% of # your RAM key_buffer_size=...
注意: 4.0 より前のバージョンでは、一部のパラメータを set-variable = innodb... = 123 のように指定する必要があります。
各設定パラメータの意味は次のとおりです。
| オプション | 説明 |
innodb_file_per_table | 4.1.1 より利用可能。このオプションによって、InnoDB は作成された各テーブルを独自の `.ibd' ファイルに格納するようになる。複数のテーブルスペースに関するセクションを参照。 |
innodb_data_home_dir | ディレクトリパスの中の、すべての InnoDB データファイルに共通な部分。`my.cnf' でこのオプションを指定しなかった場合のデフォルトは、MySQL の datadir。このオプションには空の文字列も指定できる。その場合、innodb_data_file_path に絶対ファイルパスを指定できる。
|
innodb_data_file_path | 個々のデータファイルへのパスとそのサイズ。各データファイルへのフルディレクトリパスは、ここで指定したパスに innodb_data_home_dir を連結することで取得できる。ファイルサイズはメガバイトで指定されるため、上記のようにサイズの後に 'M' が付加されている。InnoDB は 'G' という略称も認識する。1 G は 1,024 MB を意味する。3.23.44 より、大きなファイルをサポートするオペレーティングシステムで 4 GB を超えるファイルサイズも設定できるようになった。一部のオペレーティング システムでは、ファイルを 2 GB 未満にする必要がある。4.0 より、innodb_data_file_path を指定しない場合のデフォルトの動作として、10 MB の自動拡張データファイル `ibdata1' が作成される。ファイルの合計サイズは、10 MB 以上でなければならない。
|
innodb_mirrored_log_groups | データベースのために保持しておくロググループのコピーの数。現時点では 1 に設定する必要がある。 |
innodb_log_group_home_dir | InnoDB ログファイルへのディレクトリパス。`my.cnf' でこのオプションを指定しなかった場合は、デフォルトで MySQL の datadir が設定される。
|
innodb_log_files_in_group | ロググループ内のログファイルの数。InnoDB はこれらのログファイルに循環的に書き込みを行う。このパラメータの推奨値とデフォルト値は共に 2。 |
innodb_log_file_size | ロググループ内の各ログファイルのサイズ(メガバイト)。実際的な値の範囲は、1M から、下で指定するバッファプールのサイズの 1/n まで(n はグループ内のログファイルの数)。この値が大きいほど、バッファプールで必要となるチェックポイントフラッシュの回数が減るため、ディスク I/O が削減される。ただし、ログファイルが大きいと、クラッシュした場合のリカバリに時間がかかる。32 ビットコンピュータでは、ログファイルの合計サイズを 4 GB 未満にする必要がある。デフォルトは 5M。 |
innodb_log_buffer_size | InnoDB がディスク上のログファイルにログを書き出すために使用するバッファのサイズ。実際的な値は 1M 〜 8M。ログバッファを大きくすると、トランザクションコミットまでディスクにログを書き出すことなく大きなトランザクションを実行できる。大きなトランザクションがある場合は、このようにログバッファを大きくすることでディスク I/O を削減できる。 |
innodb_flush_log_at_trx_commit | 通常、このパラメータは 1 に設定する。これによって、トランザクションコミット時にログがディスクにフラッシュされ、トランザクションによる変更が確定されてデータベースクラッシュを免れる。このような安全性を必要とせず、かつ小さなトランザクションを実行している場合は、このオプションを 0 または 2 に設定してログへのディスク I/O を削減できる。値 0 を指定すると、ログファイルへのログの書き込み、およびディスクへのログファイルのフラッシュが1 秒に約1 回しか行われなくなる。値 2 を指定すると、ログファイルへのログの書き込みはコミットのたびに行われるが、ディスクへのログファイルのフラッシュは 1 秒に約 1 回しか行われなくなる。MySQL-4.0.13 よりデフォルト値が 0 から 1 に変更された。 |
innodb_log_arch_dir | ログのアーカイブを使用する場合に、いっぱいになったログファイルがアーカイブされるディレクトリ。現時点では、このパラメータを innodb_log_group_home_dir と同じ値に設定する必要がある。
|
innodb_log_archive | 現時点では、このパラメータを 0 に設定する必要がある。バックアップからのリカバリは MySQL が独自のログファイルを使って行うため、現時点では InnoDB のログファイルをアーカイブする必要はない。 |
innodb_buffer_pool_size | InnoDB が、そのテーブルのデータやインデックスをキャッシュするために使用するメモリバッファのサイズ。この値が大きいほど、テーブル内のデータへのアクセスに必要なディスク I/O が少なくなる。データベース専用サーバでは、このパラメータをマシンの物理メモリの 80% にまで設定できる。ただし、物理メモリの競合によってオペレーティングシステムでページングが発生する可能性があるため、あまり大きな値は設定しないようにする。 |
innodb_buffer_pool_awe_mem_mb | 32 ビット版 Windows の AWE メモリに配置されるバッファプールのサイズ(MB)。4.1.0 から利用可能で、32 ビット版 Windows にのみ関係する。使用する 32 ビット版 Windows オペレーティングシステムが、Address Windowing Extensions(AWE)と呼ばれる 4 GB を超えるメモリをサポートしている場合は、このパラメータを使用して InnoDB バッファプールを AWE 物理メモリに割り当てることができる。指定可能な最大値は、64000。このパラメータを指定した場合、innodb_buffer_pool_size は mysqld の 32 ビットアドレス空間におけるウィンドウとなる(このアドレス空間で InnoDB が AWE メモリをマップする)。その場合の innodb_buffer_pool_size に適したサイズは 500M。
|
innodb_additional_mem_pool_size | InnoDB がデータディクショナリの情報とその他の内部データ構造を格納するために使用するメモリプールのサイズ。このパラメータの実際的な値は 2M であるが、アプリケーションで使用するテーブルの数が多いほど、この値を大きくする必要がある。InnoDB は、このプールのメモリを使い果たすと、オペレーティングシステムからメモリを割り当てるようになり、MySQL エラーログに警告メッセージを書き込む。 |
innodb_file_io_threads | InnoDB におけるファイル I/O スレッドの数。通常、この値は 4 にする必要があるが、Windows ではこれより大きな数を指定するとディスク I/O の面で有利になる場合がある。 |
innodb_lock_wait_timeout | ロック待機の状態になった InnoDB トランザクションがロールバックされるまでのタイムアウト時間(秒)。InnoDB は、そのロックテーブルでトランザクションのデッドロックを自動的に検出し、そのトランザクションをロールバックする。LOCK TABLES コマンドを使用するか、または同じトランザクションで InnoDB 以外のトランザクションセーフのストレージエンジンを使用すると、InnoDB が検出できないデッドロックが発生することがある。タイムアウトは、このような状況の解決に役立つ。
|
innodb_flush_method | 3.23.40 より使用可能。デフォルト値は fdatasync。これ以外に、O_DSYNC を指定できる。
|
innodb_force_recovery | 警告: このオプションは、破損したデータベースからテーブルをダンプする必要がある緊急事態でのみ定義する必要がある。指定可能な値は 1 〜 6。それぞれの値の意味については、この後の「強制的なリカバリ」を参照。InnoDB では、安全対策として、このオプションが 0 より大きいときはユーザがデータを変更できないようになっている。このオプションは、バージョン 3.23.44 より使用可能。 |
MySQL がすでにインストールされ、`my.cnf' の編集も終わって必要な InnoDB 設定パラメータが設定されているとします。 MySQL を起動する前に、InnoDB データファイルとログファイル用に指定したディレクトリが存在すること、およびそれらのディレクトリへのアクセス権があることを確認する必要があります。InnoDB によって作成されるのはファイルのみで、ディレクトリは作成されません。データファイルとログファイルを保存できるだけの十分なディスク領域があることも確認してください。
MySQL を起動すると、InnoDB はデータファイルとログファイルの作成を開始し、次のようなメッセージを出力します。
~/mysqlm/sql > mysqld InnoDB: The first specified datafile /home/heikki/data/ibdata1 did not exist: InnoDB: a new database to be created! InnoDB: Setting file /home/heikki/data/ibdata1 size to 134217728 InnoDB: Database physically writes the file full: wait... InnoDB: datafile /home/heikki/data/ibdata2 did not exist: new to be created InnoDB: Setting file /home/heikki/data/ibdata2 size to 262144000 InnoDB: Database physically writes the file full: wait... InnoDB: Log file /home/heikki/data/logs/ib_logfile0 did not exist: new to be created InnoDB: Setting log file /home/heikki/data/logs/ib_logfile0 size to 5242880 InnoDB: Log file /home/heikki/data/logs/ib_logfile1 did not exist: new to be created InnoDB: Setting log file /home/heikki/data/logs/ib_logfile1 size to 5242880 InnoDB: Log file /home/heikki/data/logs/ib_logfile2 did not exist: new to be created InnoDB: Setting log file /home/heikki/data/logs/ib_logfile2 size to 5242880 InnoDB: Started mysqld: ready for connections
これで、新しい InnoDB データベースが作成されました。mysql などの通常使用する MySQL クライアントプログラムで MySQL サーバに接続できます。
`mysqladmin shutdown' で MySQL サーバをシャットダウンすると、InnoDB から次のようなメッセージが出力されます
010321 18:33:34 mysqld: Normal shutdown 010321 18:33:34 mysqld: Shutdown Complete InnoDB: Starting shutdown... InnoDB: Shutdown completed
これで、データファイルとログのディレクトリを参照できるようになり、作成したファイルを確認できます。ログディレクトリには、`ib_arch_log_0000000000' という名前の小さなファイルも作成されます。これは、データベース作成時に作成されたファイルで、その後に InnoDB がログのアーカイブをオフに切り替えています。 MySQL を再び起動すると、次のようなメッセージが出力されます。
~/mysqlm/sql > mysqld InnoDB: Started mysqld: ready for connections
InnoDB がファイル操作中にオペレーティングシステムエラーを出力した場合、通常は次のいずれかが原因です。
innodb_data_home_dir または innodb_data_file_path に構文エラーがある。
InnoDB データベースの作成で問題が生じた場合は、InnoDB が作成したすべてのファイルを削除する必要があります。つまり、MySQL データベースディレクトリから、すべてのデータファイル、すべてのログファイル、アーカイブされた小さなログファイルを削除し、InnoDB テーブルを作成した場合は、これらのテーブルに対応する `.frm' ファイルも削除します。これで、InnoDB データベースを再び作成することができます。
mysql test で MySQL クライアントを起動したとします。
InnoDB 形式のテーブルを作成するには、テーブルを作成する SQL コマンドで、TYPE = InnoDB を指定する必要があります。
CREATE TABLE CUSTOMER (A INT, B CHAR (20), INDEX (A)) TYPE = InnoDB;
この SQL コマンドによって、`my.cnf' に指定したデータファイルから成る InnoDB テーブルスペースに、テーブルおよびカラム A のインデックスが作成されます。さらに MySQL によって、MySQL データベースディレクトリ `test' に `CUSTOMER.frm' ファイルが作成されます。
内部では、InnoDB が独自のデータディクショナリに 'test/CUSTOMER' テーブルのエントリを追加します。したがって、MySQL の別のデータベースに同じ CUSTOMER という名前のテーブルを作成しても、InnoDB 内部でテーブル名が衝突することはありません。
TYPE = InnoDB で作成した任意のテーブルに対して MySQL のテーブルステータスコマンドを発行することで、InnoDB テーブルスペースの空き容量を照会できます。テーブルスペースの空き容量は、SHOW の出力のテーブルコメントセクションに表示されます。次に例を示します。
SHOW TABLE STATUS FROM test LIKE 'CUSTOMER';
SHOW が InnoDB テーブルについて出力する統計情報は概算であることに注意してください。これらの情報は、SQL 文解析の最適化で使用されます。ただし、テーブルとインデックスに予約されているサイズ(バイト単位)は正確です。
InnoDB には、インデックスを別途作成するための特別な最適化機能がありません。
このため、テーブルをエクスポートしてインポートし、その後にインデックスを作成しなおしても意味がありません。
テーブルを最も速く InnoDB に変換するには、InnoDB テーブルに直接データを挿入します。つまり、ALTER TABLE ... TYPE=INNODB を使用するか、あるいは同じ定義で空の InnoDB テーブルを作成し、INSERT INTO ... SELECT * FROM ... でレコードを挿入します。
挿入処理を管理しやすくするために、大きなテーブルは分割して挿入するとよいでしょう。
INSERT INTO newtable SELECT * FROM oldtable WHERE yourkey > something AND yourkey <= somethingelse;
すべてのデータを挿入した後に、テーブル名を変更することができます。
大きなテーブルを変換する際には、InnoDB のバッファプールサイズを大きくして、ディスク I/O を削減する必要があります。ただし、物理メモリの 80% を超えないようにしてください。 InnoDB のログファイルおよびログバッファも大きなサイズに設定します。
テーブルスペースが不足していないことを確認します。InnoDB テーブルは、MyISAM テーブルよりも多くの領域を使用します。ALTER TABLE で領域が足りなくなると、ロールバックが実行されます。ロールバックがディスクバウンドすると、完了までに数時間を要する可能性があります。
挿入の際は、InnoDB が挿入バッファを使用してセカンダリインデックスレコードをまとめてインデックスにマージします。これによって、ディスク I/O が大幅に削減されます。ロールバックではこのようなメカニズムが使用されないため、挿入の 30 倍の時間がかかる場合があります。
ロールバックが暴走した場合は、データベースに貴重なデータがなければ、膨大なディスク I/O の完了を待つよりも、データベースプロセスを強制終了し、すべての InnoDB データファイルとログファイル、および InnoDB テーブルの `.frm' ファイルを削除したうえで、再度ジョブを実行した方が得策です。
FOREIGN KEY 制約バージョン 3.23.43b より、InnoDB に外部キー制約が装備されるようになりました。 InnoDB は、データの完全性を守るためにユーザが外部キー制約を定義できるようにした最初の MySQL テーブル型です。
InnoDB における外部キー制約定義の構文は次のとおりです。
[CONSTRAINT [symbol]] FOREIGN KEY (index_col_name, ...)
REFERENCES table_name (index_col_name, ...)
[ON DELETE {CASCADE | SET NULL | NO ACTION
| RESTRICT}]
[ON UPDATE {CASCADE | SET NULL | NO ACTION
| RESTRICT}]
どちらのテーブルも InnoDB 型でなければなりません。テーブル内には、外部キーカラムが最初のカラムとして同じ順序で列挙されているインデックスが必要です。また、参照テーブル内には、参照カラムが最初のカラムとして同じ順序で列挙されているインデックスが必要です。InnoDB は、外部キーまたは参照キーに対して自動的にインデックスを作成しません。したがって、ユーザが明示的にインデックスを作成する必要があります。外部キーのチェックを高速化し、テーブルスキャンを不要にするには、インデックスが必要です。
外部キーと参照キーの対応するカラムは、型を変換しなくても比較できるように、InnoDB 内部で同じデータ型にする必要があります。
整数型については、サイズと符号の有無が同じでなければなりません。
文字列型の長さは同じでなくてもかまいません。
SET NULL アクションを指定する場合は、子テーブル内のカラムを NOT NULL と宣言していないことを確認してください。
MySQL が CREATE TABLE ステートメントでエラー番号 1005 を返し、エラーメッセージ文字列に errno 150 が示されている場合は、外部キー制約が正しく作成されなかったためにテーブルの作成が失敗しています。
同様に、ALTER TABLE が失敗して errno 150 が示された場合は、変更されたテーブルに対して外部キー定義が誤って作成されています。バージョン 4.0.13 より、SHOW INNODB STATUS を使用して、サーバで最後に発生した InnoDB 外部キーエラーの詳細な説明を参照できるようになりました。
バージョン 3.23.50 より、InnoDB は NULL カラムを含んでいる外部キーまたは参照キー値で外部キー制約をチェックしなくなりました。
標準 SQL からの逸脱: 親テーブルに同じ参照キー値を持つ複数のレコードがある場合、InnoDB の外部キーチェックでは、同じキー値を持つ親レコードが他に存在しないものとして処理が行われます。たとえば、RESTRICT 型制約を定義し、かつ複数の親レコードを持つ子レコードが存在する場合、InnoDB はこれらの親レコードの削除を禁止します。
バージョン 3.23.50 より、ON DELETE CASCADE 節または ON DELETE SET NULL 節を外部キー制約に付けることもできるようになりました。対応する ON UPDATE オプションは、4.0.8 より利用可能です。ON DELETE CASCADE が指定されている場合に親テーブル内のレコードが削除されると、InnoDB は子テーブル内で親レコード内の参照キー値と等しい外部キー値を持つすべてのレコードを自動的に削除します。ON DELETE SET NULL が指定されている場合は、子レコードが自動的に更新されて、外部キー内のカラムが SQL の NULL 値に設定されます。
標準 SQL からの逸脱: ON UPDATE CASCADE または ON UPDATE SET NULL は、カスケード中にすでに更新したテーブルを繰り返して更新する場合に、RESTRICT のように動作します。これは、カスケードされた更新から生じる無限ループを防ぐためです。一方、自己参照型の ON DELETE SET NULL が 4.0.13 から動作するようになりました。
自己参照型の ON DELETE CASCADE は、以前から動作していました。
次に例を示します。
CREATE TABLE parent(id INT NOT NULL, PRIMARY KEY (id)) TYPE=INNODB;
CREATE TABLE child(id INT, parent_id INT, INDEX par_ind (parent_id),
FOREIGN KEY (parent_id) REFERENCES parent(id)
ON DELETE SET NULL
) TYPE=INNODB;
次に示すのは複雑な例です。
CREATE TABLE product (category INT NOT NULL, id INT NOT NULL,
price DECIMAL,
PRIMARY KEY(category, id)) TYPE=INNODB;
CREATE TABLE customer (id INT NOT NULL,
PRIMARY KEY (id)) TYPE=INNODB;
CREATE TABLE product_order (no INT NOT NULL AUTO_INCREMENT,
product_category INT NOT NULL,
product_id INT NOT NULL,
customer_id INT NOT NULL,
PRIMARY KEY(no),
INDEX (product_category, product_id),
FOREIGN KEY (product_category, product_id)
REFERENCES product(category, id)
ON UPDATE CASCADE ON DELETE RESTRICT,
INDEX (customer_id),
FOREIGN KEY (customer_id)
REFERENCES customer(id)) TYPE=INNODB;
バージョン 3.23.50 より、InnoDB では次のステートメントによって新しい外部キー制約をテーブルに追加できるようになりました。
ALTER TABLE yourtablename ADD [CONSTRAINT [symbol]] FOREIGN KEY (...) REFERENCES anothertablename(...) [on_delete_and_on_update_actions]
ただし、必要なインデックスを先に作成することを忘れないでください。
バージョン 4.0.13 より、InnoDB が次のステートメントをサポートするようになりました。
ALTER TABLE yourtablename DROP FOREIGN KEY internally_generated_foreign_key_id
外部キーを破棄する場合は、SHOW CREATE TABLE を使って、内部で生成された外部キー ID を確認する必要があります。
InnoDB が 3.23.50 より前のバージョンである場合は、外部キー制約を持つテーブルまたは外部キー制約で参照されるテーブルに関連して ALTER TABLE または CREATE INDEX を使用しないでください。ALTER TABLE を実行すると、テーブルに定義されているすべての外部キー制約が削除されます。ALTER TABLE は、参照テーブルにも使用しないでください。ただし、スキーマを変更する場合は DROP TABLE および CREATE TABLE を使用します。MySQL は、ALTER TABLE を実行するときに内部的に RENAME TABLE を使用する場合があります。この場合、テーブルを参照する外部キー制約で混乱が生じます。
CREATE INDEX ステートメントは、MySQL では ALTER TABLE として処理されるため、このステートメントにもこれらの制約が適用されます。
InnoDB は、外部キーチェックを実行する際に、参照する子レコードまたは親レコードに対して共有行レベルロックを設定します。 InnoDB は、外部キー制約を即座にチェックします。チェックがトランザクションコミットまで延期されることはありません。
外部キー制約を、たとえば LOAD DATA 処理の間だけ無視する場合は、SET FOREIGN_KEY_CHECKS=0 を実行します。
InnoDB では、外部キー制約によって参照されているテーブルでも破棄できます。この場合、その制約が壊れることになります。テーブルを破棄すると、その作成ステートメントで定義された制約も破棄されます。
破棄されたテーブルを再作成する場合は、そのテーブルを参照する外部キー制約に沿った定義をテーブル内に設定する必要があります。すでに説明したように、このテーブルには、正しい名前と型を持つカラム、および参照キー上のインデックスが必要です。
これらの条件が満たされていないと、MySQL からエラー番号 1005 が返され、エラーメッセージ文字列に errno 150 が示されます。
バージョン 3.23.50 より、次のステートメントを呼び出すと、InnoDB からテーブルの外部キー定義が返されるようになりました。
SHOW CREATE TABLE yourtablename
また、`mysqldump' によってテーブルの正しい定義と共に外部キーがダンプファイルに出力されます。
また、次のステートメントでテーブル T の外部キー制約も列挙できます。
SHOW TABLE STATUS FROM yourdatabasename LIKE 'T'
外部キー制約は、出力のテーブルコメントに列挙されます。
重要: InnoDB-4.1.1 にアップグレードした後に、ダウングレードすることはできません。それは、InnoDB の以前のバージョンが複数のテーブルスペースを認識しないためです。
MySQL-4.1.1 より、各 InnoDB テーブルとそのインデックスを、そのテーブル独自のファイルに格納できるようになりました。各テーブルが独自のテーブルスペースに格納されることから、この機能は複数テーブルスペースと呼ばれます。
この機能を有効にするには、`my.cnf' の [mysqld] セクションに次の行を追加します。
innodb_file_per_table
これによって InnoDB は、各テーブルが属しているデータベースディレクトリ内にあるテーブル固有の `tablename.ibd' ファイルに、それぞれのテーブルを格納します。これは MyISAM の動作と似ていますが、MyISAM ではテーブルがデータファイル `tablename.MYD' とインデックスファイル `tablename.MYI' に分けられます。InnoDB の場合は、データとインデックスの両方が `.ibd' ファイルに格納されます。
`my.cnf' から行 innodb_file_per_table を削除すると、InnoDB によって再び `ibdata' ファイル内にテーブルが作成されます。4.1.1 以降のバージョンへアップグレードする前に `ibdata' ファイルに格納されていた古いテーブルはそのまま残され、`.ibd' ファイルに変換されることはありません。
InnoDB にはシステムテーブルスペースが必要で、`.ibd' ファイルだけでは不十分です。 システムテーブルスペースは、これまで使われていた `ibdata' ファイルで構成されます。InnoDB は、内部データディクショナリと UNDO ログをシステムテーブルスペースに配置します。
MyISAM テーブルとは異なり、.ibd ファイルは自由に移動できません。これは、テーブル定義が InnoDB システムテーブルスペースに格納されているためと、InnoDB でトランザクション ID とログシーケンス番号の整合性を維持する必要があるためです。
従来の RENAME コマンドを使って、`.ibd' ファイルと関連テーブルを、あるデータベースから同じ MySQL/InnoDB インストール内にある別のデータベースに移すことができます。
RENAME TABLE olddatabasename.tablename TO newdatabasename.tablename;
同じ MySQL/InnoDB インストールから取得した `.ibd' ファイルのクリーンバックアップがある場合は、次のコマンドでそのバックアップを InnoDB データベースにリストアできます。
ALTER TABLE tablename DISCARD TABLESPACE; /* CAUTION: 現在の .ibd ファイルを削除します! */ <バックアップした .ibd ファイルを適切な場所に置きます> ALTER TABLE tablename IMPORT TABLESPACE;
ここでのクリーンとは、次のことを意味しています。
このような `.ibd' ファイルのクリーンバックアップは、次の方法で作成できます。
SHOW INNODB STATUS 文でデータベースにアクティブなトランザクションがないことが示され、InnoDB のメインスレッドが Waiting for server activity になるのを待つ。これで、`.ibd' ファイルのコピーを作成できる。
クリーンな `.ibd' ファイルを作成するもう 1 つの方法(有償)があります。
TODO には、クリーンな `.ibd' ファイルも別の MySQL/InnoDB に移動できるようにすることが挙げられています。そのためには、`.ibd' ファイル内でトランザクション ID とログシーケンス番号をリセットすることが必要になります。
バージョン 3.23.50 および 4.0.2 より、InnoDB の最後のデータファイルに autoextend を指定できるようになりました。あるいは、追加のデータファイルを指定してテーブルスペースを拡大することができます。そのためには、MySQL サーバをシャットダウンし、`my.cnf' ファイルで innodb_data_file_path の末尾に新しいデータファイルを追加し、MySQL サーバを再起動します。
現時点では、InnoDB からデータファイルを削除することはできません。データベースのサイズを小さくするには、`mysqldump' ですべてのテーブルをダンプし、新しいデータベースを作成し、そのデータベースにテーブルをインポートする必要があります。
InnoDB ログファイルの数またはサイズを変更する場合は、MySQL をシャットダウンし、エラーなくシャットダウンすることを確認する必要があります。 その後に、シャットダウンで問題が発生した場合に備えて、古いログファイルを安全な場所にコピーします。これらはデータベースをリカバリする際に必要となります。古いログファイルをログファイルディレクトリから削除し、`my.cnf' を編集してから MySQL を再び起動します。起動時に、InnoDB から新しいログファイルを作成していることが通知されます。
安全なデータベース管理の秘訣は、定期的にバックアップを取ることです。
InnoDB ホットバックアップは、稼働中の InnoDB データベースをバックアップするためのオンラインバックアップツールです。InnoDB ホットバックアップでは、データベースをシャットダウンする必要がなく、ロックが設定されたり、通常のデータベース処理が妨害されたりすることもありません。InnoDB ホットバックアップは、追加で導入する有償のツールで、標準の MySQL ディストリビューションには含まれていません。詳細情報とスクリーンショットについては、InnoDB ホットバックアップのホームページ(http://www.innodb.com/manual.php)を参照してください。
MySQL サーバをシャットダウンできる場合は、データベースの 'バイナリ' バックアップを取るために次の作業を行う必要があります。
上記のバイナリバックアップに加えて、`mysqldump' を使って定期的にテーブルのダンプを取るようにしてください。その理由は、バイナリファイルが気付かないうちに壊れる可能性があるためです。ダンプされたテーブルはテキストファイルに格納されます。これらは人間による解読が可能で、データベースバイナリファイルよりもはるかに単純です。ダンプされたファイルではテーブルの破損を容易に確認できます。また、このファイルの形式は単純であるため、重大なテーブルの破損が起こる確率も低くなります。
データベースのバイナリバックアップと同時にダンプを取ることをお勧めします。すべてのテーブルのスナップショットを矛盾のない状態でダンプするには、すべてのクライアントをデータベースからシャットアウトする必要があります。その後にバイナリバックアップを取れば、データベースの矛盾のないスナップショットを 2 つの形式で保持することになります。
上記のバイナリバックアップから InnoDB データベースをリカバリできるようにするには、MySQL の一般的なログをオンにして MySQL データベースを実行する必要があります。ここでの一般的なログとは、InnoDB ログとは関係のない MySQL サーバのログメカニズムを意味します。
MySQL サーバプロセスのクラッシュからリカバリする場合に必要な作業は、そのプロセスの再起動だけです。InnoDB は自動的にログをチェックし、現時点までのデータベースのロールフォワードを実行します。InnoDB は、クラッシュ時にコミットされていなかったトランザクションを自動的にロールバックします。リカバリの間、InnoDB は次のようなメッセージを出力します。
~/mysqlm/sql > mysqld InnoDB: Database was not shut down normally. InnoDB: Starting recovery from log files... InnoDB: Starting log scan based on checkpoint at InnoDB: log sequence number 0 13674004 InnoDB: Doing recovery: scanned up to log sequence number 0 13739520 InnoDB: Doing recovery: scanned up to log sequence number 0 13805056 InnoDB: Doing recovery: scanned up to log sequence number 0 13870592 InnoDB: Doing recovery: scanned up to log sequence number 0 13936128 ... InnoDB: Doing recovery: scanned up to log sequence number 0 20555264 InnoDB: Doing recovery: scanned up to log sequence number 0 20620800 InnoDB: Doing recovery: scanned up to log sequence number 0 20664692 InnoDB: 1 uncommitted transaction(s) which must be rolled back InnoDB: Starting rollback of uncommitted transactions InnoDB: Rolling back trx no 16745 InnoDB: Rolling back of trx no 16745 completed InnoDB: Rollback of uncommitted transactions completed InnoDB: Starting an apply batch of log records to the database... InnoDB: Apply batch completed InnoDB: Started mysqld: ready for connections
データベースの破損またはディスク障害が発生した場合は、バックアップからリカバリを実行する必要があります。データベースが壊れた場合は、まず壊れていないバックアップを探します。MySQL のマニュアルに従って、MySQL の一般ログファイルからのリカバリを実行します。
データベースページが壊れた場合は、SELECT INTO OUTFILE を使ってデータベースからテーブルをダンプできます。通常は、ほとんどのデータが損傷を受けず正常な状態です。ところが、破損が原因で SELECT * FROM table や InnoDB のバックグラウンド処理がクラッシュまたはアサートしたり、InnoDB のロールフォワードリカバリさえもクラッシュすることがあります。InnoDB のバージョン 3.23.44 より、InnoDB を強制的に起動することのできるオプションが `my.cnf' に追加されました。また、テーブルをダンプできるように、バックグラウンド処理が実行されないようにすることも可能となりました。たとえば、`my.cnf' で
innodb_force_recovery = 4
を設定することができます。
innodb_force_recovery の選択肢は下に挙げています。
データベースには、これらのオプションを別の用途で使用しないでください。
InnoDB では、安全対策として、このオプションが 0 より大きいときはユーザが INSERT、UPDATE、または DELETE を実行できないようになっています。
バージョン 3.23.53 および 4.0.4 より、強制リカバリが使用される場合でも、テーブルの DROP または CREATE を実行することができます。ロールバックでクラッシュを引き起こしているテーブルが特定できれば、そのテーブルを破棄できます。
また、問題のある大量インポートまたは ALTER TABLE が原因で暴走したロールバックも、この方法で停止できます。
mysqld プロセスを強制終了し、`my.cnf' のオプション innodb_force_recovery=3 を使用することで、ロールバックせずにデータベースを正常な状態に戻すことができます。
その後に、ロールバックが暴走する原因となったテーブルを DROP で破棄します。
下に列挙されている各オプションでは、その番号が大きいものがより小さい番号の対策をすべて盛り込んでいます。悪くてもオプション 4 でテーブルをダンプできれば、個々のページが破損しても一部のデータが失われるだけなので比較的安全です。オプション 6 にはより劇的な影響力があります。データベースページが古い状態のまま残るため、B ツリーやその他のデータベース構造へさらに破損が及ぶ可能性があるからです。
SELECT * FROM table で壊れたインデックスレコードやページを飛び越すようにすれば、テーブルをダンプする際に役立つ。
InnoDB には、ファジーチェックポイントと呼ばれるチェックポイントメカニズムが実装されています。InnoDB は、変更されたデータベースページを小規模なバッチ単位でバッファプールからフラッシュします。バッファプールを 1 回のバッチでフラッシュする必要はありません。実際にこれを行うと、ユーザの SQL ステートメントの処理が一時的に停止します。
クラッシュリカバリの際に、InnoDB はログファイルに書き込まれたチェックポイントラベルを検索します。InnoDB は、このラベルより前に実行されたデータベースへの変更が、データベースのディスクイメージにすでに反映されていることを認識しています。 次に InnoDB は、ログファイルでこのチェックポイント以降をスキャンし、ログに記録された変更をデータベースに適用します。
InnoDB はログファイルへの書き込みを循環的に行います。 InnoDB によるリカバリが必要となった場合は、バッファプール内のデータベースページとディスク上のイメージの不一致を引き起こしている全てのコミット済みの変更を、ログファイルから取得できなければなりません。つまり、InnoDB は、ログファイルを循環的に再使用する際に、再使用しようとするログファイルに記録された変更が、ディスク上のデータベースページのイメージにすでに反映されていることを確認する必要があります。そのために InnoDB はチェックポイントを作成する必要があり、それには変更されたデータベースページをディスクにフラッシュする処理が伴います。
これらのことから、ログファイルを大きくしておけば、チェックポイントを実行する際のディスク I/O を削減できると言えます。ログファイルの合計サイズを、バッファプールのサイズ以上に設定することは理に適っています。 ログファイルを大きくした場合の難点として、データベースに適用するログの量が増えるために、クラッシュリカバリに時間がかかるおそれがあります。
Windows では、InnoDB がデータベース名とテーブル名を内部的に小文字で格納します。バイナリ形式のデータベースを Unix から Windows に、またはその逆に移動するには、すべてのテーブル名とデータベース名を小文字にする必要があります。Unix でこれを簡単に行うには、`my.cnf' の [mysqld] セクションに次の行を追加します。
lower_case_table_names=1
これは、テーブルの作成を開始する前に行います。Windows では、デフォルトで 1 に設定されます。
InnoDB のデータファイルとログファイルは、すべてのプラットフォームでバイナリ互換です。ただし、それらのマシンで浮動小数点数の形式が同じであることが必要です。
InnoDB データベースは、すべての関連ファイルをコピーするだけで移動できます。関連ファイルについては、データベースのバックアップに関する前のセクションを参照してください。マシン間で浮動小数点数の形式が異なっていても、テーブル内で FLOAT または DOUBLE データ型を使用していなければ手順は同じです。つまり、関連ファイルをコピーするだけで済みます。浮動小数点数の形式が異なっている場合に、テーブルで浮動小数点データが使用されているときは、`mysqldump' および `mysqlimport' を使用してそれらのテーブルを移動する必要があります。
パフォーマンス上のヒントとして、データをデータベースにインポートするときは、大量のインポートトランザクションによって生成される大きなロールバックセグメントに対応できるだけの十分なテーブルスペースがあると想定して、オートコミットモードをオフにします。 コミットは、テーブル全体またはテーブルのセグメントをインポートした後に実行します。
InnoDB トランザクションモデルの目標は、マルチバージョニングのデータベースの優れた特性を、従来の 2 相ロックと組み合わせることでした。
InnoDB は、行レベルでロックを行い、デフォルトではクエリを Oracle 式の非ロックの一貫性読み取りとして実行します。
InnoDB のロックテーブルはスペース効率の高い方法で格納されるため、ロックエスカレーションは不要です。一般には、複数のユーザがデータベースのあらゆるレコードまたはレコードのランダムなサブセットをロックすることができ、InnoDB でメモリ不足が発生することもありません。
InnoDB では、すべてのユーザ活動がトランザクションの内部で発生します。MySQL でオートコミットモードが使用されている場合は、各 SQL ステートメントが 1 つのトランザクションとなります。
MySQL は常にオートコミットモードをオンにして新たな接続を開始します。
SET AUTOCOMMIT = 0 でオートコミットモードがオフになると、ユーザが常にトランザクションを開いていると見なされます。このユーザが SQL の COMMIT または ROLLBACK ステートメントを実行すると、現在のトランザクションが終了し、新しいトランザクションが開始されます。どちらのステートメントも、現在のトランザクションで設定されたすべての InnoDB ロックを解除します。COMMIT を実行すると、現在のトランザクションで加えられた変更が確定し、他のユーザが認識できる状態になります。一方、ROLLBACK ステートメントを実行すると、現在のトランザクションによって加えられたすべての変更が取り消されます。
接続に AUTOCOMMIT = 1 が設定されている場合でも、ユーザはトランザクションを実行できます。その場合、START TRANSACTION または BEGIN でトランザクションを開始し、COMMIT または ROLLBACK でトランザクションを終了します。
SET ... TRANSACTION ISOLATION LEVEL ...
SQL-92 のトランザクション分離レベルに関する InnoDB のデフォルトは REPEATABLE READ です。
バージョン 4.0.5 より、InnoDB は SQL-92 標準で記述されている 4 種類のトランザクション分離レベルをすべて提供するようになりました。
`my.cnf' の [mysqld] セクションで、すべての接続に対するデフォルトの分離レベルを設定できます。
transaction-isolation = {READ-UNCOMMITTED | READ-COMMITTED
| REPEATABLE-READ | SERIALIZABLE}
ユーザは、単一セッションの分離レベルまたは新たに接続するすべてのコネクションの分離レベルを、SET TRANSACTION ステートメントで変更できます。その構文は次のとおりです。
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL
{READ UNCOMMITTED | READ COMMITTED
| REPEATABLE READ | SERIALIZABLE}
SQL 構文ではレベル名にハイフンを付けないことに注意してください。
デフォルトの動作として、次の(まだ開始されていない)トランザクションの分離レベルが設定されます。このステートメントに GLOBAL キーワードを使用すると、それ以降に作成されるすべての新しい接続(既存の接続は対象外)に対してグローバルにデフォルトのトランザクションレベルが設定されます。
これを行うには、SUPER 特権が必要です。SESSION キーワードを使用すると、現在の接続で実行されるすべての新しいトランザクションに対してデフォルトのトランザクションレベルが設定されます。どのクライアントも、自由にセッション分離レベル(トランザクションの途中であっても)または次のトランザクションの分離レベルを変更できます。
3.23.50 より前のバージョンでは、InnoDB テーブルに SET TRANSACTION が作用しませんでした。4.0.5 より前のバージョンでは、REPEATABLE READ と SERIALIZABLE のみが提供されていました。
グローバルおよびセッションのトランザクション分離レベルは、次のステートメントで確認できます。
SELECT @@global.tx_isolation; SELECT @@tx_isolation;
行レベルロックでは、InnoDB がネクストキーロックを使用します。
つまり、インデックスレコードに加えてインデックスレコードの前の ``ギャップ'' もロックすることで、他のユーザがそのインデックスレコードの直前に挿入できないようにします。ここでいうネクストキーロックとは、インデックスレコードとその前のギャップをロックするロックのことです。
ギャップロックとは、あるインデックスレコードの前のギャップのみをロックするロックです。
InnoDB における各分離レベルの詳細は次のとおりです。
READ UNCOMMITTED ``ダーティリード'' とも呼ばれる。ロックを取得しない SELECT ステートメントが実行されるため、前のバージョンのレコードが参照されない。
したがって、この分離レベルでは '一貫した' 読み取りにならない。
それ以外の場合、このレベルは READ COMMITTED と同じように機能する。
READ COMMITTED Oracle にやや近い分離レベル。
全ての SELECT ... FOR UPDATE および SELECT ... LOCK IN SHARE MODE ステートメントは、インデックスレコードのみをロックし、その前のギャップはロックしない。したがって、ロックされたレコードの次に新しいレコードを自由に挿入できる。
一意の検索条件でユニークインデックスを使用する UPDATE および DELETE は、検出したインデックスレコードのみをロックし、その前のギャップはロックしない。
ただし、範囲指定のある UPDATE および DELETE では、InnoDB が引き続きネクストキーロックまたはギャップロックを設定し、その範囲でカバーされるギャップへ他のユーザが挿入できないようにする必要がある。これは、MySQL のレプリケーションおよびリカバリが機能するように、``ファントム(幻像)行'' をブロックしなければならないからである。
一貫性読み取りは、Oracle と同じように動作する。つまり、一貫性読み取りでは、同じトランザクション内であっても、最新のスナップショットが独自に設定され、読み取られる。
REPEATABLE READ InnoDB のデフォルトの分離レベル。
SELECT ... FOR UPDATE, SELECT ... LOCK IN SHARE MODE, 一意の検索条件でユニークインデックスを使用する UPDATE および DELETE は、検出したインデックスレコードのみをロックし、その前のギャップはロックしない。
それ以外の場合は、これらの操作でネクストキーロック(次のキーでスキャンされるインデックスの範囲をロック)またはギャップロックが使用され、他のユーザによる新たな挿入がブロックされる。
読み取り一貫性には、前の分離レベルとの間に重要な違いがある。このレベルでは、同じトランザクション内のすべての一貫した読み取りが、最初の読み取りで確立されたスナップショットを読み取る。この規則によって、同じトランザクション内で複数の単純な SELECT ステートメントを発行した場合に、それらの SELECT ステートメントも互いに一貫した状態になる。
SERIALIZABLE 前の分離レベルに似ているが、単純な SELECT ステートメントがすべて暗黙的に SELECT ... LOCK IN SHARE MODE に変換される。
読み取り一貫性とは、InnoDB がそのマルチバージョニング機能を使用して、ある時点でのデータベースのスナップショットをクエリに提示することを意味します。 クエリには、その時点より前にコミットされたトランザクションによる変更のみが示され、その時点より後のトランザクションまたはコミットされていないトランザクションによる変更は示されません。例外として、クエリを発行したトランザクション自体による変更はクエリに示されます。
デフォルトの REPEATABLE READ 分離レベルで実行している場合は、最初の読み取り内容がスナップショトとして保存され、同じトランザクション内のすべての読み取りで、そのスナップショットから内容が読み取られます。より新しい状態に更新するには、現在のトランザクションをコミットし、その後に新たなクエリを発行します。
読み取り一貫性はデフォルトのモードです。このモードでは、InnoDB が SELECT ステートメントを READ COMMITTED および REPEATABLE READ 分離レベルで処理します。読み取り一貫性では、アクセスするテーブルに一切ロックが設定されません。このため、読み取り一貫性がテーブルで実行されているときにも、他のユーザはこれらのテーブルを自由に変更できます。
SELECT ... FOR UPDATE および SELECT ... LOCK IN SHARE MODE
読み取り一貫性は、状況によっては不便な場合があります。
テーブル CHILD に新しいレコードを追加するために、テーブル PARENT 内にこの子レコードの親がすでに存在することを確認するとします。
仮に、読み取り一貫性でテーブル PARENT を読み取り、このテーブルで子レコードの親の存在を確認したとします。これでテーブル CHILD に子レコードを確実に追加できるでしょうか。できません。この処理の間に他のユーザがテーブル PARENT から親レコードを削除しても気付かないからです。
これに対処するには、共有ロックモード LOCK IN SHARE MODE で SELECT 文を実行します。
SELECT * FROM PARENT WHERE NAME = 'Jones' LOCK IN SHARE MODE;
共有モードで読み取りを実行すると、入手可能な最新のデータが読み取られ、読み取ったレコードに共有モードロックが設定されます。
その最新データが、別のユーザのまだコミットされていないトランザクションに属している場合は、そのトランザクションがコミットされるまで待機します。
共有モードロックによって、読み取ったレコードは他のユーザから更新または削除されなくなります。上記のクエリから親である 'Jones' が返されたことを確認したうえで、テーブル CHILD に 'Jones' の子レコードを確実に追加し、トランザクションをコミットできます。
この例は、アプリケーションコードで参照整合性を実装する方法を示しています。
別の例を見てみましょう。テーブル CHILD_CODES に整数のカウンタフィールドがあります。このテーブルは、テーブル CHILD に追加する各子に一意の識別子を割り当てるために使用します。
このカウンタの現在の値を読み取る場合に、読み取り一貫性または共有モード読み取りが適していないのは明らかです。これは、データベースの 2 人のユーザに同じカウンタ値が返されるために、テーブルに同じ識別子を持つ 2 つの子を追加することになり、重複キーエラーが発生するためです。
この場合、カウンタの読み取りとインクリメントを実装する方法が 2 つあります。(1)カウンタを先に 1 増加してから読み取る方法と(2)カウンタを先にロックモード FOR UPDATE で読み取ってからインクリメントする方法です。
SELECT COUNTER_FIELD FROM CHILD_CODES FOR UPDATE; UPDATE CHILD_CODES SET COUNTER_FIELD = COUNTER_FIELD + 1;
SELECT ... FOR UPDATE では、入手可能な最新のデータが読み取られ、読み取った各レコードに排他ロックが設定されます。
したがって、検索された SQL UPDATE がレコードに設定するロックと同じロックが設定されます。
InnoDB は、行レベルのロックでネクストキーロックと呼ばれるアルゴリズムを使用します。 InnoDB が行レベルロックを行うのは、テーブルのインデックスを検索またはスキャンする際に、検出したインデックスレコードに共有ロックまたは排他ロックを設定するためです。このため、行レベルロックは正確にはインデックスレコードロックと呼ばれます。
InnoDB がインデックスレコードに設定するロックは、そのインデックスレコードの前の 'ギャップ' にも影響します。あるユーザがインデックスのレコード R に共有ロックまたは排他ロックを設定すると、他のユーザはインデックス順で R の直前に新しいインデックスレコードを挿入できなくなります。
このようなギャップのロックには、いわゆるファントムの問題を回避する目的があります。たとえば、テーブル CHILD から 100 より大きい ID を持つすべてのレコードを読み取ってロックし、選択したレコードに対してフィールドを更新するとします。
SELECT * FROM CHILD WHERE ID > 100 FOR UPDATE;
テーブル CHILD のカラム ID にインデックスがあるとします。クエリでは、ID が 100 より大きい最初のレコードから順にそのインデックスがスキャンされます。
ここで、インデックスレコードに設定されたロックがギャップで行われる挿入をロックアウトしないと、テーブルに新しいレコードが挿入される可能性があります。その場合に、トランザクションで次のステートメントをもう一度実行してみます。
SELECT * FROM CHILD WHERE ID > 100 FOR UPDATE;
すると、クエリから返される結果セットに新しいレコードが含まれることになります。 これは、トランザクションの分離の原則に反します。 つまり、トランザクションの実行中は、読み取ったデータが変化しないことが必要です。一組のレコードを 1 つのデータ項目と見なすと、新たな 'ファントムの' レコードによって、この分離の原則が破られることになります。
InnoDB では、インデックスをスキャンする際に、インデックス内の最後のレコードの後のギャップもロックできます。前の例と同様に、InnoDB が設定したロックによって、テーブル内の ID が 100 より大きい箇所への挿入が防止されます。
ネクストキーロックを使用して、アプリケーションに一意性のチェックを実装できます。共有モードでデータを読み取り、挿入しようとするレコードに重複が見られなければ、レコードを確実に挿入できます。また、読み取り中は対象となるレコードの後続のレコードにネクストキーロックが設定されて、第三者による重複レコードの挿入を防ぎます。このように、ネクストキーロックによって、テーブル内に存在しないものを 'ロック' することができます。
InnoDB で各種 SQL ステートメントによって設定されるロックSELECT ... FROM ...: 読み取り一貫性。データベースのスナップショットを読み取り、ロックを設定しない。
SELECT ... FROM ... LOCK IN SHARE MODE: 読み取りで検出されたすべてのインデックスレコードに共有ネクストキーロックを設定する。
SELECT ... FROM ... FOR UPDATE: 読み取りで検出されたすべてのインデックスレコードに排他ネクストキーロックを設定する。
INSERT INTO ... VALUES (...): 挿入されたレコードに排他ロックを設定する。このロックはネクストキーロックではないため、挿入されたレコードの前のギャップに他のユーザがレコードを挿入する可能性がある。重複キーエラーが発生した場合は、重複するインデックスレコードに共有ロックを設定する。
INSERT INTO T SELECT ... FROM S WHERE ...: T に挿入された各レコードに排他(非ネクストキー)ロックを設定する。読み取り一貫性として S を検索するが、MySQL ログがオンになっている場合は、S に共有ネクストキーロックを設定する。後者の場合に InnoDB がロックを設定する理由は、バックアップからのロールフォワードリカバリで、すべての SQL ステートメントを元の操作とまったく同じように実行する必要があるためである。
CREATE TABLE ... SELECT ...: 前の項目と同様に、SELECT を一貫性のある読み取りとして、または共有ロックを設定して実行する。
REPLACE は、ユニークキーで衝突がなければ、挿入と同じように実行される。衝突がある場合は、更新対象のレコードに排他ネクストキーロックが設定される。
UPDATE ... SET ... WHERE ...: 検索で検出されたすべてのレコードに排他ネクストキーロックを設定する。
DELETE FROM ... WHERE ...: 検索で検出されたすべてのレコードに排他ネクストキーロックを設定する。
FOREIGN KEY 制約が定義されている場合は、制約条件のチェックを必要とするすべての挿入、更新、削除によって、制約チェックのために参照するレコードに共有行レベルのロックが設定される。また、制約が失敗した場合も、InnoDB によってこれらのロックが設定される。
LOCK TABLES ... : テーブルロックを設定する。これらのロックは、実装時に MySQL のコードレイヤによって設定される。InnoDB の自動デッドロック検出では、このようなテーブルロックが関係するデッドロックを検出できない。
詳細については次のセクションを参照。
また、MySQL は行レベルのロックを認識しないため、別のユーザが行レベルロックを設定しているテーブル上でテーブルロックを取得することができる。ただし、それによってトランザクションの完全性が損なわれるおそれがある。
See section 7.5.15 InnoDB テーブルの制限事項。
InnoDB は、トランザクションのデッドロックを自動的に検出し、そのトランザクションをロールバックしてデッドロックを回避します。バージョン 4.0.5 より、InnoDB は小さいほうのトランザクションを選択してロールバックするようになります。トランザクションのサイズは、挿入、更新、または削除したレコードの数によって決定されます。 4.0.5 より前のバージョンの InnoDB では、デッドロックを引き起こすロックを要求したトランザクションを、常にロールバックしていました。
InnoDB は、MySQL の LOCK TABLES ステートメントが設定したロックが関係するデッドロック、または InnoDB 以外のストレージエンジンで設定されたロックが関係するデッドロックを検出できません。これらの状況は、`my.cnf' で設定する innodb_lock_wait_timeout を使って解決する必要があります。
InnoDB がトランザクションの完全なロールバックを実行すると、そのトランザクションのすべてのロックが解除されます。ところが、エラーのために単一の SQL ステートメントのみがロールバックされると、SQL が設定したロックの一部が保持される場合があります。これは、InnoDB が使用する行ロックの格納形式では、ロックを設定した SQL ステートメントを後から特定できないためです。
InnoDB での読み取り一貫性の例
デフォルトの REPEATABLE READ 分離レベルで実行しているとします。
一貫した読み取り(このレベルでは、通常の SELECT ステートメントは読み取り一貫性がある) では、クエリがデータベースを参照するときの基準となるタイムポイントを、InnoDB はトランザクションに割り当てます。こうして、タイムポイントが割り当てられた後に(セッションごとのスナップショットを取った後に)、他のトランザクションがレコードを削除してコミットしたとしても、一度読み取った内容は変わりません(トランザクション中の読み取り内容は同じです)。挿入と更新も同様です。
割り当てられたタイムポイントを先に進めるには、トランザクションをコミットし、新たな SELECT を実行します。
これは、マルチバージョン並行処理制御(multi-versioned concurrency control)と呼ばれます。
ユーザ A ユーザ B
SET AUTOCOMMIT=0; SET AUTOCOMMIT=0;
時間経過
| SELECT * FROM t;
| empty set
| INSERT INTO t VALUES (1, 2);
|
v SELECT * FROM t;
empty set
COMMIT;
SELECT * FROM t;
empty set;
COMMIT;
SELECT * FROM t;
---------------------
| 1 | 2 |
---------------------
このように、ユーザ B が挿入したレコードをユーザ A が参照できるのは、B が挿入をコミットした後で、A がコミットした後になります。また、A が自分のトランザクションをコミットしたことで、時点が B のコミットより先に進みます。
データベースの ``最新の'' 状態を参照するには、共有ロックを利用した読み取りを使用する必要があります。
SELECT * FROM t LOCK IN SHARE MODE;
デッドロックはトランザクションデータベースにおける従来からの問題ですが、一部のトランザクションを実行できなくなるほど頻繁に発生するのでなければ危険ではありません。通常は、アプリケーションを作成する際に、デッドロックのためにロールバックされたトランザクションを再発行できるようにしておく必要があります。
InnoDB は、自動で行レベルロックを使用します。単一のレコードを挿入または削除するだけのトランザクションでもデッドロックが発生します。それは、これらの操作が実際には原子性を持つものではなく、挿入/削除するレコードのインデックスレコード(おそらくは複数)に自動的にロックを設定するためです。
デッドロックに対処し、デッドロックの発生頻度を減らすための方法を次に示します。
SHOW INNODB STATUS を使用して最後に発生したデッドロックの原因を特定する。これは、デッドロックを回避するようにアプリケーションを調整する際に役立つ。
SELECT ... FOR UPDATE または ... LOCK IN SHARE MODE を使用している場合は、より低い分離レベル READ COMMITTED を使用してみる。
EXPLAIN SELECT を使用して、MySQL がクエリに対して適切なインデックスを選択することを確認する。
SELECT で古いスナップショットからデータが返されてもかまわない場合は、このステートメントに FOR UPDATE 節や LOCK IN SHARE MODE 節を追加しないようにする。この場合は、READ COMMITTED 分離レベルを使用するとよい。この分離レベルでは、同じトランザクション内の一貫した読み取りそれぞれが独自の新しいスナップショットを読み取るからである。
LOCK TABLES t1 WRITE, t2 READ, ... ; [do something with tables t1 and t2 here]; UNLOCK TABLES を使ってトランザクションを直列化する。
テーブルレベルのロックによって、トランザクションが整然と待ち行列に並ぶようになり、デッドロックが回避される。LOCK TABLES は、BEGIN コマンドと同様にトランザクションを暗黙的に開始し、UNLOCK TABLES は、COMMIT でトランザクションを暗黙的に終了することに注意する。
InnoDB は、データベースを変更するトランザクションがコミットされるたびにログをディスクにフラッシュする必要がある。一般にディスクの回転速度は最高でも 1 秒間に 167 回転(10000rpmの規格の物の場合)であるため、ディスクがオペレーティングシステムを欺かない限り、コミットの回数も同じく 1 秒間に 167 回に制限される。
innodb_flush_log_at_trx_commit を 0 に設定できる。InnoDB は、いずれにしても 1 秒間に 1 回ログをフラッシュしようとする。ただし、このフラッシュは保証されない。
InnoDB は、ログファイルの最後まで書き込むと、チェックポイントでバッファプールの変更された内容をディスクに書き込まなければならない。ログファイルが小さいと、不必要に何度もディスクへ書き込むことになる。大きなログファイルの難点は、リカバリに時間がかかることである。
fdatasync やその他の類似する方法を使ってファイルをディスクにフラッシュする際に、かなりの時間がかかる。
InnoDB は、デフォルトで fdatasync 関数を使用する。
データベースへ書き込む際のパフォーマンスが不満であれば、`my.cnf' の innodb_flush_method を O_DSYNC に設定してもかまわない。ただし、ほとんどのシステムでは O_DSYNC の方が処理が遅くなると思われる。
InnoDB にデータをインポートするときは、autocommit がオン(1)になっていないことを確認する。その場合、挿入のたびにディスクへのログのフラッシュが要求される。
SQL の単純なファイルインポート行の前に次の行を追加する。
SET AUTOCOMMIT=0;さらにその後に次の行を追加する。
COMMIT;`mysqldump' のオプション
--opt を使用すると、上記のように SET AUTOCOMMIT=0; ... COMMIT; ラッパで囲まなくても、ダンプファイルを取得して InnoDB テーブルに高速でインポートできる。
InnoDB は、挿入バッファを使って挿入時のディスク I/O を削減するが、対応するロールバックではそのようなメカニズムが使用されない。ディスクバウンドのロールバックには、対応する挿入の 30 倍の時間がかかる。データベースのプロセスを強制終了しても、データベース起動時に再度ロールバックが開始されるので役に立たない。ロールバックの暴走を回避するには、ロールバックが CPU とメモリだけで高速に実行されるようにバッファプールを拡大するか、InnoDB データベース全体を削除するしかない。
DROP TABLE または TRUNCATE(MySQL-4.0 以降)を使用してテーブルを空にする。DELETE FROM yourtable は使用しない。
INSERT を使用して、サーバとクライアント間の通信にかかるオーバヘッドを軽減する。
INSERT INTO yourtable VALUES (1, 2), (5, 5);この方法は、
InnoDB だけでなく他のテーブル型へ挿入する場合にも有効である。
SHOW INNODB STATUS と InnoDB モニタ
バージョン 3.23.42 より、InnoDB の内部状態に関する情報を出力する InnoDB モニタが InnoDB に組み込まれました。
バージョン 3.23.52 および 4.0.3 より、SQL コマンド SHOW INNODB STATUS を使用して、標準 InnoDB モニタの出力を SQL クライアントへ取り込めるようになりました。
このデータは、パフォーマンスチューニングに役立ちます。`mysql' 対話型 SQL クライアントを使用している場合は、ステートメントの末尾にあるセミコロンを \G に置き換えることで、出力が判読しやすくなります。
SHOW INNODB STATUS\G
InnoDB モニタのもう 1 つの使い方として、サーバ `mysqld' の標準出力に InnoDB モニタから継続的にデータを書き込むことができます(注意: MySQL クライアントからは何も出力されません)。
この機能をオンにすると、InnoDB モニタは約 15 秒ごとにデータを出力するようになります。`mysqld' をデーモンとして実行すると、通常はこの出力が MySQL datadir 内の `.err' ログに出力されます。
このデータは、パフォーマンスチューニングに役立ちます。
Windows でこの出力を MS-DOS コマンドプロンプトにリダイレクトするには、MS-DOS コマンドプロンプトから --console オプションを指定して mysqld-max を実行する必要があります。
別途用意されている innodb_lock_monitor では、innodb_monitor と同じ情報に加えて、各トランザクションが設定したロックに関する情報も出力されます。
出力される情報には、以下に関するデータが含まれます。
InnoDB のメインスレッドのパージおよび挿入バッファマージ活動
InnoDB モニタは、次の SQL コマンドで起動できます。
CREATE TABLE innodb_monitor (a INT) TYPE = innodb;
また、次のコマンドで停止できます。
DROP TABLE innodb_monitor;
CREATE TABLE 構文は、MySQL SQL パーサを通して InnoDB エンジンにコマンドを渡す手段に過ぎません。作成されたテーブルは InnoDB モニタとまったく無関係です。モニタの実行中にデータベースをシャットダウンし、その後で再びモニタを起動する場合は、まず作成したテーブルを破棄しないと、新たな CREATE TABLE 発行してモニタを起動することができません。
この構文は、将来のリリースで変更される可能性があります。
次に示すのは、InnoDB モニタの出力サンプルです。
================================ 010809 18:45:06 INNODB MONITOR OUTPUT ================================ -------------------------- LOCKS HELD BY TRANSACTIONS -------------------------- LOCK INFO: Number of locks in the record hash table 1294 LOCKS FOR TRANSACTION ID 0 579342744 TABLE LOCK table test/mytable trx id 0 582333343 lock_mode IX RECORD LOCKS space id 0 page no 12758 n bits 104 table test/mytable index PRIMARY trx id 0 582333343 lock_mode X Record lock, heap no 2 PHYSICAL RECORD: n_fields 74; 1-byte offs FALSE; info bits 0 0: len 4; hex 0001a801; asc ;; 1: len 6; hex 000022b5b39f; asc ";; 2: len 7; hex 000002001e03ec; asc ;; 3: len 4; hex 00000001; ... ----------------------------------------------- CURRENT SEMAPHORES RESERVED AND SEMAPHORE WAITS ----------------------------------------------- SYNC INFO: Sorry, cannot give mutex list info in non-debug version! Sorry, cannot give rw-lock list info in non-debug version! ----------------------------------------------------- SYNC ARRAY INFO: reservation count 6041054, signal count 2913432 4a239430 waited for by thread 49627477 op. S-LOCK file NOT KNOWN line 0 Mut ex 0 sp 5530989 r 62038708 sys 2155035; rws 0 8257574 8025336; rwx 0 1121090 1848344 ----------------------------------------------------- CURRENT PENDING FILE I/O'S -------------------------- Pending normal aio reads: Reserved slot, messages 40157658 4a4a40b8 Reserved slot, messages 40157658 4a477e28 ... Reserved slot, messages 40157658 4a4424a8 Reserved slot, messages 40157658 4a39ea38 Total of 36 reserved aio slots Pending aio writes: Total of 0 reserved aio slots Pending insert buffer aio reads: Total of 0 reserved aio slots Pending log writes or reads: Reserved slot, messages 40158c98 40157f98 Total of 1 reserved aio slots Pending synchronous reads or writes: Total of 0 reserved aio slots ----------- BUFFER POOL ----------- LRU list length 8034 Free list length 0 Flush list length 999 Buffer pool size in pages 8192 Pending reads 39 Pending writes: LRU 0, flush list 0, single page 0 Pages read 31383918, created 51310, written 2985115 ---------------------------- END OF INNODB MONITOR OUTPUT ============================ 010809 18:45:22 InnoDB starts purge 010809 18:45:22 InnoDB purged 0 pages
この出力についていくつかの注釈があります。
LOCKS HELD BY TRANSACTIONS セクションにロック待ちが報告されている場合は、アプリケーションでロックが競合しているおそれがある。この出力から、トランザクションのデッドロックの原因を追跡することもできる。
UNIV_SYNC_DEBUG を定義して InnoDB をコンパイルすると、SYNC INFO セクションに予約済みのセマフォが報告される。
SYNC ARRAY INFO セクションには、セマフォを待っているスレッドと、スレッドがスピンまたは相互排他ロック(mutex) や 読み書きロックでの待機を必要とした回数に関する統計情報が報告される。多数のスレッドがセマフォを待っている場合は、ディスク I/O または InnoDB 内部の競合が原因となっている可能性がある。競合の原因としては、多数のクエリを並行して処理しているか、オペレーティングシステムでのスレッドのスケジューリングに問題があることが考えられる。
CURRENT PENDING FILE I/O'S セクションには、保留中のファイル I/O 要求が列挙される。この数が多い場合は、作業負荷がディスク I/O バウンドであることを示している。
BUFFER POOL セクションには、読み書きされたページに関する統計情報が示される。これらの数値から、クエリが現在実行しているデータファイル I/O の回数を計算できる。
InnoDB はマルチバージョニングのデータベースであるため、テーブルスペース内の古いバージョンのレコードに関する情報を保持しなければなりません。この情報は、ロールバックセグメントに格納されます。 ロールバックセグメントは Oracle のロールバックセグメントのデータ構造に似ています。
InnoDB は内部的に、データベースに格納されている各レコードに 2 つのフィールドを付加します。 6 バイトのフィールドには、レコードを最後に挿入または更新したトランザクションの識別子が記録されます(トランザクション ID)。削除も内部的には更新として扱われ、レコード内の特別なビットに削除済みとしてマークされます。各レコードには、ロールポインタと呼ばれる 7 バイトのフィールドも付加されます。ロールポインタは、ロールバックセグメントに書き込まれた取り消しログレコードを指します。レコードが更新された場合は、更新前のレコードの内容を再構築するために必要な情報が取り消しログレコードに記録されます。
InnoDB は、ロールバックセグメント内の情報を使用して、トランザクションのロールバックで必要な取り消し操作を実行します。また、読み取り一貫性のために、以前のバージョンのレコードを構築する場合にも、この情報が使用されます。
ロールバックセグメント内の UNDO ログは、挿入用の UNDO ログと更新用の UNDO ログに分かれています。挿入用の UNDO ログは、トランザクションロールバックにのみ必要で、トランザクションがコミットされると直ちに破棄されます。更新用の UNDO ログは、読み取り一貫性でも使用されるため、InnoDB がスナップショットを割り当てたトランザクションが存在しなくなるまで破棄できません。このようなトランザクションでは、読み取り一貫性で以前のバージョンのレコードを構築するために、更新用の UNDO ログの情報が必要となる場合があるからです。
トランザクションは定期的にコミットする必要があります。読み取り一貫性のある物のみを発行するトランザクションも同様です。 そうしないと、InnoDB が更新用 UNDO ログからデータを破棄できなくなり、ロールバックセグメントが大きくなって、テーブルスペースがいっぱいになるおそれがあります。
ロールバックセグメントにおける UNDO ログレコードの物理サイズは、通常、対応する挿入レコードまたは更新レコードよりも小さくなります。ロールバックセグメントに必要な領域を、これらのレコード長を使って、計算できます。
このマルチバージョニングの基本構想では、SQL ステートメントでレコードを削除しても、すぐにはそのレコードがデータベースから物理的に削除されません。 削除に対して書き込まれた更新取り消しログレコードを InnoDB が破棄できるようになった時点で、対応するレコードとそのインデックスレコードがデータベースから物理的に削除されます。この削除操作はパージと呼ばれます。この操作はきわめて高速で、通常は削除を行った SQL ステートメントと同程度の時間しかかかりません。
MySQL は、テーブルのデータディクショナリ情報を、データベースディレクトリ内の `.frm' ファイルに保存します。ところが、InnoDB 型のテーブルは、テーブルスペース内の InnoDB 内部データディクショナリにも独自のエントリを持っています。MySQL は、テーブルやデータベースを破棄するときに、`.frm' ファイルおよび InnoDB データディクショナリ内の対応するエントリの両方を削除する必要があります。
このために、単に `.frm' ファイルを移動するだけではデータベース間で InnoDB テーブルを移動できません。また、MySQL 3.23.43 以下のバージョンで InnoDB 型のテーブルがあると DROP DATABASE が機能しなかったのもこれが原因です。
すべての InnoDB テーブルには、クラスタードインデックスと呼ばれる、レコードのデータを格納する特別なインデックスがあります。テーブルで PRIMARY KEY を定義すると、主キーのインデックスがクラスタードインデックスになります。
テーブルに主キーを定義しない場合は、InnoDB によって内部的にクラスタードインデックスが作成され、そこで InnoDB がテーブル内のレコードに割り当てるロー ID の順にレコードが並べられます。ロー ID は 6 バイトのフィールドで、新しいレコードが挿入されると単純に数が増加していきます。したがって、ロー ID の順に並べられたレコードは、物理的に挿入された順で並ぶことになります。
クラスタードインデックスを介したレコードへのアクセスは迅速です。これは、インデックス検索が行われるページにレコードデータが配置されるためです。他の多くのデータベースでは、データがインデックスレコードとは別のページに伝統的に格納されています。一般に、テーブルが大きい場合は、クラスタードインデックスの方が従来の方法よりもディスク I/O が少なくなります。
InnoDB では、非クラスタードインデックス(セカンダリインデックスとも呼ばれる)のレコードにレコードの主キー値が含まれています。InnoDB はこの主キー値を使用して、クラスタードインデックスからレコードを検索します。主キーが長いと、セカンダリインデックスの使用する領域が増えることに注意してください。
InnoDB のすべてのインデックスは B ツリーで、インデックスのレコードはツリーのリーフページに格納されます。インデックスページのデフォルトサイズは 16 KB です。新しいレコードが挿入されると、InnoDB はページの 1/16 を、将来のインデックスレコードの挿入や更新に備えて空けようとします。
インデックスレコードがシーケンシャル(昇順または降順)に挿入されると、インデックスページの約 15/16 までがいっぱいになります。 レコードがランダムに挿入された場合は、ページの 1/2 〜 15/16 までがいっぱいになります。インデックスページの使用容量が 1/2 未満になると、InnoDB はインデックスツリーを縮小してページを解放しようとします。
データベースアプリケーションでは、主キーが一意の識別子であり、新しいレコードが主キーの昇順で挿入されることが一般的です。したがって、クラスタードインデックスへの挿入では、ディスクからのランダムな読み取りを必要としません。
一方、セカンダリインデックスは通常一意ではなく、セカンダリインデックスへの挿入は比較的ランダムに行われます。 このため、InnoDB で特別なメカニズムが使用されることなく、多数のランダムなディスク I/O が発生します。
一意でないセカンダリインデックスにインデックスレコードが挿入される場合は、セカンダリインデックスページがすでにバッファプール内にあるかどうかが InnoDB によってチェックされます。すでにある場合は、InnoDB によってインデックスページに直接レコードが挿入されます。バッファプール内にインデックスページがなかった場合は、InnoDB によって特別な挿入バッファ構造にレコードが挿入されます。 挿入バッファは、その全体がバッファプール内に収まるように小さくしてあるため、このバッファへの挿入はきわめて高速です。
挿入バッファは、データベース内のセカンダリインデックスツリーに定期的にマージされます。インデックスツリーの同じページ上で複数の挿入をマージすることで、ディスク I/O を削減できます。 挿入バッファによってテーブルへの挿入速度が最大 15 倍に高められることが測定されています。
データベースのほぼ全体がメインメモリ内に収まる場合に、そのデータベースで最も速くクエリを実行するには、ハッシュインデックスを使用します。InnoDB には、テーブルに定義されたインデックスで実行される検索を監視するメカニズムがあり、ハッシュインデックスの構築がクエリにとって有益であると InnoDB が判断した場合は、自動的にそのインデックスが構築されます。
ただし、ハッシュインデックスは常にテーブルに存在する B ツリーインデックスを基に構築されるので注意が必要です。 InnoDB は、B ツリーインデックス上で検出した検索パターンに応じて、任意の長さのキー(B ツリーに定義されたキー)の先頭部分に、ハッシュインデックスを構築できます。 ハッシュインデックスは部分的であってもかまいません。つまり、B ツリーインデックス全体をバッファプールにキャッシュする必要はありません。InnoDB は、頻繁にアクセスされるインデックスページへの要求に応じてハッシュインデックスを構築します。
ある意味では、柔軟なハッシュインデックスのメカニズムを使用して、InnoDB が十分に余裕のあるメインメモリに適応することで、メインメモリデータベースのアーキテクチャに近づいています。
AUTO_INCREMENT カラムの仕組み
データベース起動後に、オートインクリメントカラムが定義されたテーブル T にユーザが最初に挿入を行う際にそのカラムに明示的に値を指定しなかった場合は、InnoDB によって SELECT MAX(auto-inc-column) FROM T が実行され、その結果の値に 1 を加えた値が、このカラムおよびテーブルのオートインクリメントカウンタに割り当てられます。
これを、「テーブル T のオートインクリメントカウンタが初期化された」と表現します。
InnoDB は、新たに作成されたテーブルに対するオートインクリメントカウンタの初期化で、これと同じ手順を実行します。
ユーザが挿入時にオートインクリメントカラムに値 0 を指定すると、InnoDB は値が指定されなかったものとしてレコードを扱うことに注意してください。
オートインクリメントカウンタが初期化された後に、ユーザがオートインクリメントカラムの値を明示的に指定してレコードを挿入した時、その値が現在のカウンタ値より大きい場合は、カウンタは指定された値に設定されます。ユーザが値を明示的に指定しなかった場合は、InnoDB によってカウンタが 1 つ増加され、その新しい値がカラムに割り当てられます。
カウンタから値が割り当てられるときは、オートインクリメントのメカニズムによって、ロックとトランザクションの処理が省略されます。このため、カウンタから数値を取得したトランザクションをロールバックすると、数値の順序にずれが生じることもあります。
ユーザがオートインクリメントカラムに負の値を指定した場合、または、整数型に格納できる最大の整数値より大きな値を指定した場合の、オートインクリメントの動作は定義されていません。
InnoDB は、ディスク I/O で非同期 I/O を使用します。Windows NT では、オペレーティングシステムが提供するネイティブの非同期 I/O が使用されます。 Unix では、InnoDB に組み込まれた疑似的な非同期 I/O が使用されます。InnoDB は、多数の I/O スレッドを作成して、先読みなどの I/O 操作に対応します。将来のバージョンでは、Windows NT の疑似 aio、および Unix のネイティブ aio(装備されている場合)が追加でサポートされるようになります。
Windows NT では、InnoDB はバッファなしの I/O を使用します。つまり、InnoDB が読み書きするディスクページが、オペレーティングシステムのファイルキャッシュにバッファされません。これによって、メモリの使用帯域幅をある程度節約できます。
3.23.41 より、InnoDB は二重書き込みと呼ばれる斬新なファイルフラッシュ技法を使用するようになりました。
この技法によって、オペレーティングシステムのクラッシュや停電後のリカバリがより安全になります。また、fsync 操作の必要性を軽減することで、ほとんどの Unix フレーバでパフォーマンスが向上します。
二重書き込みでは、InnoDB がデータファイルにページを書き込む前に、まず二重書き込みバッファと呼ばれる隣接するテーブルスペースに、それらのページが書き込まれます。書き込みおよび二重書き込みバッファへのフラッシュが完了した後で初めて、InnoDB はデータファイルの適切な場所にページを書き込みます。このページへの書き込みの最中にオペレーティングシステムがクラッシュした場合は、InnoDB が二重書き込みバッファから適切なページのコピーを探し出してリカバリを行います。
3.23.41 よりローデバイスもデータファイルとして使用できるようになりましたが、このテストはまだ完了していません。新しいデータファイルを作成するときに、innodb_data_file_path で指定したデータファイルサイズの直後に newraw キーワードを付加する必要があります。パーティションは、少なくとも指定したサイズと同じ大きさでなければなりません。 InnoDB での 1M は 1,024 x 1,024 バイトですが、通常のディスクの仕様では、1 MB は 1,000,000 バイトを意味することに注意してください。
innodb_data_file_path=/dev/hdd1:5Gnewraw;/dev/hdd2:2Gnewraw
サーバを再起動する前に、キーワードを raw に変更する必要があります。そうしないと InnoDB がパーティションを上書きします。
innodb_data_file_path=/dev/hdd1:5Graw;/dev/hdd2:2Graw
Unix の一部のバージョンでは、ローデバイスを使用することで、バッファなしの I/O を実行できます。
ローデバイスを使用するときは、MySQL サーバを実行する OS のアカウントに、それらのパーティション(上記例では/dev/hdd1)に対して読み書きできる権限があることを確認してください。
InnoDB には、先読み方法として、シーケンシャルな先読みとランダムな先読みの 2 種類があります。シーケンシャルな先読みでは、テーブルスペース内のセグメントへのアクセスパターンがシーケンシャルであることを InnoDB が検知します。 この場合 InnoDB は、一連のデータベースページの読み取りを事前に 1 つにまとめて I/O システムに送信します。ランダムな先読みでは、テーブルスペース内のある領域がバッファプールへ完全に読み取られている最中であることを InnoDB が検知します。この場合、InnoDB は残りの読み取りを I/O システムに送信します。
オプション設定ファイルに定義するデータファイルから、InnoDB のテーブルスペースが構成されます。これらのファイルは、単純に連結されてテーブルスペースになります。ストライピングは使用されません。 現時点では、テーブルスペースのどの位置にテーブルが割り当てられるかを定義できません。ただし、新たに作成されるテーブルスペースでは、InnoDB が末端からスペースを割り当てます。
テーブルスペースは、デフォルトサイズが 16 KB のデータベースページで構成されます。 これらのページは、64個の連続するページから成るエクステントにグループ化されます。InnoDB では、テーブルスペース内部の 'ファイル' をセグメントと呼びます。ロールバックセグメントという名前は、多少誤解を招くおそれがあります。これは、ロールバックセグメントが実際にはテーブルスペース内の多数のセグメントを含んでいるためです。
InnoDB では、各インデックスに 2 つのセグメントが割り当てられます。1 つは B ツリーの非リーフノード用、もう 1 つはリーフノード用です。これには、データを含んでいるリーフノードで連続性を高める意図があります。
テーブルスペース内でセグメントが大きくなると、InnoDB はそのセグメントに最初の 32 ページを個別に割り当てます。その後は、エクステント全体がセグメントに割り当てられます。 InnoDB では、データの連続性を確保するために、大きなセグメントに一度に最大 4 つのエクステントを追加できます。
テーブルスペースには、他のページのビットマップを含んだページがあるため、InnoDB テーブルスペース内のいくつかのエクステントは、全体としてではなく個別のページとしてのみセグメントに割り当てることができます。
クエリ SHOW TABLE STATUS FROM ... LIKE ... を発行してテーブルスペース内の空き領域を照会すると、InnoDB からテーブルスペース内の完全に空いているエクステントが報告されます。
InnoDB は、常にいくつかのエクステントをクリーンアップとその他の内部的な用途のために確保しています。これらのエクステントは空き領域に含まれません。
テーブルからデータを削除すると、InnoDB によって対応する B ツリーインデックスが縮小されます。これによって個々のページまたはエクステントがテーブルスペースに解放されて、他のユーザがその領域を利用できるようになるかどうかは、削除のパターンによって異なります。テーブルを破棄するか、またはテーブルからすべてのレコードを削除すると、他のユーザに確実に領域が解放されます。ただし、削除されたレコードは、トランザクションロールバックまたは一貫した読み取りでそのレコードが必要なくなった後のパージ操作で初めて物理的に削除されることに留意してください。
InnoDB テーブルのインデックスでランダムな挿入または削除が行われると、インデックスがフラグメント化されることがあります。 フラグメント化とは、ディスクでのインデックスページの物理的な順序が、ページでのレコードのアルファベット順とかけ離れていること、またはインデックスに割り当てられた 64 ページのブロック内に多数の未使用ページがあることを意味します。
インデックスのスキャンを速くするには、定期的に mysqldump を使ってテーブルをテキストファイルにダンプしてからテーブルを破棄し、ダンプからテーブルを再ロードします。
デフラグメント化のもう 1 つの方法として、テーブル変更操作 ALTER TABLE tablename TYPE=InnoDB を実行します。
これによって、MySQL がテーブルを再構築します。
インデックスへの挿入が常に昇順で行われ、レコードが必ず末尾から削除される場合は、InnoDB のファイル領域管理アルゴリズムによってインデックスのフラグメント化が発生しないことが保証されます。
InnoDB でのエラー処理は、必ずしも SQL 標準に明記されているとおりではありません。SQL-99 では、SQL ステートメントでエラーが発生した場合は、そのステートメントでロールバックを実行するように記述されています。InnoDB では、ステートメントの一部のみ、またはトランザクション全体がロールバックされることがあります。 次のリストは、InnoDB でのエラー処理の仕様です。
'Table is full' エラーが発生し、InnoDB が SQL ステートメントをロールバックする。
INSERT INTO ... SELECT ... のようなステートメント内であっても、その特定のレコードの挿入のみがロールバックされる。
この仕様は、SQL ステートメントに IGNORE オプションを指定しなかった場合のみステートメントがロールバックされるように変更される予定。
mysql データベースの配下のテーブル)を MyISAM から InnoDB に変換してはならない。この変換はサポートされていない。変換してしまうと、バックアップから以前のシステムテーブルをリストアするか、mysql_install_db スクリプトでシステムテーブルを再構築しない限り、MySQL を再起動できなくなる。
SHOW TABLE STATUS から返される InnoDB テーブルの統計情報は、テーブルが確保している物理サイズを除いて正確ではない。
レコードのカウントは、SQL の最適化で使用される大まかな推定値に過ぎない。
CREATE TABLE T (A CHAR(20), B INT, UNIQUE (A(5))) TYPE = InnoDB;カラムの先頭の一部に非ユニークなインデックスを張ると、InnoDB はそのカラム全体にインデックスを作成する。
INSERT DELAYED は、InnoDB テーブルではサポートされない。
LOCK TABLES 操作では、すでに完了した SQL ステートメントで設定された InnoDB の行レベルロックが考慮されない。つまり、あるテーブルで他のユーザのトランザクションが行レベルロックを設定していても、そのテーブルでテーブルロックを取得できる。したがって、テーブルで実行した操作が他のユーザのロックと衝突した場合は、操作が待機状態になる可能性がある。また、デッドロックが発生する可能性もある。ただし、InnoDB によって設定される行レベルロックでは常に完全性が配慮されているため、デッドロックによってトランザクションの完全性が損なわれることはない。
また、テーブルロックのために、他のトランザクションはテーブル上で(矛盾するロックモードで)行レベルロックを追加で取得できなくなる。
DELETE FROM TABLE ではテーブルが再生成されないが、代わりにすべてのレコードが 1 つずつ削除される。この処理はそれほど速くない。MySQL の将来のバージョンでは、処理の速い TRUNCATE を使用できるようになる。
AUTO_INCREMENT カラムの古い値を再使用する場合がある。
AUTO_INCREMENT カラムの最初の値を CREATE TABLE ... AUTO_INCREMENT=...(または ALTER TABLE ...)で設定できない。最初の値を設定するには、1 を差し引いた値を持つダミーのレコードを挿入し、その後でダミーのレコードを削除する。
AUTOCOMMIT=0 の場合、またはステートメントが BEGIN ... COMMIT で囲まれている場合は、InnoDB テーブルにも MySQL のクエリキャッシュを使用できるようになった。
#sql... -> rsql... を利用した場合に、InnoDB が row_mysql_lock_data_dictionary() でアサートしていた。
page_dir_find_slot() でアサートエラーが発生していた。
SELECT が同時に発生した場合に、InnoDB が無関係なレコードを返すことがあった。
LOCK TABLES の内部で SELECT が使用された場合に、`btr0sea.c' ラッチでハングが発生しないように修正された。
DELETE ステートメントが、いくつかのローを削除した後に FOREIGN KEY エラーまたは Table is full エラーで失敗した場合に、MySQL が SQL ステートメント全体をロールバックしていなかった。
BLOB および TEXT なしで)を超過した場合に、InnoDB は単にクラスタードインデックスからレコードを削除していた。同様の挿入で、InnoDB が予約されたファイル領域のエクステントをリークし、それが次の mysqld 起動時まで解放されなかった。
BLOB 値を使用すると、大きな BLOB 操作の際に、InnoDB が最新のチェックポイントの後に作成されたログに一時的に上書きする可能性があった。その時点で InnoDB がクラッシュすると、最新のチェックポイントまでログをスキャンできないために、クラッシュリカバリが失敗していた。このバージョンより、InnoDB は最新のチェックポイントが十分に新しいことを保証するようになった。これを保証できない場合、InnoDB は MySQL の `.err' ログに警告を出力し、ログファイルを拡大するように勧告する。
innodb_fast_shutdown=0 を設定しても作用しなかった。
CREATE TABLE がコメントで終わっていると、メモリオーバランが発生する可能性があった。
Operating system error number .. in a file operation を出力するときのエラー番号の説明が間違っていた。回避策: Windows のエラー番号については、http://www.innodb.com/ibman.php のセクション 13.2 を参照。
CHAR カラムで、t(a CHAR(200), PRIMARY KEY (a(10))) のようにカラムプリフィックス PRIMARY KEY を作成すると、単純な SELECT でも InnoDB がクラッシュしていた。作成されたキーが PRIMARY でない場合も、CHECK TABLE からテーブルの破損が報告されていた。
SAVEPOINT および ROLLBACK TO SAVEPOINT をサポートするようになった。構文については、http://www.innodb.com/ibman.php#Savepoints を参照。
CREATE TABLE t (a BLOB, INDEX (a(10))) のように、カラムプリフィックスキーを作成できるようになった。
O_DIRECT を innodb_flush_method として使用することもできる。ただし、これらのオペレーティングシステムで予想されるバグに注意する。
InnoDB: Warning: an inconsistent page in the doublewrite buffer InnoDB: space id 2552202359 page number 8245, 127'th page in dblwr buf.ただし、これは危険ではないので無視してかまわない。
SHOW INNODB STATUS で最新の UNIQUE KEY エラーに関する詳細情報が出力されていたが、この情報を格納すると REPLACE の速度が大きく低下する可能性があった。この情報が格納または出力されなくなった。
SET FOREIGN_KEY_CHECKS=0 が適切にレプリケートされていなかった。下位バージョンの 3.23 にはこの修正が移植されない。
innodb_max_dirty_pages_pct で、バッファプール内の空きページが考慮されていなかった。このため、バッファプール内に多数の空きページがある場合でも、必要以上にフラッシュされることがあった。回避策: SET GLOBAL innodb_max_dirty_pages_pct = 100。
AUTOCOMMIT=1 の場合にバイナリロギングがオンになっていないと、MySQL が LOCK TABLES の内部で更新を行う SQL ステートメントの後にコミットを実行できなかった。また、SELECT ステートメントではバイナリロギングの状態に関係なくコミットが実行されなかった。
page_dir_find_slot() 関数でアサートが発生していた。
UPDATE CASCADE 節を伴う FOREIGN KEY で、親カラムの内部ストレージ長が子カラムと異なっている場合にカスケードされた更新を実行すると、子テーブルでカラム長が不適切となり、子テーブルが破損していた。MySQL の '暗黙的なカラム仕様の変更' のために固定長の CHAR カラムが内部的に VARCHAR に変更され、このエラーを引き起こす可能性がある。
latin1 以外のキャラクタセットが使用された場合、および FOREIGN KEY で親カラムの内部ストレージ長が子カラムと違っている場合に、子テーブルへの挿入がすべて外部キーエラーで失敗していた。
SELECT が同時に発生した場合に無関係なローを返したりすることがあった。
LOCK TABLES 内で SELECT が使用された場合に、`btr0sea.c' ラッチ上でハングが発生しないように修正された。
os_event_wait() が適切に機能していなかったために、各種ログ操作でリソースの枯渇が発生していた。
DELETE ステートメントがいくつかのローを削除した後に FOREIGN KEY エラーまたは 'Table is full error' で失敗した場合に、MySQL は SQL ステートメント全体をロールバックせず、失敗したステートメントをバイナリログに書き込み、その際にゼロ以外の error_code を報告していた。
CREATE TABLE でその制限をチェックしていなかったために、テーブルに対する後続の INSERT または SELECT でアサートが発生する可能性があった。
innodb_flush_log_at_trx_commit のデフォルト値が 0 から 1 に変更された。この新しいリリースでは、`my.cnf' でこの値を明示的に指定しないと値 1 が設定されるため、トランザクションコミットのたびにディスクへのログのフラッシュが発生してアプリケーションの実行速度が大幅に低下する。
pthread_mutex_destroy() が呼び出されていなかった。そのために、FreeBSD およびその他の Linux 以外の Unix でメモリリークが発生する可能性があった。
GROUP BY および DISTINCT が NULL 値を不等として扱うことがあった。
InnoDB が ALTER TABLE DROP FOREIGN KEY をサポートするようになった。外部キーを破棄する場合は、SHOW CREATE TABLE を使って、内部で生成された外部キー ID を検出する必要がある。
SHOW INNODB STATUS で、最後に検出された FOREIGN KEY エラーおよび UNIQUE KEY エラーの詳細情報が出力されるようになった。InnoDB が CREATE TABLE からエラー 150 を返した原因がわからない場合は、このステートメントを使って原因を調査できる。
ANALYZE TABLE が InnoDB 型のテーブルでも動作するようになった。このステートメントは、各インデックスツリーをランダムに 10 箇所調べ、それに応じてインデックスのカーディナリティの推定値を更新する。これは推定値に過ぎないため、ANALYZE TABLE を実行するたびに異なる数値が生成される可能性があることに注意する。MySQL は、インデックスカーディナリティの推定値を結合の最適化でのみ使用する。適切に最適化されていない結合がある場合は、ANALYZE TABLE を試すことができる。
InnoDB のグループコミット機能が、MySQL のバイナリログがオンになっている場合も動作するようになった。グループコミットをアクティブにするには、クライアントスレッドの数が 3 つ以上でなければならない。
innodb_flush_log_at_trx_commit のデフォルト値が 0 から 1 に変更された。この新しいリリースでは、`my.cnf' でこの値を明示的に指定しないと値 1 が設定されるため、トランザクションコミットのたびにディスクへのログのフラッシュが発生してアプリケーションの実行速度が大幅に低下する。
innodb_max_dirty_pages_pct が追加された。この変数は 0 〜 100 の整数である。
デフォルト値は 90。InnoDB のメインスレッドは、多くてもこのパーセンテージが常にフラッシュされずに残るように、バッファプールからページをフラッシュしようとする。
innodb_force_recovery=6 の場合に、InnoDB が破損ページを二重書き込みバッファに基づいて修復しないようになった。
InnoDB の起動が速くなった。
FOREIGN KEY 定義用の InnoDB パーサで混乱が生じていた。
FOREIGN KEY で参照されていたテーブルを破棄し、その後に一致しないカラム型で同じテーブルを作成すると、InnoDB が `dict0load.c' の dict_load_table() 関数でアサートする可能性があった。
GROUP BY および DISTINCT が NULL 値を不等として扱うことがあった。
また、インデックスの範囲が空である場合に、MySQL がネクストキーロックを実行できなかった。
CREATE TABLE では InnoDB トランザクションがコミットされない。
ON DELETE SET NULL で変更できなかったが、これを可能にして、カスケードされた操作で無限ループが生じないようにした。
HANDLER PREV および NEXT を使用できるようになった。
MIN() または MAX() によってデッドロックまたはロック待ちのタイムアウトが発生した場合に、MySQL がエラーを返さずに、関数の値として NULL を返していた。
InnoDB が pthread_mutex_destroy() を呼び出していなかった。そのために、FreeBSD およびその他の Linux 以外の Unix システムでメモリリークが発生する可能性があった。
InnoDB が、Windows を実行する 32 ビットの Intel コンピュータでバッファプールメモリを 64 GB までサポートするようになった。これが実現したのは、InnoDB が Windows の AWE 拡張を使用して 32 ビットプロセスの 4 GB の制限を超えるメモリに対応できるようになったため。新しい起動変数 innodb_buffer_pool_awe_mem_mb によって、AWE が有効になり、バッファプールのサイズがメガバイト単位で設定される。
InnoDB の使用するメモリが 2% 少なくなった。
SELECT ... WHERE indexcolumn < x および SELECT ... WHERE indexcolumn > x というタイプのクエリで、適切に選択できる場合でもテーブルがスキャンされる可能性があった。
TL_IGNORE を指定して store_lock を呼び出す場合に発生していたバグが修正された。
mysql_use_result() を使用するアプリケーションプログラムで、2 つ以上の接続を使って SQL クエリを送信すると、`btr0sea.c' の適応的なハッシュ S-ラッチでデッドロックが発生する可能性があった。これを改善し、mysqld が SELECT からのデータをクライアントに渡すたびに S-ラッチを解放するようにした。
SELECT ... FROM ... ORDER BY ... DESC が、無限ループでハングする可能性があった。
SET FOREIGN_KEY_CHECKS=0 が適切にレプリケートされない。
INSERT INTO t1 SELECT ... FROM t2 WHERE ... で、t2 にテーブルレベルのリードロックが設定されていた。このロックが設定されなくなった。
SHOW INNODB STATUS の最大出力長が 200 KB に拡大された。
SELECT ... WHERE indexcolumn < x および SELECT ... WHERE indexcolumn > x というタイプのクエリで、適切に選択できる場合でもテーブルがスキャンされる可能性があった。
btr_free_externally_stored_field() に設定された X-ラッチによって発生するセマフォ待ち。
ha_innobase::change_active_index() で mysqld がクラッシュしていた。
SELECT ステートメントの途中でクエリを推定すると、`btr0sea.c' の適応的なハッシュインデックスラッチで InnoDB がハングする可能性があった。
page_dir_find_owner_slot() でアサートする可能性があった。
エラー 33 ERROR_LOCK_VIOLATION で失敗する可能性があった。
同期書き込みで、InnoDB が書き込みを 1 秒間隔で 100 回再試行するようになった。
REPLACE INTO t1 SELECT ... が、t1 にオートインクリメントカラムがある場合に機能しなかった。
SET FOREIGN_KEY_CHECKS=0 が適切にレプリケートされない。
INSERT INTO t1 SELECT ... FROM t2 WHERE ... で、t2 にテーブルレベルの読み取りロックが設定されていた。このロックが設定されなくなった。
btr_search_info_update_slow でのアサートが、3 つのスレッドの競合で理論的に失敗する可能性があった。
btr_free_externally_stored_field() に設定された X-ラッチによって発生するセマフォ待ち。
SELECT ステートメントの途中でクエリを推定すると、`btr0sea.c' の適応的なハッシュインデックスラッチで InnoDB がハングする可能性があった。
page_dir_find_owner_slot() でアサートする可能性があった。
エラー 33 ERROR_LOCK_VIOLATION で失敗する可能性があった。
同期書き込みで、InnoDB が書き込みを 1 秒間隔で 100 回再試行するようになった。
SET FOREIGN_KEY_CHECKS=0 が適切にレプリケートされない。この修正は 4.0.11 で行われ、3.23 に移植される可能性は低い。
page_cur_search_with_match で、InnoDB が同じページから永久的に動かなくなるバグが修正された。
このバグは、テーブルに複数のページがある場合のみ発生する。
'InnoDB: Out of memory in additional memory pool' が削除された。
FOREIGN KEY (...) REFERENCES ...(...) [ON UPDATE CASCADE | ON UPDATE SET NULL | ON UPDATE RESTRICT | ON UPDATE NO ACTION] もサポートするようになった。
SHOW TABLE STATUS で "InnoDB free" に表示される空き領域が増える。
SELECT でデッドロックまたはロック待ちタイムアウトが発生した場合に、.err ログから余分なエラー 149 および 150 が出力されないようになった。
btr_search_info_update_slow でのアサートが、3 つのスレッドの競合で理論的に失敗する可能性があった。
REPEATABLE READ から別のレベルに設定すると、REPEATABLE READ に戻すことができなかった。
innodb_log_arch_dir は、MySQL に関係しないため、`my.cnf' でこのパラメータを指定する必要がなくなった。
AUTOCOMMIT=1 モードの LOAD DATA INFILE が、1 MB 分のバイナリログが書き込まれるたびに暗黙的にコミットを実行しなくなった。
LOCK TABLES ... READ LOCAL では、読み取られたローに行ロックを設定できなかった。このため、mysqldump でデッドロックやロック待ちタイムアウトが発生していた。
AUTO_INCREMENT で、REPLACE がカウンタを 1 のままにする可能性があった。
デッドロックまたはロック待ちタイムアウトでも同じ問題が発生する可能性があった。
TRUNCATE を実行すると、InnoDB がクラッシュしていた。
INSERT INTO ... SELECT ... または CREATE TABLE ... SELECT ... を実行すると、`btr0sea.c' の 128 行目で作成されたセマフォで InnoDB がハングする可能性があった。
回避策: バイナリログをオンにする。
SLAVE STOP を発行すると、SLAVE START がトランザクションの一部しか実行しなくなる可能性があった。スレーブがクラッシュ後に再起動された場合も、同様のエラーが発生する可能性があった。
NOT NULL として宣言すると、ORDER BY が失敗する可能性があった。
ON DELETE CASCADE に関連するロック待ちタイムアウトによって、インデックスが破損する可能性があった。
SELECT を実行した場合に、検索で一致したレコードに削除マークが付いていると、InnoDB が誤って次のレコードを返す可能性があった。
LOCK TABLES ... READ LOCAL では、読み取られたローに行ロックを設定できなかった。このため、mysqldump でデッドロックやロック待ちタイムアウトが発生していた。
READ COMMITTED および READ UNCOMMITTED をサポートするようになった。READ COMMITTED は Oracle を厳密にエミュレートしているため、Oracle から MySQL へのアプリケーションの移植が容易になる。
lower_case_table_names の設定が考慮されるようになった。
SHOW CREATE TABLE で外部キー定義にデータベース名が出力されない。
innodb_force_recovery に 0 より大きい値を設定すると、InnoDB はテーブルに対して SELECT * FROM を実行する際に、破損したインデックスレコードとページをとばす。これはダンプの際に役立つ。
SHOW INNODB STATUS、innodb_monitor、または innodb_lock_monitor で、1 回のレポートに数百のトランザクションを出力する必要がある場合に出力が切り捨てられると、`srv0srv.c' の 1621 行目で作成された mutex に対する多数の待ちをエラーログに出力する際に InnoDB がハングしていた。
SHOW INNODB STATUS を実行すると、平均ファイル読み取りサイズが常に 0 バイトとして報告されていた。
ORDER BY ... DESC を実行するようになった。
DROP TABLE を実行すると、クラッシュまたはハングが発生する可能性があった。これが実際にユーザにとって問題であると見なされた場合のみ、この修正が下位の 3.23 にも移植される。
NOT NULL として宣言すると、ORDER BY が失敗する可能性があった。
ON DELETE CASCADE に関連するロック待ちタイムアウトによって、インデックスが破損する可能性があった。
SELECT を実行した場合に、検索で一致したレコードに削除マークが付いていると、InnoDB が次のレコードを返す可能性があった。
AUTO_INCREMENT に関する 2 つのバグが検出された。1 つは REPLACE がカウンタを 1 のままにする可能性があること、もう 1 つはデッドロックまたはロック待ちタイムアウトで同じ問題が発生する可能性があることである。これらは 4.0.6 で修正される。
innodb_force_recovery が設定されている場合でも、テーブルを破棄または作成できるようになった。これを利用して、ロールバックまたはパージでクラッシュを引き起こすテーブルを破棄したり、テーブルインポートが失敗した後のリカバリでロールバックが暴走した場合にテーブルを破棄することができる。
SELECT を実行すると、`btr0cur.c' の 310 行目で長時間(600 秒より長い)のセマフォ待ちが発生する可能性があった。
LOCK TABLES 内でテンポラリテーブルを作成し、そのテーブルを使用すると、`ha_innobase.cc' でアサートエラーが発生していた。
SHOW INNODB STATUS、innodb_monitor、または innodb_lock_monitor で、1 回のレポートに数百のトランザクションを出力する必要がある場合に出力が切り捨てられると、`srv0srv.c' の 1621 行目で作成されたミューテックスに対する多数の待ちをエラーログに出力する際に InnoDB がハングしていた。
SHOW INNODB STATUS を実行すると、平均ファイル読み取りサイズが常に 0 バイトとして報告されていた。
SHOW TABLE STATUS のテーブルコメントフィールドが拡大されて、外部キー定義を 16,000 文字まで出力できるようになった。
innodb_force_recovery が設定されている場合でも、テーブルを破棄または作成できるようになった。これを利用して、ロールバックまたはパージでクラッシュを引き起こすテーブルを破棄したり、テーブルインポートが失敗した後のリカバリでロールバックが暴走した場合にテーブルを破棄することができる。
ORDER BY primarykey DESC を使用すると、`btr0pcur.c' の 203 行目でアサートエラーが発生していた。
SELECT を実行すると、`btr0cur.c' の 310 行目で長時間(600 秒より長い)のセマフォ待ちが発生する可能性があった。
ON DELETE CASCADE または ...SET NULL で変更が実行されてもクエリキャッシュが無効にならなかった。
LOCK TABLES 内でテンポラリテーブルを作成し、そのテーブルを使用すると、`ha_innodb.cc' でアサートエラーが発生していた。
innodb_flush_log_at_trx_commit を 1 に設定すると、SHOW VARIABLES でその値が1,600 万として表示されていた。
HANDLER SQL コマンドが、InnoDB 型のテーブルでも動作するようになった。InnoDB は、HANDLER の読み取りを常に一貫した読み取りとして実行する。HANDLER は、ダイレクトアクセスパスとしてテーブルのインデックスを個別に読み取る。場合によっては、HANDLER をサーバ側カーソルの代わりに使用できる。
DROP TABLE で、テーブル名に文字コードが 127 より大きい文字を使用すると、`pars0sym.c' の 155 行目で InnoDB がアサートする可能性があった。
SHOW INNODB STATUS によって、InnoDB モニタの出力がクライアントに返されるようになった。InnoDB モニタで、最後に検出されたデッドロックに関する詳細情報が出力されるようになった。
BEGIN および COMMIT がトランザクション周辺のバイナリログに追加されるようになった。
MySQL レプリケーションでトランザクションの境界が考慮されるようになった。これで、レプリケーションスレーブでユーザに半分のトランザクションが表示されなくなる。
innodb_flush_log_at_trx_commit=2 によって、InnoDB がコミットのたびにオペレーティングシステムのファイルキャッシュにログを書き込むようになった。その速度は、設定 innodb_flush_log_at_trx_commit=0 とほぼ同じである。また設定 2 には、クラッシュが発生してもオペレーティングシステムがクラッシュしなければコミットされたトランザクションが失われないという優れた特徴がある。
オペレーティングシステムのクラッシュまたは停電が発生した場合は、設定 2 の安全性が設定 0 より低くなる。
SET FOREIGN_KEY_CHECKS=0 が役立つ。
SET UNIQUE_CHECKS=0 を指定すると、InnoDB へのテーブルのインポートが速くなる。
このフラグは、入力レコードが UNIQUE 制約に違反していないことが確実である場合のみ使用できる。
SHOW TABLE STATUS で、想定される ON DELETE CASCADE や ON DELETE SET NULL もテーブルのコメントフィールドに列挙されるようになった。
CHECK TABLE を実行すると、すべてのテーブルのハッシュインデックスもチェックされるようになった。
ON DELETE CASCADE または SET NULL を定義し、親レコードで参照キーを更新すると、InnoDB によって子レコードが削除または更新されていた。この動作が変更され、SQL-92 に準拠するようになった。つまり、エラー 'Cannot delete parent row' が表示されるようになった。
SHOW TABLE STATUS でテーブルのオートインクリメントカウンタが初期化されるようになった。
これによって、SHOW TABLE STATUS で突然デッドロックが発生することがほぼなくなった。
ON DELETE... 操作にも影響する。
CREATE TABLE の実行中に DROP TABLE または DROP DATABASE を実行すると、失敗する可能性があった。
innodb_log_monitor でページに対してロック出力を抑制するとハングが発生していた。
AUTOCOMMIT モードで SELECT を実行した直後に RENAME TABLE を実行すると、RENAME が失敗し、MySQL からエラー 1192 が返されていた。
ON DELETE CASCADE を指定して自己参照型の外部キー制約をテーブルに追加した場合にレコードを削除すると、カスケード削除のために InnoDB が同じレコードを 2 回削除しようとしてアサートエラーが発生していた。
ON DELETE CASCADE または ON DELETE SET NULL 節を定義できるようになった。
ALTER TABLE および CREATE INDEX で生き残るようになった。
SHOW CREATE TABLE で外部キー制約も列挙されるようになった。mysqldump でも、テーブル定義内の外部キーが出力されるようになった。
ALTER TABLE ... ADD CONSTRAINT FOREIGN KEY (...) REFERENCES ... (...) で追加できるようになった。
SET TRANSACTION ISOLATION LEVEL ... が、InnoDB テーブルに次のように作用するようになった。トランザクションが SERIALIZABLE として定義されている場合、InnoDB はすべての読み取り一貫性に概念上 LOCK IN SHARE MODE を追加する。トランザクションがそれ以外の分離レベルで定義されている場合、InnoDB はそのデフォルトのロック方法である REPEATABLE READ に従う。
SHOW TABLE STATUS でオートインクリメントインデックスの末尾に x-ロックが設定されなくなった。これによって、SHOW TABLE STATUS で突然デッドロックが発生することがほぼなくなる。
CREATE TABLE ステートメントで文字列 'foreign' の後に空白以外の文字が続いていると、FOREIGN KEY パーサで混乱が生じ、テーブル作成がエラー 150 で失敗していた。
DROP DATABASE を呼び出すと、MySQL サーバがクラッシュまたはハングする可能性があった。クラッシュについては修正されたが、完全に修正されるには、MySQL コードレイヤでの変更を待つ必要がある。
DROP DATABASE を実行するには、データベース名を小文字で指定する必要があった。3.23.49 で修正: Windows では大文字と小文字が区別されなくなった。Unix では引き続きデータベース名で大文字と小文字が区別される。
SELECT クエリが同時に実行された場合のパフォーマンス上の問題が解消された。
CPU バウンドの大規模な SELECT クエリの実行も、すべてのプラットフォームで一様に速くなる。
SHOW CREATE TABLE または SHOW TABLE STATUS を呼び出すと、メモリが破損し、mysqld がクラッシュする可能性があった。
特に、SHOW CREATE TABLE を頻繁に呼び出す mysqldump では危険性が高かった。
ALTER TABLE とクエリを同時に実行した場合に、mysqld が `row0row.c' の 474 行目でアサートエラーを起こしてクラッシュする可能性があった。
LOCK TABLES 内にラップされていると、InnoDB が `lock0lock.c' でアサートしていた。
CHECK TABLE からはテーブルが破損していると報告されていた。
CHECK TABLE がこの状況でエラーを報告しなくなった。
SHOW VARIABLES を実行すると、innodb_flush_log_at_trx_commit などのブール値の起動パラメータが、オンであっても常に OFF と表示されていた。
ORDER BY クエリと DISTINCT クエリの実行が速くなった。
ORDER BY クエリでソートが不要になる。
innodb_lock_wait_timeout のデフォルト値が無限から 50 秒へと変更され、innodb_file_io_threads のデフォルト値が 9 から 4 に変更された。
LOCK IN SHARE MODE を認識しなかった。これが修正された。
ALTER TABLE を実行すると、MySQL サーバがクラッシュする可能性があった。
'mysql.user'、'mysql.host'、または 'mysql.db' を InnoDB 型で作成できないようになった。
FOREIGN KEY (col1) REFERENCES table2(col2)
innodb_table_monitor も含まれる。
DROP DATABASE が InnoDB テーブルでも動作するようになった。
innodb_thread_concurrency は、並行処理が頻繁に行われる環境でのパフォーマンスチューニングに役立つ。
innodb_force_recovery は、破損したデータベースからテーブルをダンプする際に役立つ。
innodb_fast_shutdown は、シャットダウンを高速化する。通常、InnoDB はシャットダウン時に完全なパージと挿入バッファのマージを実行する。
MAX(col) が選択される際に、col が複合インデックスの最初のカラムでない場合に発生していたクラッシュが修正された。
innodb_monitor、innodb_lock_monitor、innodb_tablespace_monitor の 3 つになった。
innodb_monitor で、バッファプールのヒット率、および挿入、更新、削除、読み取りが行われたレコードの総数が新たに出力されるようになった。
RENAME TABLE でのバグが修正された。
ORDER BY のバグ('Sort aborted')が修正された。
CHECK TABLE が InnoDB テーブルでも動作するようになった。
innodb_unix_file_flush_method が追加された。このパラメータは、ディスク書き込みパフォーマンスのチューニングに使用できる。
SELECT ... LOCK IN SHARE MODE が導入された。
InnoDB が fsync() を呼び出し、書き込みまたは読み取りを行うすべてのデータベースページのチェックサムを計算してディスクの不具合を明らかにするようになった。
InnoDB についての問い合わせ先
InnoDB エンジンの製造元である Innobase Oy の問い合わせ先は次のとおりです。
Web サイト: http://www.innodb.com/。
電子メール: sales@innodb.com
phone: 358-9-6969 3250 (office) 358-40-5617367 (mobile) Innobase Oy Inc. World Trade Center Helsinki Aleksanterinkatu 17 P.O.Box 800 00101 Helsinki Finland
BDB または BerkeleyDB テーブルBDB テーブルの概要
BerkeleyDB(http://www.sleepycat.com/ で入手可能)は、MySQL にトランザクションストレージエンジンをもたらしました。このストレージエンジンのサポートは、バージョン 3.23.34 から MySQL ソースディストリビューションに組み込まれるようになり、MySQL-Max バイナリでアクティブ化されます。通常、このストレージエンジンは略して BDB と呼ばれます。
BDB テーブルはクラッシュに対する耐久性が高く、トランザクションでの COMMIT および ROLLBACK 操作にも対応します。
MySQL ソースディストリビューションに付属する BDB ディストリビューションには、MySQL でより円滑に動作するための小規模なパッチがいくつか適用されています。
パッチが適用されていない BDB バージョンは、MySQL で使用できません。
MySQL AB は、Sleepycat 社と密接に協力しながら、MySQL/BDB インタフェースの品質維持に努めています。
BDB テーブルのサポートに関しては、ユーザによる問題の特定を支援すると共に、BDB テーブルが関係するあらゆる問題に対する再現可能なテストケースの作成を支援しています。作成されたテストケースは Sleepycat 社に送られ、同社の支援を受けながら問題を特定して修正します。このように 2 段階の作業になるため、BDB テーブルの問題は、他のストレージエンジンよりも修正に若干時間がかかる場合があります。
ただし、BerkeleyDB コード自体はこれまでに MySQL 以外の多くのアプリケーションで使用されているため、大きな問題が発生することは考えられません。 See section 1.4.1 MySQL AB によって提供されるサポート。
BDB のインストール
BerkeleyDB をサポートする MySQL のバイナリバージョンをダウンロードした場合は、MySQL のバイナリバージョンをインストールするための指示に従ってください。
See section 2.2.12 MySQL バイナリディストリビューションのインストール。 See section 4.8.5 mysqld-max(拡張 mysqld サーバ)。
Berkeley DB をサポートする MySQL をコンパイルするには、MySQL バージョン 3.23.34 以降をダウンロードし、--with-berkeley-db オプションを使って MySQL をコンフィギャします。 See section 2.3 MySQL ソースディストリビューションのインストール。
cd /path/to/source/of/mysql-3.23.34 ./configure --with-berkeley-db
詳しい最新情報については、BDB ディストリビューション付属のマニュアルを参照してください。
Berkeley DB 自体は十分にテストされていて信頼できますが、MySQL とのインタフェースはまだガンマ品質と見なされています。 当社は、このインタフェースの 1 日も早い安定化を目指して、積極的に改善と最適化を行っています。
BDB 起動オプション
AUTOCOMMIT=0 で実行している場合、BDB テーブルでの変更は COMMIT を実行するまで反映されません。コミットの代わりに ROLLBACK を実行すると、変更が無効になります。 See section 6.7.1 START TRANSACTION、COMMIT、ROLLBACK の各構文。
AUTOCOMMIT=1(デフォルト)で実行している場合は、変更が直ちにコミットされます。SQL コマンド BEGIN WORK で拡張トランザクションを開始できます。その場合、COMMIT を実行するまで(または変更の ROLLBACK を実行するまで)変更がコミットされません。
次に示す mysqld のオプションを使用すると、BDB テーブルの動作を変更できます。
| オプション | 説明 |
--bdb-home=directory | BDB テーブルのベースディレクトリ。--datadir に使用するディレクトリと同じでなければならない。
|
--bdb-lock-detect=# | Berkely のロック検出。DEFAULT、OLDEST、RANDOM、または YOUNGEST のいずれか。
|
--bdb-logdir=directory | Berkeley DB のログファイルディレクトリ。 |
--bdb-no-sync | ログを同期的にフラッシュしない。 |
--bdb-no-recover | Berkeley DB をリカバリモードで起動しない。 |
--bdb-shared-data | Berkeley DB をマルチプロセスモードで起動する(Berkeley DB を初期化する際に DB_PRIVATE を使用しない)。
|
--bdb-tmpdir=directory | Berkeley DB のテンポラリファイルディレクトリ。 |
--skip-bdb | BDB テーブルの使用を無効にする。
|
-O bdb_max_lock=1000 | ロックの数の上限を設定する。 See section 4.6.8.4 SHOW VARIABLES。
|
--skip-bdb を使用すると、MySQL は Berkeley DB ライブラリを初期化しなくなるため、大量のメモリを節約できます。このオプションを使用しているときは BDB テーブルを使用できません。BDB テーブルを作成しようとすると、代わりに MyISAM テーブルが作成されます。
BDB テーブルを使用する予定であれば、通常は --bdb-no-recover を指定せずに mysqld を起動する必要があります。ただし、BDB ログファイルが破損している場合に mysqld を起動しようとすると、問題が発生する可能性があります。 See section 2.4.2 MySQL サーバの起動に関する問題。
bdb_max_lock では、BDB テーブルでアクティブにできるロックの最大数(デフォルトは 10,000)を指定できます。長いトランザクションを実行しているときや、mysqld がクエリの計算で大量のレコードを調べているときに、bdb: Lock table is out of available locks または Got error 12 from ... というエラーが発生する場合は、この数値を大きくしてください。
また、大規模なトランザクションを使用する場合は、binlog_cache_size および max_binlog_cache_size を変更することもできます。
See section 6.7.1 START TRANSACTION、COMMIT、ROLLBACK の各構文。
BDB テーブルの特性BDB ストレージエンジンは、トランザクションをロールバックできるようにログファイルを保持している。パフォーマンスを最大限に高めるには、--bdb-logdir オプションを使ってこれらのログファイルをデータベースとは別のディスクに配置する必要がある。
BDB ログファイルが開始されるたびにチェックポイントを実行し、現在のトランザクションに不要なログファイルを削除する。FLUSH LOGS を任意の時点で実行して、Berkeley DB テーブルをチェックポイントすることもできる。
障害リカバリには、テーブルバックアップおよび MySQL のバイナリログを使用する必要がある。 See section 4.5.1 データベースのバックアップ。
警告: 使用中の古いログファイルを削除すると、問題が発生した場合に BDB がリカバリを実行できなくなり、データが失われるおそれがある。
BDB テーブルに主キーを必要とする。ユーザがこのキーを作成しない場合は、MySQL によって隠し主キーが作成され、管理される。この隠しキーは長さが 5 バイトで、挿入が試行されるたびにインクリメントされる。
BDB テーブルでアクセスするすべてのカラムが、同じインデックスの一部または主キーの一部であれば、MySQL は実際のレコードにアクセスすることなくクエリを実行できる。MyISAM テーブルでは、カラムが同じインデックスの一部である場合のみこれが当てはまる。
主キーは、ローデータと共に格納されるため、他のどのキーよりも速く処理される。他のキーはキーデータ + 主キーとして格納されるため、主キーはできるだけ短くしてディスクを節約し、処理速度を高めることが重要である。
LOCK TABLES は、他のテーブルと同様に BDB テーブルでも動作する。LOCK TABLE を使用しない場合は、MySQL によってテーブルに対する内部的な複数書き込みのロックが発行され、別のスレッドがテーブルロックを発行した場合にテーブルが適切にロックされるようになる。
BDB テーブルでの内部ロックは、ページレベルで行われる。
BDB テーブルではテーブル内のレコード数のカウントが管理されないため、SELECT COUNT(*) FROM table_name に時間がかかる。
BDB テーブルのデータは、独立したデータファイルではなく B ツリーに格納されるため、順次スキャンには MyISAM テーブルよりも時間がかかる。
BDB テーブルの変更で発生する自動ロールバックおよびデッドロックエラーを伴う読み取りの失敗にいつでも対処できるようにしておく必要がある。
MyISAM テーブルのキーとは異なり、キーのプリフィックスまたはサフィックスは圧縮されない。つまり、BDB テーブルでは MyISAM テーブルに比べてキー情報の領域が若干大きい。
BDB テーブルには、多くの場合、キーツリーの途中で新たなレコードを挿入できるようにすき間が空いている。このため、BDB テーブルは MyISAM テーブルよりも若干大きくなる。
BDB テーブル内の独立したセグメントで管理することで、これに対応している。DELETE または ROLLBACK ステートメントを大量に発行しない限り、この数値は MySQL オプティマイザにとって十分に正確であるが、MySQL はクローズ時までこの数値を格納しないため、MySQL が突然中断されると数値が不正確になる場合がある。この数値が 100% 正確でなくても、致命的な問題にはならない。レコードの数は、ANALYZE TABLE または OPTIMIZE TABLE を実行することで更新できる。 See section 4.6.2 ANALYZE TABLE 構文。 See section 4.6.1 OPTIMIZE TABLE 構文。
BDB テーブルでディスクがいっぱいになると、エラー(おそらくエラー 28)が発生し、トランザクションがロールバックされる。これとは対照的に、MyISAM テーブルと ISAM テーブルでは、十分な空きディスクが確保されるのを待って、mysqld が処理を続行する。
BDB の問題BDB テーブルを同時に開く際にかなり時間がかかる。BDB テーブルを使用する場合は、テーブルキャッシュをあまり大きくしないようにする(たとえば 256 以下にする)必要がある。また、mysql クライアントで --no-auto-rehash を使用する必要がある。これについては、4.0 で部分的に修正する予定である。
SHOW TABLE STATUS で BDB テーブルに関して提供される情報がまだ十分でない。
BDB でサポートされているオペレーティングシステム
今のところ、BDB ストレージエンジンは次のオペレーティングシステムで動作することが確認されています。
次のオペレーティングシステムでは動作しません。
注意: 上記の一覧は完全ではありません。情報が入り次第更新する予定です。
BDB テーブルのサポート付きで MySQL をビルドする場合に、mysqld を起動するとログファイルに次のエラーが記録されることがあります。
bdb: architecture lacks fast mutexes: applications cannot be threaded Can't init databases
これは、使用するアーキテクチャで BDB テーブルがサポートされていないことを意味します。
この場合は、BDB テーブルのサポートなしで MySQL をビルドし直す必要があります。
BDB テーブルの制限事項
次に挙げるのは、BDB テーブルを使用する場合の制限事項です。
BDB テーブルは、`.db' ファイルに、作成されたときのこのファイルへのパスを格納する
(これは、シンボリックリンクをサポートするマルチユーザ環境でロックを検出できるようにするために行われる)。
このため、BDB テーブルはディレクトリ間で移動できない。
BDB テーブルのバックアップを取るときは、mysqldump を使用するか、すべての table_name.db ファイルと BDB ログファイルのバックアップを取る必要がある。BDB のログファイルは、ベースデータディレクトリにある log.XXXXXXXXXX(10 桁)という名前のファイルである。
BDB ストレージエンジンは未完了のトランザクションをログファイルに格納する。これらのログは、mysqld が起動されるときに存在していなければならない。
BDB テーブルを使用するときに起こりうるエラーmysqld を起動する際に hostname.err ログに次のエラーが記録されることがある。
bdb: Ignoring log file: .../log.XXXXXXXXXX: unsupported log version #これは、新しいバージョンの
BDB が古いログファイル形式をサポートしていないことを意味する。この場合は、データベースディレクトリからすべての BDB ログ(log.XXXXXXXXXX という形式の名前を持つファイル)を削除し、mysqld を再起動する必要がある。また、古い BDB テーブルの mysqldump --opt を実行したうえで、そのテーブルを削除し、ダンプをリストアした方がよい。
001119 23:43:56 bdb: Missing log fileid entry
001119 23:43:56 bdb: txn_abort: Log undo failed for LSN:
1 3644744: Invalid
これは致命的なエラーではないが、この問題が修正されるまでは(小さな修正ではない)、オートコミットモードでないときにテーブルを削除しない方がよい。
Go to the first, previous, next, last section, table of contents.