第7章 MySQL のテーブル型

目次

7.1. MyISAM テーブル
7.1.1. キーに必要な領域
7.1.2. MyISAM テーブル形式
7.1.3. MyISAM テーブルの問題
7.2. MERGE テーブル
7.2.1. MERGE テーブルの問題
7.3. ISAM テーブル
7.4. HEAP テーブル
7.5. InnoDB テーブル
7.5.1. InnoDB テーブルの概要
7.5.2. MySQL バージョン 3.23 での InnoDB
7.5.3. InnoDB 起動オプション
7.5.4. InnoDB テーブルスペースの作成
7.5.5. InnoDB テーブルの作成
7.5.6. InnoDB データファイルとログファイルの追加と削除
7.5.7. InnoDB データベースのバックアップとリカバリ
7.5.8. InnoDB データベースを別のマシンに移動する
7.5.9. InnoDB トランザクションモデルとロック
7.5.10. パフォーマンスチューニングのヒント
7.5.11. マルチバージョニングの実装
7.5.12. テーブルとインデックスの構造
7.5.13. ファイル領域の管理とディスク I/O
7.5.14. エラー処理
7.5.15. InnoDB テーブルの制限事項
7.5.16. InnoDB の変更履歴
7.5.17. InnoDB についての問い合わせ先
7.6. BDB または BerkeleyDB テーブル
7.6.1. BDB テーブルの概要
7.6.2. BDB のインストール
7.6.3. BDB 起動オプション
7.6.4. BDB テーブルの特性
7.6.5. 近い将来に修正する必要がある BDB の問題
7.6.6. BDB でサポートされているオペレーティングシステム
7.6.7. BDB テーブルの制限事項
7.6.8. BDB テーブルを使用するときに起こりうるエラー

MySQL バージョン 3.23.6 より、3 種類の基本テーブル形式(ISAMHEAPMyISAM)を選択できるようになりました。これより新しい MySQL のバージョンでは、コンパイルの方法に応じて追加のテーブル型(InnoDB または BDB)をサポートしています。1 つのデータベースに異なる型のテーブルを収容することができます。

新しいテーブルを作成するときに、そのテーブルの型を MySQL に通知できます。通常、デフォルトのテーブル型は MyISAM です。

MySQL では、テーブル定義とカラム定義を保持する .frm ファイルが必ず作成されます。テーブルのインデックスとデータは、テーブル型に応じて、このファイル以外の 1 つ以上のファイルに格納されます。

コンパイルまたはアクティブ化されていないテーブル型を使用しようとすると、MySQL によってそのテーブル型の代わりに MyISAM 型のテーブルが作成されます。この動作は、さまざまなテーブル型をサポートする MySQL サーバ間でテーブルをコピーする場合に便利です(ほとんどの場合、マスタサーバは安全性を高めるためにトランザクションストレージエンジンをサポートし、スレーブサーバは処理速度を高めるために非トランザクションストレージエンジンのみをサポートしています)。

MySQL の初心者は、このテーブル型の自動変更に戸惑うかもしれません。この点については、バージョン 4.1 で新しいクライアント/サーバプロトコルに警告を導入し、テーブル型が自動変更される際に警告を生成する方法で対応する予定です。

ALTER TABLE ステートメントを使用すれば、テーブルを別の型に変換できます。 See 項6.5.4. 「ALTER TABLE 構文」

MySQL では 2 種類のテーブルをサポートしていることに注意してください。1 つはトランザクションセーフのテーブル(InnoDBBDB)、もう 1 つは非トランザクションセーフのテーブル(HEAPISAMMERGEMyISAM)です。

トランザクションセーフのテーブル(TST)には次の利点があります。

InnoDB テーブルを使用するには、少なくとも innodb_data_file_path 起動オプションを使用する必要があることに注意してください。 See 項7.5.3. 「InnoDB 起動オプション」

非トランザクションセーフのテーブル(NTST)には次の利点があります。

同じステートメントで TST テーブルと NTST テーブルを組み合わせると、両方の利点を活かすことができます。

7.1. MyISAM テーブル

MyISAM は、MySQL バージョン 3.23 でのデフォルトのテーブル型です。この型は ISAM コードに基づいており、多数の便利な拡張機能を備えています。

インデックスは .MYI(MYIndex)拡張子の付いたファイルに、データは .MYD(MYData)拡張子の付いたファイルにそれぞれ格納されます。MyISAM テーブルは、myisamchk ユーティリティで検査および修復することができます。See 項4.5.6.7. 「myisamchk を使用したクラッシュのリカバリ」。 また、MyISAM テーブルを myisampack で圧縮することで、使用する領域を大幅に削減できます。 See 項4.8.4. 「myisampack(MySQL 圧縮読み取り専用テーブルジェネレータ)」

MyISAM の新機能は次のとおりです。

  • MyISAM ファイルには、テーブルが正しく閉じられたかどうかを示すフラグがある。mysqld を起動する際に --myisam-recover を指定すると、MyISAM テーブルを開く際にそのテーブルが正しく閉じられたかどうかが自動的に検査され、必要であれば修復される。

  • データファイル内に空きブロックがないテーブルに対し、他のスレッドがそのテーブルから読み取りを行うのと同時に新しいレコードを INSERT できる(同時挿入)。空きブロックは、大量のデータを含んだ可変長レコードに含まれるデータの長さが短くなるような更新をした場合、またはレコードを削除した場合に発生する。すべての空きブロックを使い切ると、それ以後の挿入は再び同時挿入になる。

  • 大きなファイルをサポートするファイルシステム/オペレーティングシステムで、大きなファイル(63 ビット)がサポートされる。

  • すべてのデータが下位バイトから順に格納される。これによって、データがマシンと OS に依存しなくなる。バイナリ移植性を実現するための唯一の要件は、2 の補数で表現された符号付き整数(すべてのマシンで過去 20 年間使われていた形式)および IEEE 浮動小数点形式(同じく主流のマシンでの主要な形式)をマシンで使用することである。マシンにおいてバイナリ互換性をサポートしていない可能性がある領域は、埋め込みシステムのみ(特殊なプロセッサを使用している場合があるため)。

    データを下位バイトから先に格納しても、速度上大きな問題はない。テーブルロー内のバイトは通常整列されておらず、未整列のバイトを順番に読み取る操作は、逆順に読み取る操作に比べてそれほど処理能力を必要としないからである。また、実際のカラムの値を読み取るコードも他のコードに比べて速度が重視されない。

  • すべての数値キーを上位バイトから先に格納して、インデックスの圧縮効率を高めている。

  • 1 つの AUTO_INCREMENT カラムを内部処理している。MyISAM では、このカラムが INSERT/UPDATE で自動更新される。AUTO_INCREMENT の値は、myisamchk でリセットできる。これによって AUTO_INCREMENT カラムの処理が速くなる(最低でも 10%)。また、以前の ISAM のように古い番号が再使用されない。マルチパートキーの最後の項目に AUTO_INCREMENT が定義されている場合は、以前の動作が引き続き有効となることに注意する。

  • キーツリーは、ソートされた順序で挿入されると(AUTO_INCREMENT カラムを使用しているときなど)、分割されて上位ノードにキーが 1 つだけ含まれるようになる。これによって、キーツリーでの領域利用率が向上する。

  • BLOB カラムと TEXT カラムにインデックスを作成できる。

  • インデックスが作成されたカラムで NULL 値が許可される。この場合、1 キー当たり 0 ? 1 バイトが使用される。

  • 最大キー長のデフォルト値は 500 バイト(再コンパイルで変更可能)。長さが 250 バイトを超えるキーの場合は、デフォルトの 1,024 バイトより大きなキーブロックサイズが使用される。

  • テーブル当たりの最大キー数のデフォルト値は 32。この数値は、myisamchk を再コンパイルしなくても 64 まで拡大できる。

  • myisamchk--update-state 付きで実行すると、テーブルが検査済みとしてマークされる。 myisamchk --fast は、このマークがないテーブルのみを検査する。

  • myisamchk -a は、キーの各部分(ISAM のようにキー全体だけではなく)に関する統計情報を格納する。

  • 更新/挿入と削除が混在している場合に、可変長レコードがフラグメント化されることが少なくなった。これは、隣接する削除済みブロックの結合、および次のブロックが削除されている場合のブロックの拡張が自動的に行われるようになったためである。

  • myisampack で、BLOB および VARCHAR カラムをパックできる。

  • データファイルとインデックスファイルを別々のディレクトリに配置して速度を高めることができる(CREATE TABLEDATA/INDEX DIRECTORY="path" オプションを使用)。 See 項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 項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 項4.1.1. 「mysqld コマンドラインオプション」。 テーブルが開く際に検査されます。検査の内容は、テーブルにクラッシュのマークが付いているかどうか、またはテーブルのオープンカウント変数が 0 ではなく、かつ --skip-external-locking で実行しているかどうかです。上記のどちらかが当てはまる場合は、次の処理が行われます。

  • テーブルにエラーがないかどうかが検査される。

  • エラーが見つかった場合は、テーブルの高速修復(ソートは行うがデータファイルは再構築しない)を試みる。

  • データファイルにエラーがあるために(重複キーエラーなど)修復が失敗した場合は、もう一度修復を試みるが、今度はデータファイルを再構築する。

  • この修復が失敗した場合は、以前の修復方法(ソートせずに 1 レコードずつ書き込む方法)で再度修復を試みる。この方法で、どのようなエラーでもわずかなディスク容量で修復できるはずである。

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 項4.1.1. 「mysqld コマンドラインオプション」

7.1.1. キーに必要な領域

MySQL ではさまざまなインデックスをサポートしていますが、一般に使用されるのは ISAM または MyISAM です。これらは B ツリーインデックスを使用します。このインデックスファイルのサイズは、すべてのキーについて (キー長+4)/0.67 を計算し、それを合計することで大まかに算出できます(これは、すべてのキーがソートされた順に挿入され、かつキーが一切圧縮されないという、最悪のケースを想定しています)。

文字列インデックスでは空白が圧縮されます。インデックスの最初の部分が文字列の場合は、プリフィックスも圧縮されます。文字列カラムに含まれる後続の空白が長い場合、またはそのカラムが VARCHAR カラムであるためにその長さがフルに使用されることがない場合は、空白の圧縮によってインデックスファイルが上記の数値よりも小さくなります。プリフィックスの圧縮は、文字列で始まるキーで使用されます。同一のプリフィックスを持つ文字列が多数存在する場合は、プリフィックスの圧縮が役立ちます。

MyISAM テーブルでは、テーブル作成時に PACK_KEYS=1 を指定することで、数値のプリフィックスを圧縮することもできます。この機能は、数値が上位バイトから順に格納される場合に、同一のプリフィックスを持つ整数キーが多数あるときに役立ちます。

7.1.2. MyISAM テーブル形式

MyISAM は、3 種類のテーブル型をサポートします。そのうち 2 つは、使用しているカラムの型に応じて自動的に選択されます。3 番目の圧縮テーブルは、myisampack ツールによってのみ作成されます。

BLOB 値を持たないテーブルを CREATE または ALTER する際に、ROW_FORMAT=# テーブルオプションを使用してテーブル形式を強制的に DYNAMIC または FIXED に設定できます。将来的には、ALTER TABLEROW_FORMAT=compressed | default を指定することで、テーブルを圧縮/展開できるようになります。 See 項6.5.3. 「CREATE TABLE 構文」

7.1.2.1. 静的(固定長)テーブルの特性

これはデフォルトの形式です。この形式は、テーブルに VARCHARBLOB、または TEXT 型のカラムが含まれていない場合に使用されます。

この形式は最も単純かつ最も安全です。また、ディスク上の形式としては最も高速です。速度が速いのは、ディスク上で簡単にデータを検出できるためです。検索の対象がインデックスと静的形式を使用している場合、操作はきわめて単純です。単にレコードの番号にレコードの長さを掛けるだけです。

また、テーブルをスキャンする際にも、1 回のディスク読み取りで一定数のレコードを簡単に読み取ることができます。

固定サイズの MyISAM ファイルへ書き込んでいるときにコンピュータがクラッシュした場合の保全性も証明されています。この場合、myisamchk によって各レコードの開始位置と終了位置が簡単に割り出されます。したがって、通常は、部分的に書き込まれたレコードを除くすべてのレコードを回復できます。MySQL ではすべてのインデックスをいつでも再構築できることに注意してください。

  • CHARNUMERICDECIMAL の各カラムは、そのカラム長までの残りの部分が空白で埋められる。

  • きわめて高速。

  • キャッシュが容易。

  • レコードが固定位置にあるため、クラッシュ後の再構築が容易。

  • myisamchk で再編成する必要がない。ただし、多数のレコードを削除したために空いたディスク領域をオペレーティングシステムに戻す場合を除く。

  • 通常は動的テーブルよりも多くのディスク領域を必要とする。

7.1.2.2. 動的テーブルの特性

この形式は、テーブルに VARCHARBLOB、または TEXT カラムが含まれている場合、あるいはテーブルが ROW_FORMAT=dynamic で作成された場合に使用されます。

この形式は少し複雑です。各レコードにそれぞれの長さを記録したヘッダが必要となるからです。1 つのレコードが、更新によって長くなったために、複数の場所に存在することになる可能性もあります。

OPTIMIZE table または myisamchk を使用して、テーブルをデフラグメント化することができます。VARCHAR または BLOB カラムと同じテーブル内に、頻繁にアクセス/変更する静的データがある場合は、フラグメント化を回避するために動的なカラムを他のテーブルに移動するとよいでしょう。

  • 文字列カラムはすべて動的である(長さが 4 バイト未満のものを除く)。

  • 各レコードの先頭には、どの文字列カラムが空白('')で、どの数値カラムがゼロであるかを示すビットマップが付いている(NULL 値を含んだカラムとは異なる)。文字列カラムで後続の空白を取り除いた後の長さがゼロになった場合、または数値カラムの値がゼロである場合は、それらのカラムがビットマップでマークされ、ディスクに保存されない。空白でない文字列は、長さが記録されたバイトに文字列の内容を付加して保存される。

  • 通常は、使用するディスク領域が固定長テーブルに比べてはるかに少ない。

  • 各レコードは、要求されただけの領域を使用する。レコードが大きくなると、必要に応じてそのレコードが断片に分割される。その結果、レコードのフラグメント化が生じる。

  • レコードの更新によってレコードの長さが拡張されると、そのレコードがフラグメント化される。この場合は、myisamchk -r をときどき実行して、パフォーマンスを高める必要がある。一部の統計情報には、myisamchk -ei tbl_name を使用する。

  • レコードが細かくフラグメント化され、リンク(フラグメント)が失われている可能性があるため、クラッシュ後の再構築は容易ではない。

  • 動的なサイズのレコードについては、予期されるレコードの長さを次の方法で算出できる。

    3
    + (フィールド数 + 7) / 8
    + (char カラムの数)
    + 数値カラムをパックしたサイズ
    + 文字列の長さ
    + (NULL カラムの数 + 7) / 8
    

    各リンクには 6 バイトのペナルティがある。動的レコードは、更新によってレコードが拡張されるたびにリンクされる。新しいリンクは少なくともそれぞれ 20 バイトあるため、次回の拡張は同じリンクで行われると考えられる。そうでない場合は、新たなリンクが発生する。リンクの数は、myisamchk -ed でチェックできる。すべてのリンクを削除するには、myisamchk -r を使用する。

7.1.2.3. 圧縮テーブルの特性

これは、オプションの myisampack ツール(ISAM テーブルでは pack_isam)で生成される読み取り専用テーブルです。

  • MySQL ディストリビューションは、MySQL が GPL 版になる前のものも含めて、いずれも myisampack で圧縮されたテーブルを読み取ることができる。

  • 圧縮テーブルはごくわずかなディスク領域しか使用しない。ディスク使用量が最小限に抑えられるため、遅いディスク(CD-ROM など)を使用する場合に適している。

  • 各レコードは別々に圧縮される(アクセスオーバヘッドがきわめて少ない)。レコードのヘッダは、テーブルで最も大きなレコードに応じて固定される(1 ? 3 バイト)。各カラムは異なる方法で圧縮される。圧縮タイプには次のものがある。

    • 通常は、カラムごとに異なるハフマンテーブルが存在する。

    • サフィックス空白の圧縮。

    • プリフィックス空白の圧縮。

    • 0 の数値は 1 ビットで格納する。

    • 値の範囲が小さい整数カラムは、可能な限り小さな型を使って格納する。たとえば、BIGINT カラム(8 バイト)のすべての値が 0 ? 255 の範囲内にある場合は、このカラムを TINYINT カラム(1 バイト)として格納する。

    • カラムの可能値が少ない場合は、カラムの型を ENUM に変換する。

    • 上記の圧縮を組み合わせて使用することもできる。

  • 固定長または可変長のレコードを処理できる。

  • myisamchk で圧縮を解除できる。

7.1.3. MyISAM テーブルの問題

MySQL がデータの格納に使用するファイル形式は広範な検査を受けていますが、データベーステーブルの破損を招きかねない状況は常に存在します。

7.1.3.1. MyISAM テーブルが破損した場合

MyISAM は信頼性の高いテーブル形式ですが(テーブルに対するすべての変更は SQL ステートメントから制御が戻る前に書き込まれます)、それでも以下の状況が発生した場合はテーブルが破損するおそれがあります。

  • 書き込みの途中で mysqld プロセスが強制終了された場合。

  • コンピュータが予期せずシャットダウンされた場合(コンピュータの電源が切られた場合など)。

  • ハードウェアエラー。

  • 稼働中のテーブルで外部プログラム(myisamchk など)を使用した場合。

  • MySQL または MyISAM コードのソフトウェアバグ。

テーブルが破損すると、一般に次のような現象が見られます。

  • テーブルからデータを選択するときに、Incorrect key file for table: '...'. Try to repair it というエラーが表示される。

  • クエリがテーブルでレコードを検出できない、または不完全なデータを返す。

テーブルが破損していないかどうかは、CHECK TABLE コマンドで確認できます。 See 項4.5.4. 「CHECK TABLE 構文」

破損したテーブルは、REPAIR TABLE で修復できます。 See 項4.5.5. 「REPAIR TABLE 構文」。 また、mysqld が稼働していないときに、myisamchk コマンドを使ってテーブルを修復することもできます。 myisamchk syntax

テーブルが大きく破損している場合は、原因を突き止める必要があります。 See 項A.4.1. 「MySQL が何度もクラッシュする場合に行うこと」

この場合に最も重要なのは、mysqld が強制終了されたときにテーブルが破損したのかを確認することです(これは、mysqld エラーファイルに restarted mysqld という行が最近記録されたかどうかをチェックすることで簡単に検証できます)。これが該当しない場合は、その破損のテストケースを作成してみる必要があります。 See 項E.1.6. 「テーブルが破損した場合にテストケースを作成する」

7.1.3.2. クライアントがテーブルを使用している、またはテーブルを適切に閉じていない

MyISAM .MYI ファイルのヘッダには、テーブルが適切に閉じられているかをチェックするためのカウンタがあります。

CHECK TABLE または myisamchk から次の警告が返されることがあります。

# clients is using or hasn't closed the table properly

これは、このカウンタがずれていることを意味します。テーブルの破損を意味しているわけではありませんが、少なくともテーブルを検査して問題がないことを確認する必要があります。

カウンタの仕組みは次のとおりです。

  • MySQL でテーブルが最初に更新されるときに、インデックスファイルのヘッダ内にあるカウンタが増加する。

  • その後の更新では、カウンタは変更されない。

  • FLUSH によって、またはテーブルキャッシュに空きがないために、テーブルの最後のインスタンスが閉じられると、それまでにテーブルが一箇所でも更新されていればカウンタが減少する。

  • テーブルを修復するか、テーブルを検査して問題がなかった場合は、カウンタがゼロにリセットされる。

  • テーブルを検査する他のプロセスとの相互作用に伴う問題を回避するため、カウンタがゼロである場合は、テーブルを閉じる際にもカウンタが減少しない。

つまり、カウンタがずれる可能性があるのは、次の場合に限られます。

  • MyISAM テーブルが LOCK および FLUSH TABLES を使わずにコピーされた。

  • 更新されてから閉じられるまでの間に MySQL がクラッシュした(ただし、MySQL は各ステートメントで生じたすべての書き込みを次のステートメントまでに発行するため、テーブルが無事である可能性もある)。

  • mysqld が使用していたテーブルで、第三者が myisamchk --recover または myisamchk --update-state を実行した。

  • ある mysqld サーバが使用しているテーブルに対し、別の mysqld サーバが REPAIR または CHECK を実行した。この場合、CHECK は実行しても問題ない(ただし他のサーバから警告を受ける)が、REPAIR は避ける必要がある。現時点では、REPAIR を実行するとデータファイルが新しいファイルで置換され、それが他のサーバに通知されないからである。

7.2. MERGE テーブル

MERGE テーブルは、MySQL バージョン 3.23.25 で新たに導入されました。コードはまだガンマ版ですが、比較的安定しているはずです。

MERGE テーブル(MRG_MyISAM テーブルとも呼ばれます)は、1 つのテーブルとして使用できる同一の MyISAM テーブルの集合です。テーブルの集合には、SELECTDELETEUPDATE のみを実行できます。MERGE テーブルに対して DROP を実行すると、MERGE の仕様のみが破棄されます。

WHERE なしで DELETE FROM merge_table を使用すると、テーブルに対するマッピングのみが消去され、マップされたテーブルの内容は削除されないことに注意してください(これは 4.1 で修正する予定です)。

同一のテーブルとは、すべてのテーブルが同一のカラムおよびキー情報で作成されていることを意味します。カラムのパック方法が異なるテーブル、保持するカラムがまったく同じでないテーブル、あるいはキーの順序が異なるテーブルはマージできません。ただし、一部のテーブルは myisampack で圧縮できます。 See 項4.8.4. 「myisampack(MySQL 圧縮読み取り専用テーブルジェネレータ)」

MERGE テーブルを作成すると、.frm テーブル定義ファイルおよび .MRG テーブルリストファイルが作成されます。.MRG には、1 つのインデックスファイルとして使用される複数のインデックスファイル(.MYI ファイル)のリストのみが含まれています。4.1.1 より前のバージョンでは、使用されるすべてのテーブルを MERGE テーブルと同じデータベースに配置する必要がありました。

今のところ、MERGE テーブルにマップするテーブルに対しては、SELECTUPDATEDELETE の各特権が必要です。

MERGE テーブルには、次のような効果があります。

  • 一連のログテーブルを簡単に管理できる。たとえば、各月のデータを別々のファイルに収録し、それらの一部を myisampack で圧縮した後に、MERGE を作成してそれらを 1 つのファイルとして使用することができる。

  • 処理速度が速くなる。大きな読み取り専用テーブルをいくつかの基準に基づいて分割し、分割した各部分を別々のディスクに配置できる。この場合、MERGE テーブルを作成すれば、大きなテーブルを使用する場合に比べてはるかに処理が速くなる(RAID を使用した場合も同じ効果が得られる)。

  • より効率的に検索できる。処理の対象を正確に把握していれば、一部のクエリに対して分割されたテーブルの 1 つで検索を行い、その他のクエリに対して MERGE を使用することができる。多数のさまざまな MERGE テーブルをアクティブにすることもできる。この場合、ファイルが重複していてもかまわない。

  • より効率的に修復できる。1 つの大きなファイルを修復するより、MERGE ファイルにマップされたファイルを個別に修復する方が簡単である。

  • 多数のファイルを 1 つのファイルとして即座にマップできる。MERGE テーブルは、個々のテーブルのインデックスを使用する。MERGE テーブル独自のインデックスを保持する必要はない。このため、MERGE テーブルの集合の作成または再マップにはほとんど時間がかからない。MERGE テーブルを作成するときは、キー定義を指定する必要があることに注意する。

  • 一連のテーブルをオンデマンドまたはバッチで大きなテーブルに結合する場合は、代わりにそれらのテーブルに対してオンデマンドで MERGE テーブルを作成した方がよい。この方が時間がかからず、ディスク領域も大幅に節約できる。

  • オペレーティングシステムのファイルサイズ制限を回避できる。

  • 1 つのテーブル上で 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 項5.2.1. 「EXPLAIN 構文(SELECT に関する情報の取得)」

  • "開いている" MERGE テーブルによってマップされているテーブルには、DROP TABLEALTER TABLEWHERE 節なしの DELETE FROM table_nameREPAIR TABLETRUNCATE TABLEOPTIMIZE 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=(...) を使用する。

  • .MRG ファイルを変更し、MERGE テーブルとこれを構成するすべてのテーブルに対して FLUSH TABLE を発行することで、ストレージエンジンが新しい定義ファイルを読み取るようにする。

7.2.1. 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 TABLEUNIQUE キーを通常のキーの前に配置するためである。

  • Windows では、MERGE テーブルが使用しているテーブルに対して DROP TABLE を実行できない。これは、MERGE ストレージエンジンが実行するテーブルマッピングを MySQL の上位レイヤが認識しないためである。Windows では開いているファイルを破棄できないため、対象のテーブルを破棄する前に、FLUSH TABLES ですべての MERGE テーブルをフラッシュするか、MERGE テーブルを破棄する必要がある。これについては、ビューを導入するときに修正する。

7.3. ISAM テーブル

MySQL バージョン 5.0 では、ISAM テーブル型が廃止されます。MySQL 4.1 では、このテーブル型がソースに含まれていてもコンパイルできません。このテーブルハンドラの実装としては MyISAM の方が優れているため、できるだけ早く ISAM テーブルを MyISAM テーブルに変換する必要があります。

ISAM は、B ツリーインデックスを使用します。インデックスは .ISM 拡張子の付いたファイルに保存され、データは .ISD 拡張子の付いたファイルに保存されます。ISAM テーブルは、isamchk ユーティリティで検査および修復することができます。 See 項4.5.6.7. 「myisamchk を使用したクラッシュのリカバリ」

ISAM には、次の機能と特性があります。

  • 圧縮された固定長のキー。

  • 固定および可変のレコード長。

  • 16 個のキーと 1 キー当たり 16 個の部品。

  • 最大キー長は 256 バイト(デフォルト)。

  • マシンの形式でデータを格納するため、高速だがマシンと OS に依存する。

MyISAM テーブルに当てはまることは、ほとんどの場合 ISAM テーブルにも当てはまります。See 項7.1. 「MyISAM テーブル」MyISAM テーブルとの主な相違点は次のとおりです。

  • ISAM テーブルでは、OS/プラットフォームをまたがってバイナリを移植できない。

  • 4 GB を超えるテーブルを処理できない。

  • 文字列でのプリフィックスの圧縮のみをサポートする。

  • キーの制限値が小さい。

  • 動的なテーブルがより頻繁にフラグメント化される。

  • テーブルの圧縮に、myisampack ではなく pack_isam が使用される。

ISAM テーブルを MyISAM テーブルに変換して mysqlcheck などのユーティリティを使用できるようにするには、ALTER TABLE ステートメントを使用します。

mysql> ALTER TABLE tbl_name TYPE = MYISAM;

組み込み式の MySQL では、ISAM テーブルがサポートされません。

7.4. 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 カラムをサポートしない。

  • MySQL 4.0.2 より前のバージョンでは、HEAPNULL カラムのインデックスをサポートしない。

  • HEAP テーブルでは一意でないキーを使用できる(ハッシュテーブルではあまり使用されない)。

  • HEAP テーブルは、他のテーブルと同様にすべてのクライアント間で共有される。

  • 次のエントリを順番に検索できない(つまり、インデックスを使って ORDER BY を実行できない)。

  • HEAP テーブルのデータは、小さなブロックで割り当てられる。テーブルは 100% 動的である(挿入時)。オーバフローエリアも余分なキースペースも必要ない。削除されたレコードはリンクされたリストに保存され、新しいデータをテーブルに挿入する際に再使用される。

  • 同時に使用するすべての HEAP テーブルに対して十分な追加メモリが必要。

  • メモリを解放するには、DELETE FROM heap_tableTRUNCATE heap_table、または DROP TABLE heap_table を実行する必要がある。

  • MySQL では、2 つの値の間に存在するおおよそのレコード数を確認できない(この値は、使用するインデックスを範囲オプティマイザが決定するために使用する)。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 です。

7.5. InnoDB テーブル

7.5.1. 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 の商用ライセンスを購入する必要があります。

7.5.2. MySQL バージョン 3.23 での InnoDB

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 項2.2.9. 「MySQL バイナリディストリビューションのインストール」。 See 項4.8.5. 「mysqld-max(拡張 mysqld サーバ)」

InnoDB をサポートする MySQL をコンパイルするには、MySQL-3.23.34a 以降のバージョンを http://www.mysql.com/ からダウンロードし、--with-innodb オプションで MySQL をコンフィギャします。MySQL ソースディストリビューションのインストールについては、MySQL のマニュアルを参照してください。 See 項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.cnfinnodb_data_home_dir を指定しない場合は、デフォルトで MySQL の datadir にこれらのファイルが作成されます。innodb_data_home_dir を空の文字列として指定すれば、innodb_data_file_path でデータファイルへの絶対パスを指定できます。

これを最小限の方法で変更するには、[mysqld] セクションに次の行を追加します。

innodb_data_file_path=ibdata:30M

ただし、高いパフォーマンスを得るには、推奨された方法でオプションを指定するのが最善です。 See 項7.5.3. 「InnoDB 起動オプション」

7.5.3. InnoDB 起動オプション

MySQL バージョン 3.23 で InnoDB テーブルを有効にする方法については、項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_pathibdata1 のサイズとして明示的に指定する必要があります。これで、新たなデータファイルを 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_logfile0ib_logfile1 が、MySQL の datadir(一般には /mysql/data)に作成されます。 また、アーカイブされた小さな InnoDB ログファイル ib_arch_log_0000000000datadir に作成されます。

[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 での規則は次のとおりです。

  • my.cnf または my.ini の一方のみを作成する必要がある。

  • my.cnf ファイルは、C:\my.cnf にする必要がある。

  • my.ini ファイルは、C:\WINDOWSC:\WINNT などの %WINDIR% ディレクトリなどに配置する必要がある。MS-DOS のSET コマンドを使用すれば、%WINDIR% の値を出力できる。

  • PC で使用するブートローダのブートドライブが C: ドライブではない場合は、my.ini ファイルしか使用できない。

Unix では、どこでオプションを指定すればいいでしょうか。 Unix では、mysqld が次のファイル(存在する場合)から次の順序でオプションを読み取ります。

  • /etc/my.cnf グローバルオプション

  • COMPILATION_DATADIR/my.cnf サーバ固有のオプション

  • defaults-extra-file --defaults-extra-file=... で指定されたファイル

  • ~/.my.cnf ユーザ固有のオプション

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 でこのようなデータファイルを指定する方法については、マニュアルで 項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_table4.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_dirInnoDB ログファイルへのディレクトリパス。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_sizeInnoDB がディスク上のログファイルにログを書き出すために使用するバッファのサイズ。実際的な値は 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_sizeInnoDB が、そのテーブルのデータやインデックスをキャッシュするために使用するメモリバッファのサイズ。この値が大きいほど、テーブル内のデータへのアクセスに必要なディスク I/O が少なくなる。データベース専用サーバでは、このパラメータをマシンの物理メモリの 80% にまで設定できる。ただし、物理メモリの競合によってオペレーティングシステムでページングが発生する可能性があるため、あまり大きな値は設定しないようにする。
innodb_buffer_pool_awe_mem_mb32 ビット版 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_sizeInnoDB がデータディクショナリの情報とその他の内部データ構造を格納するために使用するメモリプールのサイズ。このパラメータの実際的な値は 2M であるが、アプリケーションで使用するテーブルの数が多いほど、この値を大きくする必要がある。InnoDB は、このプールのメモリを使い果たすと、オペレーティングシステムからメモリを割り当てるようになり、MySQL エラーログに警告メッセージを書き込む。
innodb_file_io_threadsInnoDB におけるファイル I/O スレッドの数。通常、この値は 4 にする必要があるが、Windows ではこれより大きな数を指定するとディスク I/O の面で有利になる場合がある。
innodb_lock_wait_timeoutロック待機の状態になった InnoDB トランザクションがロールバックされるまでのタイムアウト時間(秒)。InnoDB は、そのロックテーブルでトランザクションのデッドロックを自動的に検出し、そのトランザクションをロールバックする。LOCK TABLES コマンドを使用するか、または同じトランザクションで InnoDB 以外のトランザクションセーフのストレージエンジンを使用すると、InnoDB が検出できないデッドロックが発生することがある。タイムアウトは、このような状況の解決に役立つ。
innodb_flush_method3.23.40 より使用可能。デフォルト値は fdatasync。これ以外に、O_DSYNC を指定できる。
innodb_force_recovery警告: このオプションは、破損したデータベースからテーブルをダンプする必要がある緊急事態でのみ定義する必要がある。指定可能な値は 1 ? 6。それぞれの値の意味については、この後の「強制的なリカバリ」を参照。InnoDB では、安全対策として、このオプションが 0 より大きいときはユーザがデータを変更できないようになっている。このオプションは、バージョン 3.23.44 より使用可能。

7.5.4. InnoDB テーブルスペースの作成

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

7.5.4.1. データベース作成時に問題が生じた場合

InnoDB がファイル操作中にオペレーティングシステムエラーを出力した場合、通常は次のいずれかが原因です。

  • InnoDB のデータディレクトリまたはログディレクトリを作成しなかった。

  • mysqld に、これらのディレクトリでファイルを作成する権利がない。

  • mysqld が正しい my.cnf または my.ini ファイルを読み取らなかったために、ユーザが指定したオプションを認識していない。

  • ディスクがいっぱいか、またはディスククォータが超過している。

  • 作成したサブディレクトリの名前が、指定したデータファイルと同じになっている。

  • innodb_data_home_dir または innodb_data_file_path に構文エラーがある。

InnoDB データベースの作成で問題が生じた場合は、InnoDB が作成したすべてのファイルを削除する必要があります。つまり、MySQL データベースディレクトリから、すべてのデータファイル、すべてのログファイル、アーカイブされた小さなログファイルを削除し、InnoDB テーブルを作成した場合は、これらのテーブルに対応する .frm ファイルも削除します。これで、InnoDB データベースを再び作成することができます。

7.5.5. 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 データベースディレクトリ testCUSTOMER.frm ファイルが作成されます。 内部では、InnoDB が独自のデータディクショナリに 'test/CUSTOMER' テーブルのエントリを追加します。したがって、MySQL の別のデータベースに同じ CUSTOMER という名前のテーブルを作成しても、InnoDB 内部でテーブル名が衝突することはありません。

TYPE = InnoDB で作成した任意のテーブルに対して MySQL のテーブルステータスコマンドを発行することで、InnoDB テーブルスペースの空き容量を照会できます。テーブルスペースの空き容量は、SHOW の出力のテーブルコメントセクションに表示されます。次に例を示します。

SHOW TABLE STATUS FROM test LIKE 'CUSTOMER';

SHOW が InnoDB テーブルについて出力する統計情報は概算であることに注意してください。これらの情報は、SQL 文解析の最適化で使用されます。ただし、テーブルとインデックスに予約されているサイズ(バイト単位)は正確です。

7.5.5.1. MyISAM テーブルから InnoDB への変換

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 ファイルを削除したうえで、再度ジョブを実行した方が得策です。

7.5.5.2. 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'

外部キー制約は、出力のテーブルコメントに列挙されます。

7.5.5.3. 複数テーブルスペース - 各テーブルを独自の .ibd ファイルに入れる

重要: 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 ファイル内に、トランザクションが実行してコミットされていない変更がない。

  • .ibd ファイルにマージされていない挿入バッファエントリがない。

  • 削除マークされたすべてのインデックスレコードが .ibd ファイルから削除されている。

  • mysqld によって、.ibd ファイルの変更されたすべてのページがバッファプールからファイルにフラッシュされている。

このような .ibd ファイルのクリーンバックアップは、次の方法で作成できます。

  • mysqld サーバのすべての活動を停止し、すべてのトランザクションをコミットする。

  • SHOW INNODB STATUS 文でデータベースにアクティブなトランザクションがないことが示され、InnoDB のメインスレッドが Waiting for server activity になるのを待つ。これで、.ibd ファイルのコピーを作成できる。

クリーンな .ibd ファイルを作成するもう 1 つの方法(有償)があります。

  • InnoDB ホットバックアップを使用して InnoDB のデータをバックアップする。

  • 新たな mysqld サーバをバックアップしたファイルを使用するようにして起動し、バックアップ内の .ibd ファイルをクリーンアップする。

TODO には、クリーンな .ibd ファイルも別の MySQL/InnoDB に移動できるようにすることが挙げられています。そのためには、.ibd ファイル内でトランザクション ID とログシーケンス番号をリセットすることが必要になります。

7.5.6. InnoDB データファイルとログファイルの追加と削除

バージョン 3.23.50 および 4.0.2 より、InnoDB の最後のデータファイルに autoextend を指定できるようになりました。あるいは、追加のデータファイルを指定してテーブルスペースを拡大することができます。そのためには、MySQL サーバをシャットダウンし、my.cnf ファイルで innodb_data_file_path末尾に新しいデータファイルを追加し、MySQL サーバを再起動します。

現時点では、InnoDB からデータファイルを削除することはできません。データベースのサイズを小さくするには、mysqldump ですべてのテーブルをダンプし、新しいデータベースを作成し、そのデータベースにテーブルをインポートする必要があります。

InnoDB ログファイルの数またはサイズを変更する場合は、MySQL をシャットダウンし、エラーなくシャットダウンすることを確認する必要があります。 その後に、シャットダウンで問題が発生した場合に備えて、古いログファイルを安全な場所にコピーします。これらはデータベースをリカバリする際に必要となります。古いログファイルをログファイルディレクトリから削除し、my.cnf を編集してから MySQL を再び起動します。起動時に、InnoDB から新しいログファイルを作成していることが通知されます。

7.5.7. InnoDB データベースのバックアップとリカバリ

安全なデータベース管理の秘訣は、定期的にバックアップを取ることです。

InnoDB ホットバックアップは、稼働中の InnoDB データベースをバックアップするためのオンラインバックアップツールです。InnoDB ホットバックアップでは、データベースをシャットダウンする必要がなく、ロックが設定されたり、通常のデータベース処理が妨害されたりすることもありません。InnoDB ホットバックアップは、追加で導入する有償のツールで、標準の MySQL ディストリビューションには含まれていません。詳細情報とスクリーンショットについては、InnoDB ホットバックアップのホームページ(http://www.innodb.com/manual.php)を参照してください。

MySQL サーバをシャットダウンできる場合は、データベースの 'バイナリ' バックアップを取るために次の作業を行う必要があります。

  • MySQL データベースをシャットダウンし、それがエラーなくシャットダウンすることを確認する。

  • すべてのデータファイルを安全な場所にコピーする。

  • すべての InnoDB ログファイルを安全な場所にコピーする。

  • my.cnf オプション設定ファイルを安全な場所にコピーする。

  • InnoDB テーブルのすべての .frm ファイルを安全な場所にコピーする。

上記のバイナリバックアップに加えて、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 の一般ログファイルからのリカバリを実行します。

7.5.7.1. 強制的なリカバリ

データベースページが壊れた場合は、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 より大きいときはユーザが INSERTUPDATE、または DELETE を実行できないようになっています。

バージョン 3.23.53 および 4.0.4 より、強制リカバリが使用される場合でも、テーブルの DROP または CREATE を実行することができます。ロールバックでクラッシュを引き起こしているテーブルが特定できれば、そのテーブルを破棄できます。 また、問題のある大量インポートまたは ALTER TABLE が原因で暴走したロールバックも、この方法で停止できます。 mysqld プロセスを強制終了し、my.cnf のオプション innodb_force_recovery=3 を使用することで、ロールバックせずにデータベースを正常な状態に戻すことができます。 その後に、ロールバックが暴走する原因となったテーブルを DROP で破棄します。

下に列挙されている各オプションでは、その番号が大きいものがより小さい番号の対策をすべて盛り込んでいます。悪くてもオプション 4 でテーブルをダンプできれば、個々のページが破損しても一部のデータが失われるだけなので比較的安全です。オプション 6 にはより劇的な影響力があります。データベースページが古い状態のまま残るため、B ツリーやその他のデータベース構造へさらに破損が及ぶ可能性があるからです。

  • 1(SRV_FORCE_IGNORE_CORRUPT)では、壊れたページが検出されてもサーバが実行される。SELECT * FROM table で壊れたインデックスレコードやページを飛び越すようにすれば、テーブルをダンプする際に役立つ。

  • 2(SRV_FORCE_NO_BACKGROUND)では、メインスレッドが実行されなくなる。パージの際にクラッシュが発生する場合は、このオプションによってそれが回避される。

  • 3(SRV_FORCE_NO_TRX_UNDO)では、リカバリ後にトランザクションロールバックが実行されない。

  • 4(SRV_FORCE_NO_IBUF_MERGE)では、挿入バッファのマージ操作も実行されなくなる。この操作がクラッシュを引き起こす場合は、実行しない方がよい。また、テーブル統計情報も計算しないようにする。

  • 5(SRV_FORCE_NO_UNDO_LOG_SCAN)では、データベース起動時に UNDO ログが参照されない。InnoDB は、未完了のトランザクションもコミット済みとして扱う。

  • 6(SRV_FORCE_NO_LOG_REDO)では、リカバリに関連してログのロールフォワードが実行されない。

7.5.7.2. チェックポイント

InnoDB には、ファジーチェックポイントと呼ばれるチェックポイントメカニズムが実装されています。InnoDB は、変更されたデータベースページを小規模なバッチ単位でバッファプールからフラッシュします。バッファプールを 1 回のバッチでフラッシュする必要はありません。実際にこれを行うと、ユーザの SQL ステートメントの処理が一時的に停止します。

クラッシュリカバリの際に、InnoDB はログファイルに書き込まれたチェックポイントラベルを検索します。InnoDB は、このラベルより前に実行されたデータベースへの変更が、データベースのディスクイメージにすでに反映されていることを認識しています。 次に InnoDB は、ログファイルでこのチェックポイント以降をスキャンし、ログに記録された変更をデータベースに適用します。

InnoDB はログファイルへの書き込みを循環的に行います。 InnoDB によるリカバリが必要となった場合は、バッファプール内のデータベースページとディスク上のイメージの不一致を引き起こしている全てのコミット済みの変更を、ログファイルから取得できなければなりません。つまり、InnoDB は、ログファイルを循環的に再使用する際に、再使用しようとするログファイルに記録された変更が、ディスク上のデータベースページのイメージにすでに反映されていることを確認する必要があります。そのために InnoDB はチェックポイントを作成する必要があり、それには変更されたデータベースページをディスクにフラッシュする処理が伴います。

これらのことから、ログファイルを大きくしておけば、チェックポイントを実行する際のディスク I/O を削減できると言えます。ログファイルの合計サイズを、バッファプールのサイズ以上に設定することは理に適っています。 ログファイルを大きくした場合の難点として、データベースに適用するログの量が増えるために、クラッシュリカバリに時間がかかるおそれがあります。

7.5.8. InnoDB データベースを別のマシンに移動する

Windows では、InnoDB がデータベース名とテーブル名を内部的に小文字で格納します。バイナリ形式のデータベースを Unix から Windows に、またはその逆に移動するには、すべてのテーブル名とデータベース名を小文字にする必要があります。Unix でこれを簡単に行うには、my.cnf[mysqld] セクションに次の行を追加します。

lower_case_table_names=1

これは、テーブルの作成を開始する前に行います。Windows では、デフォルトで 1 に設定されます。

InnoDB のデータファイルとログファイルは、すべてのプラットフォームでバイナリ互換です。ただし、それらのマシンで浮動小数点数の形式が同じであることが必要です。 InnoDB データベースは、すべての関連ファイルをコピーするだけで移動できます。関連ファイルについては、データベースのバックアップに関する前のセクションを参照してください。マシン間で浮動小数点数の形式が異なっていても、テーブル内で FLOAT または DOUBLE データ型を使用していなければ手順は同じです。つまり、関連ファイルをコピーするだけで済みます。浮動小数点数の形式が異なっている場合に、テーブルで浮動小数点データが使用されているときは、mysqldump および mysqlimport を使用してそれらのテーブルを移動する必要があります。

パフォーマンス上のヒントとして、データをデータベースにインポートするときは、大量のインポートトランザクションによって生成される大きなロールバックセグメントに対応できるだけの十分なテーブルスペースがあると想定して、オートコミットモードをオフにします。 コミットは、テーブル全体またはテーブルのセグメントをインポートした後に実行します。

7.5.9. InnoDB トランザクションモデルとロック

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 でトランザクションを終了します。

7.5.9.1. InnoDB と 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 READSERIALIZABLE のみが提供されていました。

グローバルおよびセッションのトランザクション分離レベルは、次のステートメントで確認できます。

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 に変換される。

7.5.9.2. ロックを取得しない読み取り一貫性

読み取り一貫性とは、InnoDB がそのマルチバージョニング機能を使用して、ある時点でのデータベースのスナップショットをクエリに提示することを意味します。 クエリには、その時点より前にコミットされたトランザクションによる変更のみが示され、その時点より後のトランザクションまたはコミットされていないトランザクションによる変更は示されません。例外として、クエリを発行したトランザクション自体による変更はクエリに示されます。

デフォルトの REPEATABLE READ 分離レベルで実行している場合は、最初の読み取り内容がスナップショトとして保存され、同じトランザクション内のすべての読み取りで、そのスナップショットから内容が読み取られます。より新しい状態に更新するには、現在のトランザクションをコミットし、その後に新たなクエリを発行します。

読み取り一貫性はデフォルトのモードです。このモードでは、InnoDB が SELECT ステートメントを READ COMMITTED および REPEATABLE READ 分離レベルで処理します。読み取り一貫性では、アクセスするテーブルに一切ロックが設定されません。このため、読み取り一貫性がテーブルで実行されているときにも、他のユーザはこれらのテーブルを自由に変更できます。

7.5.9.3. ロックを取得する読み取り SELECT ... FOR UPDATE および SELECT ... LOCK IN SHARE MODE

読み取り一貫性は、状況によっては不便な場合があります。 テーブル CHILD に新しいレコードを追加するために、テーブル PARENT 内にこの子レコードの親がすでに存在することを確認するとします。

仮に、読み取り一貫性でテーブル PARENT を読み取り、このテーブルで子レコードの親の存在を確認したとします。これでテーブル CHILD に子レコードを確実に追加できるでしょうか。できません。この処理の間に他のユーザがテーブル PARENT から親レコードを削除しても気付かないからです。

これに対処するには、共有ロックモード LOCK IN SHARE MODESELECT 文を実行します。

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 がレコードに設定するロックと同じロックが設定されます。

7.5.9.4. ネクストキーロック: ファントムの問題の回避

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 より大きい箇所への挿入が防止されます。

ネクストキーロックを使用して、アプリケーションに一意性のチェックを実装できます。共有モードでデータを読み取り、挿入しようとするレコードに重複が見られなければ、レコードを確実に挿入できます。また、読み取り中は対象となるレコードの後続のレコードにネクストキーロックが設定されて、第三者による重複レコードの挿入を防ぎます。このように、ネクストキーロックによって、テーブル内に存在しないものを 'ロック' することができます。

7.5.9.5. 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 項7.5.15. 「InnoDB テーブルの制限事項」

7.5.9.6. デッドロックの検出とロールバック

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 ステートメントを後から特定できないためです。

7.5.9.7. 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;

7.5.9.8. デッドロックの対処法

デッドロックはトランザクションデータベースにおける従来からの問題ですが、一部のトランザクションを実行できなくなるほど頻繁に発生するのでなければ危険ではありません。通常は、アプリケーションを作成する際に、デッドロックのためにロールバックされたトランザクションを再発行できるようにしておく必要があります。

InnoDB は、自動で行レベルロックを使用します。単一のレコードを挿入または削除するだけのトランザクションでもデッドロックが発生します。それは、これらの操作が実際には原子性を持つものではなく、挿入/削除するレコードのインデックスレコード(おそらくは複数)に自動的にロックを設定するためです。

デッドロックに対処し、デッドロックの発生頻度を減らすための方法を次に示します。

  • MySQL の 3.23.52 以降または 4.0.3 以降のバージョンでは、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 でトランザクションを暗黙的に終了することに注意する。

  • トランザクションを直列化するもう 1 つの方法として、レコードが 1 つしかない補助的な 'セマフォ' テーブルを作成する。各トランザクションは、このレコードを更新してから他のテーブルにアクセスする。こうして、すべてのトランザクションが直列的に実行される。 MySQL のテーブルレベルロックでは、タイムアウトを使ってデッドロックを解決する必要がある。

7.5.10. パフォーマンスチューニングのヒント

  1. Unix の top または Windows のタスクマネージャで表示されたプロセスの CPU 使用率が 70% 未満である場合、おそらくそのプロセスはディスク処理である。トランザクションコミットの数が多すぎるか、バッファプールが小さすぎることが考えられる。 バッファプールは大きくした方がよいが、物理メモリの 80% を超えないようにする。

  2. 複数の変更処理を 1 つのトランザクションにまとめる。InnoDB は、データベースを変更するトランザクションがコミットされるたびにログをディスクにフラッシュする必要がある。一般にディスクの回転速度は最高でも 1 秒間に 167 回転(10000rpmの規格の物の場合)であるため、ディスクがオペレーティングシステムを欺かない限り、コミットの回数も同じく 1 秒間に 167 回に制限される。

  3. 最近コミットされたトランザクションの一部を失ってもかまわない場合は、my.cnf のパラメータ innodb_flush_log_at_trx_commit を 0 に設定できる。InnoDB は、いずれにしても 1 秒間に 1 回ログをフラッシュしようとする。ただし、このフラッシュは保証されない。

  4. ログファイルをバッファプールと同じぐらい大きくする。InnoDB は、ログファイルの最後まで書き込むと、チェックポイントでバッファプールの変更された内容をディスクに書き込まなければならない。ログファイルが小さいと、不必要に何度もディスクへ書き込むことになる。大きなログファイルの難点は、リカバリに時間がかかることである。

  5. ログバッファも大きくする必要がある(8 MB など)。

  6. (3.23.39 以降に関係) Linux および Unix の一部のバージョンでは、Unix の fdatasync やその他の類似する方法を使ってファイルをディスクにフラッシュする際に、かなりの時間がかかる。 InnoDB は、デフォルトで fdatasync 関数を使用する。 データベースへ書き込む際のパフォーマンスが不満であれば、my.cnfinnodb_flush_methodO_DSYNC に設定してもかまわない。ただし、ほとんどのシステムでは O_DSYNC の方が処理が遅くなると思われる。

  7. InnoDB にデータをインポートするときは、autocommit がオン(1)になっていないことを確認する。その場合、挿入のたびにディスクへのログのフラッシュが要求される。 SQL の単純なファイルインポート行の前に次の行を追加する。

    SET AUTOCOMMIT=0;
    

    さらにその後に次の行を追加する。

    COMMIT;
    

    mysqldump のオプション --opt を使用すると、上記のように SET AUTOCOMMIT=0; ... COMMIT; ラッパで囲まなくても、ダンプファイルを取得して InnoDB テーブルに高速でインポートできる。

  8. 大量に INSERT する時の大規模なロールバックに注意する。InnoDB は、挿入バッファを使って挿入時のディスク I/O を削減するが、対応するロールバックではそのようなメカニズムが使用されない。ディスクバウンドのロールバックには、対応する挿入の 30 倍の時間がかかる。データベースのプロセスを強制終了しても、データベース起動時に再度ロールバックが開始されるので役に立たない。ロールバックの暴走を回避するには、ロールバックが CPU とメモリだけで高速に実行されるようにバッファプールを拡大するか、InnoDB データベース全体を削除するしかない。

  9. 他の大規模なディスクバウンドの操作にも注意する。 DROP TABLE または TRUNCATE(MySQL-4.0 以降)を使用してテーブルを空にする。DELETE FROM yourtable は使用しない。

  10. 多数のレコードを挿入する場合は、複数行の INSERT を使用して、サーバとクライアント間の通信にかかるオーバヘッドを軽減する。

    INSERT INTO yourtable VALUES (1, 2), (5, 5);
    

    この方法は、InnoDB だけでなく他のテーブル型へ挿入する場合にも有効である。

7.5.10.1. SHOW INNODB STATUSInnoDB モニタ

バージョン 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 と同じ情報に加えて、各トランザクションが設定したロックに関する情報も出力されます。

出力される情報には、以下に関するデータが含まれます。

  • トランザクションのロックの取得待ち

  • スレッドのセマフォ待ち

  • 保留中のファイル I/O 要求

  • バッファプールの統計情報

  • 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.iUNIV_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 の回数を計算できる。

7.5.11. マルチバージョニングの実装

InnoDB はマルチバージョニングのデータベースであるため、テーブルスペース内の古いバージョンのレコードに関する情報を保持しなければなりません。この情報は、ロールバックセグメントに格納されます。 ロールバックセグメントは Oracle のロールバックセグメントのデータ構造に似ています。

InnoDB は内部的に、データベースに格納されている各レコードに 2 つのフィールドを付加します。 6 バイトのフィールドには、レコードを最後に挿入または更新したトランザクションの識別子が記録されます(トランザクション ID)。削除も内部的には更新として扱われ、レコード内の特別なビットに削除済みとしてマークされます。各レコードには、ロールポインタと呼ばれる 7 バイトのフィールドも付加されます。ロールポインタは、ロールバックセグメントに書き込まれた取り消しログレコードを指します。レコードが更新された場合は、更新前のレコードの内容を再構築するために必要な情報が取り消しログレコードに記録されます。

InnoDB は、ロールバックセグメント内の情報を使用して、トランザクションのロールバックで必要な取り消し操作を実行します。また、読み取り一貫性のために、以前のバージョンのレコードを構築する場合にも、この情報が使用されます。

ロールバックセグメント内の UNDO ログは、挿入用の UNDO ログと更新用の UNDO ログに分かれています。挿入用の UNDO ログは、トランザクションロールバックにのみ必要で、トランザクションがコミットされると直ちに破棄されます。更新用の UNDO ログは、読み取り一貫性でも使用されるため、InnoDB がスナップショットを割り当てたトランザクションが存在しなくなるまで破棄できません。このようなトランザクションでは、読み取り一貫性で以前のバージョンのレコードを構築するために、更新用の UNDO ログの情報が必要となる場合があるからです。

トランザクションは定期的にコミットする必要があります。読み取り一貫性のある物のみを発行するトランザクションも同様です。 そうしないと、InnoDB が更新用 UNDO ログからデータを破棄できなくなり、ロールバックセグメントが大きくなって、テーブルスペースがいっぱいになるおそれがあります。

ロールバックセグメントにおける UNDO ログレコードの物理サイズは、通常、対応する挿入レコードまたは更新レコードよりも小さくなります。ロールバックセグメントに必要な領域を、これらのレコード長を使って、計算できます。

このマルチバージョニングの基本構想では、SQL ステートメントでレコードを削除しても、すぐにはそのレコードがデータベースから物理的に削除されません。 削除に対して書き込まれた更新取り消しログレコードを InnoDB が破棄できるようになった時点で、対応するレコードとそのインデックスレコードがデータベースから物理的に削除されます。この削除操作はパージと呼ばれます。この操作はきわめて高速で、通常は削除を行った SQL ステートメントと同程度の時間しかかかりません。

7.5.12. テーブルとインデックスの構造

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 はこの主キー値を使用して、クラスタードインデックスからレコードを検索します。主キーが長いと、セカンダリインデックスの使用する領域が増えることに注意してください。

7.5.12.1. インデックスの物理構造

InnoDB のすべてのインデックスは B ツリーで、インデックスのレコードはツリーのリーフページに格納されます。インデックスページのデフォルトサイズは 16 KB です。新しいレコードが挿入されると、InnoDB はページの 1/16 を、将来のインデックスレコードの挿入や更新に備えて空けようとします。

インデックスレコードがシーケンシャル(昇順または降順)に挿入されると、インデックスページの約 15/16 までがいっぱいになります。 レコードがランダムに挿入された場合は、ページの 1/2 ? 15/16 までがいっぱいになります。インデックスページの使用容量が 1/2 未満になると、InnoDB はインデックスツリーを縮小してページを解放しようとします。

7.5.12.2. 挿入バッファ

データベースアプリケーションでは、主キーが一意の識別子であり、新しいレコードが主キーの昇順で挿入されることが一般的です。したがって、クラスタードインデックスへの挿入では、ディスクからのランダムな読み取りを必要としません。

一方、セカンダリインデックスは通常一意ではなく、セカンダリインデックスへの挿入は比較的ランダムに行われます。 このため、InnoDB で特別なメカニズムが使用されることなく、多数のランダムなディスク I/O が発生します。

一意でないセカンダリインデックスにインデックスレコードが挿入される場合は、セカンダリインデックスページがすでにバッファプール内にあるかどうかが InnoDB によってチェックされます。すでにある場合は、InnoDB によってインデックスページに直接レコードが挿入されます。バッファプール内にインデックスページがなかった場合は、InnoDB によって特別な挿入バッファ構造にレコードが挿入されます。 挿入バッファは、その全体がバッファプール内に収まるように小さくしてあるため、このバッファへの挿入はきわめて高速です。

挿入バッファは、データベース内のセカンダリインデックスツリーに定期的にマージされます。インデックスツリーの同じページ上で複数の挿入をマージすることで、ディスク I/O を削減できます。 挿入バッファによってテーブルへの挿入速度が最大 15 倍に高められることが測定されています。

7.5.12.3. ハッシュインデックス

データベースのほぼ全体がメインメモリ内に収まる場合に、そのデータベースで最も速くクエリを実行するには、ハッシュインデックスを使用します。InnoDB には、テーブルに定義されたインデックスで実行される検索を監視するメカニズムがあり、ハッシュインデックスの構築がクエリにとって有益であると InnoDB が判断した場合は、自動的にそのインデックスが構築されます。

ただし、ハッシュインデックスは常にテーブルに存在する B ツリーインデックスを基に構築されるので注意が必要です。 InnoDB は、B ツリーインデックス上で検出した検索パターンに応じて、任意の長さのキー(B ツリーに定義されたキー)の先頭部分に、ハッシュインデックスを構築できます。 ハッシュインデックスは部分的であってもかまいません。つまり、B ツリーインデックス全体をバッファプールにキャッシュする必要はありません。InnoDB は、頻繁にアクセスされるインデックスページへの要求に応じてハッシュインデックスを構築します。

ある意味では、柔軟なハッシュインデックスのメカニズムを使用して、InnoDB が十分に余裕のあるメインメモリに適応することで、メインメモリデータベースのアーキテクチャに近づいています。

7.5.12.4. 物理的なレコード構造

  • InnoDB 内の各インデックスレコードには、6 バイトのヘッダが含まれる。このヘッダは、連続するレコードをリンクするためと、行レベルロックで使用される。

  • クラスタードインデックス内のレコードには、すべてのユーザ定義カラムのフィールドが含まれる。これに加えて、トランザクション ID 用の 6 バイトのフィールドと、ロールポインタ用の 7 バイトのフィールドが 1 つずつ含まれている。

  • ユーザがテーブルに主キーを定義していない場合は、クラスタードインデックスの各レコードに 6 バイトのロー ID フィールドも追加される。

  • セカンダリインデックスの各レコードには、クラスタードインデックスキーに対して定義されたすべてのフィールドも含まれる。

  • レコードには、そのレコードの各フィールドへのポインタも含まれる。 レコード内のフィールド長の合計が 128 バイト未満の場合はポインタが 1 バイト、128 バイト以上の場合はポインタが 2 バイトになる。

7.5.12.5. InnoDB での AUTO_INCREMENT カラムの仕組み

データベース起動後に、オートインクリメントカラムが定義されたテーブル T にユーザが最初に挿入を行う際にそのカラムに明示的に値を指定しなかった場合は、InnoDB によって SELECT MAX(auto-inc-column) FROM T が実行され、その結果の値に 1 を加えた値が、このカラムおよびテーブルのオートインクリメントカウンタに割り当てられます。 これを、「テーブル T のオートインクリメントカウンタが初期化された」と表現します。

InnoDB は、新たに作成されたテーブルに対するオートインクリメントカウンタの初期化で、これと同じ手順を実行します。

ユーザが挿入時にオートインクリメントカラムに値 0 を指定すると、InnoDB は値が指定されなかったものとしてレコードを扱うことに注意してください。

オートインクリメントカウンタが初期化された後に、ユーザがオートインクリメントカラムの値を明示的に指定してレコードを挿入した時、その値が現在のカウンタ値より大きい場合は、カウンタは指定された値に設定されます。ユーザが値を明示的に指定しなかった場合は、InnoDB によってカウンタが 1 つ増加され、その新しい値がカラムに割り当てられます。

カウンタから値が割り当てられるときは、オートインクリメントのメカニズムによって、ロックとトランザクションの処理が省略されます。このため、カウンタから数値を取得したトランザクションをロールバックすると、数値の順序にずれが生じることもあります。

ユーザがオートインクリメントカラムに負の値を指定した場合、または、整数型に格納できる最大の整数値より大きな値を指定した場合の、オートインクリメントの動作は定義されていません。

7.5.13. ファイル領域の管理とディスク I/O

7.5.13.1. ディスク I/O

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 システムに送信します。

7.5.13.2. ファイル領域管理

オプション設定ファイルに定義するデータファイルから、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 ツリーインデックスが縮小されます。これによって個々のページまたはエクステントがテーブルスペースに解放されて、他のユーザがその領域を利用できるようになるかどうかは、削除のパターンによって異なります。テーブルを破棄するか、またはテーブルからすべてのレコードを削除すると、他のユーザに確実に領域が解放されます。ただし、削除されたレコードは、トランザクションロールバックまたは一貫した読み取りでそのレコードが必要なくなった後のパージ操作で初めて物理的に削除されることに留意してください。

7.5.13.3. InnoDB テーブルのデフラグメント化

InnoDB テーブルのインデックスでランダムな挿入または削除が行われると、インデックスがフラグメント化されることがあります。 フラグメント化とは、ディスクでのインデックスページの物理的な順序が、ページでのレコードのアルファベット順とかけ離れていること、またはインデックスに割り当てられた 64 ページのブロック内に多数の未使用ページがあることを意味します。

インデックスのスキャンを速くするには、定期的に mysqldump を使ってテーブルをテキストファイルにダンプしてからテーブルを破棄し、ダンプからテーブルを再ロードします。 デフラグメント化のもう 1 つの方法として、テーブル変更操作 ALTER TABLE tablename TYPE=InnoDB を実行します。 これによって、MySQL がテーブルを再構築します。

インデックスへの挿入が常に昇順で行われ、レコードが必ず末尾から削除される場合は、InnoDB のファイル領域管理アルゴリズムによってインデックスのフラグメント化が発生しないことが保証されます。

7.5.14. エラー処理

InnoDB でのエラー処理は、必ずしも SQL 標準に明記されているとおりではありません。SQL-99 では、SQL ステートメントでエラーが発生した場合は、そのステートメントでロールバックを実行するように記述されています。InnoDB では、ステートメントの一部のみ、またはトランザクション全体がロールバックされることがあります。 次のリストは、InnoDB でのエラー処理の仕様です。

  • テーブルスペース内でファイル領域を使い果たすと、MySQL の 'Table is full' エラーが発生し、InnoDB が SQL ステートメントをロールバックする。

  • トランザクションデッドロックまたはロック待ちのタイムアウトが発生すると、InnoDB がトランザクション全体をロールバックする。

  • 重複キーエラーが発生した場合は、INSERT INTO ... SELECT ... のようなステートメント内であっても、その特定のレコードの挿入のみがロールバックされる。 この仕様は、SQL ステートメントに IGNORE オプションを指定しなかった場合のみステートメントがロールバックされるように変更される予定。

  • 'row too long' エラーが発生した場合は、SQL ステートメントがロールバックされる。

  • その他のエラーは主に MySQL のコードレイヤによって検出され、検出された場合は対応する SQL ステートメントがロールバックされる。

7.5.15. InnoDB テーブルの制限事項

  • InnoDB テーブルはフルテキストインデックスをサポートしない。

  • Windows では、InnoDB がデータベース名とテーブル名を内部的に常に小文字で格納する。バイナリ形式のデータベースを Unix と Windows の間で移動するには、すべてのテーブル名とデータベース名を小文字にする必要がある。

  • 警告: MySQL システムテーブル(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 テーブルではサポートされない。

  • MySQL の LOCK TABLES 操作では、すでに完了した SQL ステートメントで設定された InnoDB の行レベルロックが考慮されない。つまり、あるテーブルで他のユーザのトランザクションが行レベルロックを設定していても、そのテーブルでテーブルロックを取得できる。したがって、テーブルで実行した操作が他のユーザのロックと衝突した場合は、操作が待機状態になる可能性がある。また、デッドロックが発生する可能性もある。ただし、InnoDB によって設定される行レベルロックでは常に完全性が配慮されているため、デッドロックによってトランザクションの完全性が損なわれることはない。 また、テーブルロックのために、他のトランザクションはテーブル上で(矛盾するロックモードで)行レベルロックを追加で取得できなくなる。

  • 1 つのテーブルに作成できるカラムは 1,000個 までである。

  • DELETE FROM TABLE ではテーブルが再生成されないが、代わりにすべてのレコードが 1 つずつ削除される。この処理はそれほど速くない。MySQL の将来のバージョンでは、処理の速い TRUNCATE を使用できるようになる。

  • InnoDB のデフォルトのデータベースページサイズは 16 KB。コードを再コンパイルすることで、このサイズは 8 KB ? 64 KB に設定できる。 InnoDB の 3.23.40 以下のバージョンでは、レコードの最大長がデータベースページの半分よりもやや小さい。ソースリリース 3.23.41 より、BLOB および TEXT カラムを 4 GB 未満にすることが可能となった。レコードの合計の長さも 4 GB 未満でなければならない。InnoDB は、サイズが 128 バイト以下のフィールドを個別のページに格納しない。InnoDB が長いフィールドを別のページに格納することでレコードを変更した場合、レコードの残りの長さはデータベースページの半分より小さくなければならない。キーの最大長は 7,000 バイトである。

  • 一部のオペレーティングシステムでは、データファイルが 2 GB 未満でなければならない。ログファイルを結合した大きさは、4 GB 未満でなければならない。

  • テーブルスペースの最大サイズは、40 億 データベースページ。これはテーブルの最大サイズでもある。テーブルスペースの最小サイズは 10 MB。

  • MySQL サーバを再起動したときに、InnoDB が AUTO_INCREMENT カラムの古い値を再使用する場合がある。

  • InnoDB では、AUTO_INCREMENT カラムの最初の値を CREATE TABLE ... AUTO_INCREMENT=...(または ALTER TABLE ...)で設定できない。最初の値を設定するには、1 を差し引いた値を持つダミーのレコードを挿入し、その後でダミーのレコードを削除する。

7.5.16. InnoDB の変更履歴

7.5.16.1. MySQL/InnoDB-4.1.1(2003 年 12 月 4 日)
7.5.16.2. MySQL/InnoDB-4.0.16(2003 年 10 月 22 日)
7.5.16.3. MySQL/InnoDB-3.23.58(2003 年 9 月 15 日)
7.5.16.4. MySQL/InnoDB-4.0.15(2003 年 9 月 10 日)
7.5.16.5. MySQL/InnoDB-4.0.14(2003 年 7 月 22 日)
7.5.16.6. MySQL/InnoDB-3.23.57(2003 年 6 月 20 日)
7.5.16.7. MySQL/InnoDB-4.0.13(2003 年 5 月 20 日)
7.5.16.8. MySQL/InnoDB-4.1.0(2003 年 4 月 3 日)
7.5.16.9. MySQL/InnoDB-3.23.56(2003 年 3 月 17 日)
7.5.16.10. MySQL/InnoDB-4.0.12(2003 年 3 月 18 日)
7.5.16.11. MySQL/InnoDB-4.0.11(2003 年 2 月 25 日)
7.5.16.12. MySQL/InnoDB-4.0.10(2003 年 2 月 4 日)
7.5.16.13. MySQL/InnoDB-3.23.55(2003 年 1 月 24 日)
7.5.16.14. MySQL/InnoDB-4.0.9(2003 年 1 月 14 日)
7.5.16.15. MySQL/InnoDB-4.0.8(2003 年 1 月 7 日)
7.5.16.16. MySQL/InnoDB-4.0.7(2002 年 12 月 26 日)
7.5.16.17. MySQL/InnoDB-4.0.6(2002 年 12 月 19 日)
7.5.16.18. MySQL/InnoDB-3.23.54(2002 年 12 月 12 日)
7.5.16.19. MySQL/InnoDB-4.0.5(2002 年 11 月 18 日)
7.5.16.20. MySQL/InnoDB-3.23.53(2002 年 10 月 9 日)
7.5.16.21. MySQL/InnoDB-4.0.4(2002 年 10 月 2 日)
7.5.16.22. MySQL/InnoDB-4.0.3(2002 年 8 月 28 日)
7.5.16.23. MySQL/InnoDB-3.23.52(2002 年 8 月 16 日)
7.5.16.24. MySQL/InnoDB-4.0.2(2002 年 7 月 10 日)
7.5.16.25. MySQL/InnoDB-3.23.51(2002 年 6 月 12 日)
7.5.16.26. MySQL/InnoDB-3.23.50(2002 年 4 月 23 日)
7.5.16.27. MySQL/InnoDB-3.23.49(2002 年 2 月 17 日)
7.5.16.28. MySQL/InnoDB-3.23.48(2002 年 2 月 9 日)
7.5.16.29. MySQL/InnoDB-3.23.47(2001 年 12 月 28 日)
7.5.16.30. MySQL/InnoDB-4.0.1(2001 年 12 月 23 日)
7.5.16.31. MySQL/InnoDB-3.23.46(2001 年 11 月 30 日)
7.5.16.32. MySQL/InnoDB-3.23.45(2001 年 11 月 23 日)
7.5.16.33. MySQL/InnoDB-3.23.44(2001 年 11 月 2 日)
7.5.16.34. MySQL/InnoDB-3.23.43(2001 年 10 月 4 日)
7.5.16.35. MySQL/InnoDB-3.23.42(2001 年 9 月 9 日)
7.5.16.36. MySQL/InnoDB-3.23.41(2001 年 8 月 13 日)
7.5.16.37. MySQL/InnoDB-3.23.40(2001 年 7 月 16 日)
7.5.16.38. MySQL/InnoDB-3.23.39(2001 年 6 月 13 日)
7.5.16.39. MySQL/InnoDB-3.23.38(2001 年 5 月 12 日)

7.5.16.1. MySQL/InnoDB-4.1.1(2003 年 12 月 4 日)

  • InnoDB で複数テーブルスペースを利用できるようになった。InnoDB 型の各テーブルとそのインデックスを個別の .ibd ファイルに格納し、.frm ファイルの格納先と同じ MySQL データベースディレクトリの下に配置できる。

  • AUTOCOMMIT=0 の場合、またはステートメントが BEGIN ... COMMIT で囲まれている場合は、InnoDB テーブルにも MySQL のクエリキャッシュを使用できるようになった。

  • バッファプールのサイズを 8 MB 未満に設定することで、InnoDB のメモリ消費量を数メガバイト削減できる。

  • Windows でもローデバイスを使用できる。

7.5.16.2. MySQL/InnoDB-4.0.16(2003 年 10 月 22 日)

  • バグ修正: マニュアルでの記述に反し、一意の複合インデックスで一意の完全一致検索条件が使用された場合に、ロックを取得する読み取りで InnoDB が 2 つの行ロックを設定していた。単一カラムの一意のキーでは正しく動作していた。

  • バグ修正: テンポラリテーブルをリカバリする目的で名称変更 #sql... -> rsql... を利用した場合に、InnoDB が row_mysql_lock_data_dictionary() でアサートしていた。

  • MySQL のバグデータベースに、非クリティカルな未解決のバグがいくつか報告されている。これらのバグの修正は、次にリリースされる 4.1.1 にリソースが配分されているために延期された。

7.5.16.3. MySQL/InnoDB-3.23.58(2003 年 9 月 15 日)

  • バグ修正: mysqld 起動後の最初の B ツリーページ分割で、InnoDB がインデックスページディレクトリを破壊する可能性があった。現象としては、page0page.c の関数 page_dir_find_slot() でアサートエラーが発生していた。

  • バグ修正: ごくまれではあるが、ロールバック、パージ、および SELECT が同時に発生した場合に、InnoDB が無関係なレコードを返すことがあった。

  • LOCK TABLES の内部で SELECT が使用された場合に、btr0sea.c ラッチでハングが発生しないように修正された。

  • バグ修正: 単一の DELETE ステートメントが、いくつかのローを削除した後に FOREIGN KEY エラーまたは Table is full エラーで失敗した場合に、MySQL が SQL ステートメント全体をロールバックしていなかった。

7.5.16.4. MySQL/InnoDB-4.0.15(2003 年 9 月 10 日)

  • バグ修正: レコードを更新したことで最大長の 8,000 バイト(BLOB および TEXT なしで)を超過した場合に、InnoDB は単にクラスタードインデックスからレコードを削除していた。同様の挿入で、InnoDB が予約されたファイル領域のエクステントをリークし、それが次の mysqld 起動時まで解放されなかった。

  • バグ修正: ログファイルが比較的小さい場合に大きな BLOB 値を使用すると、大きな BLOB 操作の際に、InnoDB が最新のチェックポイントの後に作成されたログに一時的に上書きする可能性があった。その時点で InnoDB がクラッシュすると、最新のチェックポイントまでログをスキャンできないために、クラッシュリカバリが失敗していた。このバージョンより、InnoDB は最新のチェックポイントが十分に新しいことを保証するようになった。これを保証できない場合、InnoDB は MySQL の .err ログに警告を出力し、ログファイルを拡大するように勧告する。

  • バグ修正: innodb_fast_shutdown=0 を設定しても作用しなかった。

  • 4.0.13 で検出されたバグを修正: CREATE TABLE がコメントで終わっていると、メモリオーバランが発生する可能性があった。

  • バグ修正: Windowsで InnoDB が .err ログに 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 からテーブルの破損が報告されていた。

7.5.16.5. MySQL/InnoDB-4.0.14(2003 年 7 月 22 日)

  • InnoDB が SQL ステートメント SAVEPOINT および ROLLBACK TO SAVEPOINT をサポートするようになった。構文については、http://www.innodb.com/ibman.php#Savepoints を参照。

  • CREATE TABLE t (a BLOB, INDEX (a(10))) のように、カラムプリフィックスキーを作成できるようになった。

  • Linux および FreeBSD の最新バージョンでは、O_DIRECTinnodb_flush_method として使用することもできる。ただし、これらのオペレーティングシステムで予想されるバグに注意する。

  • データページのチェックサム計算が修正された。以前は、OS のファイルシステムの破損がほとんど気付かれなかった。4.0.14 以降のバージョンから 4.0.14 未満のバージョンにダウングレードすると、最初の起動時に InnoDB から次の警告が出力されることに注意する。

    InnoDB: Warning: an inconsistent page in the doublewrite buffer
    InnoDB: space id 2552202359 page number 8245, 127'th page in dblwr buf.
    

    ただし、これは危険ではないので無視してかまわない。

  • バッファプール置換アルゴリズムが変更されて、LRU リストの最後の 10% に置換可能なページがない場合に、変更されたページがフラッシュされるようになった。これによって、プロセスが読み取りと書き込みを行う場合に、ディスク I/O を削減できる。

  • バッファプールのチェックポイントフラッシュアルゴリズムで、フラッシュリストの最後にあるページに隣接するページもフラッシュされるようになった。これによって、データベースのシャットダウンを高速化できると同時に、InnoDB のログファイルがバッファプールに比べてかなり小さい場合にディスクの書き込みを高速化できる。

  • 4.0.13 では、SHOW INNODB STATUS で最新の UNIQUE KEY エラーに関する詳細情報が出力されていたが、この情報を格納すると REPLACE の速度が大きく低下する可能性があった。この情報が格納または出力されなくなった。

  • バグ修正: MySQL レプリケーションで、SET FOREIGN_KEY_CHECKS=0 が適切にレプリケートされていなかった。下位バージョンの 3.23 にはこの修正が移植されない。

  • バグ修正: パラメータ innodb_max_dirty_pages_pct で、バッファプール内の空きページが考慮されていなかった。このため、バッファプール内に多数の空きページがある場合でも、必要以上にフラッシュされることがあった。回避策: SET GLOBAL innodb_max_dirty_pages_pct = 100

  • バグ修正: 大規模なインデックススキャンが行われると、セマフォ待ちが長くなるために、ファイル読み取り要求でリソースが不足し、InnoDB がアサートする可能性があった。

  • バグ修正: AUTOCOMMIT=1 の場合にバイナリロギングがオンになっていないと、MySQL が LOCK TABLES の内部で更新を行う SQL ステートメントの後にコミットを実行できなかった。また、SELECT ステートメントではバイナリロギングの状態に関係なくコミットが実行されなかった。

  • バグ修正: mysqld 起動後の最初の B ツリーページ分割で、InnoDB がインデックスページディレクトリを破壊する可能性があった。現象としては、page0page.cpage_dir_find_slot() 関数でアサートが発生していた。

  • バグ修正: UPDATE CASCADE 節を伴う FOREIGN KEY で、親カラムの内部ストレージ長が子カラムと異なっている場合にカスケードされた更新を実行すると、子テーブルでカラム長が不適切となり、子テーブルが破損していた。MySQL の '暗黙的なカラム仕様の変更' のために固定長の CHAR カラムが内部的に VARCHAR に変更され、このエラーを引き起こす可能性がある。

  • バグ修正: latin1 以外のキャラクタセットが使用された場合、および FOREIGN KEY で親カラムの内部ストレージ長が子カラムと違っている場合に、子テーブルへの挿入がすべて外部キーエラーで失敗していた。

  • バグ修正: InnoDB が、クラスタードインデックスレコードが見つからないというメッセージを返したり、ごくまれではあるが、ロールバック、パージ、および SELECT が同時に発生した場合に無関係なローを返したりすることがあった。

  • LOCK TABLES 内で SELECT が使用された場合に、btr0sea.c ラッチ上でハングが発生しないように修正された。

  • バグ修正: 4.0.13 のリリースノートの記載に反し、MySQL のバイナリロギングがオンであるとグループコミットが動作しなかった。

  • バグ修正: Unix で os_event_wait() が適切に機能していなかったために、各種ログ操作でリソースの枯渇が発生していた。

  • バグ修正: 単一の DELETE ステートメントがいくつかのローを削除した後に FOREIGN KEY エラーまたは 'Table is full error' で失敗した場合に、MySQL は SQL ステートメント全体をロールバックせず、失敗したステートメントをバイナリログに書き込み、その際にゼロ以外の error_code を報告していた。

  • バグ修正: テーブルには最大 1,000 個のカラムを収容できるが、InnoDB が CREATE TABLE でその制限をチェックしていなかったために、テーブルに対する後続の INSERT または SELECT でアサートが発生する可能性があった。

7.5.16.6. MySQL/InnoDB-3.23.57(2003 年 6 月 20 日)

  • innodb_flush_log_at_trx_commit のデフォルト値が 0 から 1 に変更された。この新しいリリースでは、my.cnf でこの値を明示的に指定しないと値 1 が設定されるため、トランザクションコミットのたびにディスクへのログのフラッシュが発生してアプリケーションの実行速度が大幅に低下する。

  • バグ修正: InnoDB で、テーブルが破棄される際に pthread_mutex_destroy() が呼び出されていなかった。そのために、FreeBSD およびその他の Linux 以外の Unix でメモリリークが発生する可能性があった。

  • バグ修正: 空ではないインデックス範囲に対して InnoDB がそのサイズを 0 レコードと推定すると、MySQL から誤って '空集合' が返される可能性があった。また、インデックス範囲が空であると、MySQL はネクストキーロックを実行できなかった。

  • バグ修正: GROUP BY および DISTINCT が NULL 値を不等として扱うことがあった。

7.5.16.7. MySQL/InnoDB-4.0.13(2003 年 5 月 20 日)

  • InnoDBALTER TABLE DROP FOREIGN KEY をサポートするようになった。外部キーを破棄する場合は、SHOW CREATE TABLE を使って、内部で生成された外部キー ID を検出する必要がある。

  • SHOW INNODB STATUS で、最後に検出された FOREIGN KEY エラーおよび UNIQUE KEY エラーの詳細情報が出力されるようになった。InnoDBCREATE TABLE からエラー 150 を返した原因がわからない場合は、このステートメントを使って原因を調査できる。

  • ANALYZE TABLEInnoDB 型のテーブルでも動作するようになった。このステートメントは、各インデックスツリーをランダムに 10 箇所調べ、それに応じてインデックスのカーディナリティの推定値を更新する。これは推定値に過ぎないため、ANALYZE TABLE を実行するたびに異なる数値が生成される可能性があることに注意する。MySQL は、インデックスカーディナリティの推定値を結合の最適化でのみ使用する。適切に最適化されていない結合がある場合は、ANALYZE TABLE を試すことができる。

  • InnoDB のグループコミット機能が、MySQL のバイナリログがオンになっている場合も動作するようになった。グループコミットをアクティブにするには、クライアントスレッドの数が 3 つ以上でなければならない。

  • innodb_flush_log_at_trx_commit のデフォルト値が 0 から 1 に変更された。この新しいリリースでは、my.cnf でこの値を明示的に指定しないと値 1 が設定されるため、トランザクションコミットのたびにディスクへのログのフラッシュが発生してアプリケーションの実行速度が大幅に低下する。

  • 設定可能な MySQL グローバルシステム変数 innodb_max_dirty_pages_pct が追加された。この変数は 0 ? 100 の整数である。 デフォルト値は 90。InnoDB のメインスレッドは、多くてもこのパーセンテージが常にフラッシュされずに残るように、バッファプールからページをフラッシュしようとする。

  • innodb_force_recovery=6 の場合に、InnoDB が破損ページを二重書き込みバッファに基づいて修復しないようになった。

  • バッファプール内のメモリをゼロに設定しないようになったため、InnoDB の起動が速くなった。

  • バグ修正: MySQL のコメント内に 'foreign key' というキーワードがあると、FOREIGN KEY 定義用の InnoDB パーサで混乱が生じていた。

  • バグ修正: FOREIGN KEY で参照されていたテーブルを破棄し、その後に一致しないカラム型で同じテーブルを作成すると、InnoDBdict0load.cdict_load_table() 関数でアサートする可能性があった。

  • バグ修正: GROUP BY および DISTINCTNULL 値を不等として扱うことがあった。 また、インデックスの範囲が空である場合に、MySQL がネクストキーロックを実行できなかった。

  • バグ修正: MyISAM テーブルが更新されたときに現在のトランザクションはコミットされない。このため、バイナリロギングが有効であっても、CREATE TABLE では InnoDB トランザクションがコミットされない。

  • バグ修正: 削除が行われたテーブルは ON DELETE SET NULL で変更できなかったが、これを可能にして、カスケードされた操作で無限ループが生じないようにした。

  • バグ修正: 主キーでの一意の検索でカーソルを位置付けた後でも HANDLER PREV および NEXT を使用できるようになった。

  • バグ修正: MIN() または MAX() によってデッドロックまたはロック待ちのタイムアウトが発生した場合に、MySQL がエラーを返さずに、関数の値として NULL を返していた。

  • バグ修正: テーブルが破棄される際に、InnoDBpthread_mutex_destroy() を呼び出していなかった。そのために、FreeBSD およびその他の Linux 以外の Unix システムでメモリリークが発生する可能性があった。

7.5.16.8. MySQL/InnoDB-4.1.0(2003 年 4 月 3 日)

  • InnoDB が、Windows を実行する 32 ビットの Intel コンピュータでバッファプールメモリを 64 GB までサポートするようになった。これが実現したのは、InnoDB が Windows の AWE 拡張を使用して 32 ビットプロセスの 4 GB の制限を超えるメモリに対応できるようになったため。新しい起動変数 innodb_buffer_pool_awe_mem_mb によって、AWE が有効になり、バッファプールのサイズがメガバイト単位で設定される。

  • バッファヘッダとロックテーブルのサイズが削減された。InnoDB の使用するメモリが 2% 少なくなった。

7.5.16.9. MySQL/InnoDB-3.23.56(2003 年 3 月 17 日)

  • InnoDB のクエリ最適化における主要なバグを修正: SELECT ... WHERE indexcolumn < x および SELECT ... WHERE indexcolumn > x というタイプのクエリで、適切に選択できる場合でもテーブルがスキャンされる可能性があった。

  • MySQL がクエリの途中で TL_IGNORE を指定して store_lock を呼び出す場合に発生していたバグが修正された。

7.5.16.10. MySQL/InnoDB-4.0.12(2003 年 3 月 18 日)

  • クラッシュリカバリの際に、InnoDB がトランザクションロールバックの進行状況をパーセンテージで出力するようになった。

  • バグ/機能修正: mysql_use_result() を使用するアプリケーションプログラムで、2 つ以上の接続を使って SQL クエリを送信すると、btr0sea.c の適応的なハッシュ S-ラッチでデッドロックが発生する可能性があった。これを改善し、mysqldSELECT からのデータをクライアントに渡すたびに S-ラッチを解放するようにした。

  • バグ修正: 空ではないインデックス範囲に対して InnoDB がそのサイズを 0 レコードと推定すると、MySQL から誤って '空集合' が返される可能性があった。また、インデックス範囲が空であると、MySQL はネクストキーロックを実行できなかった。

7.5.16.11. MySQL/InnoDB-4.0.11(2003 年 2 月 25 日)

  • 4.0.10 で検出されたバグを修正: SELECT ... FROM ... ORDER BY ... DESC が、無限ループでハングする可能性があった。

  • 未解決のバグ: MySQL レプリケーションで、SET FOREIGN_KEY_CHECKS=0 が適切にレプリケートされない。

7.5.16.12. MySQL/InnoDB-4.0.10(2003 年 2 月 4 日)

  • MySQL は、INSERT INTO t1 SELECT ... FROM t2 WHERE ... で、t2 にテーブルレベルのリードロックが設定されていた。このロックが設定されなくなった。

  • SHOW INNODB STATUS の最大出力長が 200 KB に拡大された。

  • InnoDB のクエリ最適化における主要なバグを修正: SELECT ... WHERE indexcolumn < x および SELECT ... WHERE indexcolumn > x というタイプのクエリで、適切に選択できる場合でもテーブルがスキャンされる可能性があった。

  • バグ修正: 主キーのインデックスツリーの高さが 1 である BLOB テーブルで、パージによってハングが発生する可能性があった。現象: btr_free_externally_stored_field() に設定された X-ラッチによって発生するセマフォ待ち。

  • バグ修正: 新しいハンドルで InnoDB の HANDLER コマンドを使用すると、ha_innobase::change_active_index()mysqld がクラッシュしていた。

  • バグ修正: MySQL が、SELECT ステートメントの途中でクエリを推定すると、btr0sea.c の適応的なハッシュインデックスラッチで InnoDB がハングする可能性があった。

  • バグ修正: 適応的なハッシュインデックスの検索がパージまたは挿入と同時に実行された場合に、InnoDB がテーブルの破損を報告し、page_dir_find_owner_slot() でアサートする可能性があった。

  • バグ修正: Windows 2000 のあるファイルシステムスナップショットツールによって、InnoDB のファイル書き込みが エラー 33 ERROR_LOCK_VIOLATION で失敗する可能性があった。 同期書き込みで、InnoDB が書き込みを 1 秒間隔で 100 回再試行するようになった。

  • バグ修正: REPLACE INTO t1 SELECT ... が、t1 にオートインクリメントカラムがある場合に機能しなかった。

  • 未解決のバグ: MySQL レプリケーションで、SET FOREIGN_KEY_CHECKS=0 が適切にレプリケートされない。

7.5.16.13. MySQL/InnoDB-3.23.55(2003 年 1 月 24 日)

  • MySQL は、INSERT INTO t1 SELECT ... FROM t2 WHERE ... で、t2 にテーブルレベルの読み取りロックが設定されていた。このロックが設定されなくなった。

  • バグ修正: 32 ビットコンピュータで、InnoDB のログファイルを結合した大きさが 2 GB 以上であると、InnoDB が誤った位置にログを書き込んでいた。このため、クラッシュリカバリと InnoDB ホットバックアップがログスキャンで失敗する可能性があった。

  • バグ修正: インデックスカーソルのリストアが、理論的に失敗する可能性があった。

  • バグ修正: btr0sea.c の関数 btr_search_info_update_slow でのアサートが、3 つのスレッドの競合で理論的に失敗する可能性があった。

  • バグ修正: 主キーのインデックスツリーの高さが 1 である BLOB テーブルで、パージによってハングが発生する可能性があった。現象: btr_free_externally_stored_field() に設定された X-ラッチによって発生するセマフォ待ち。

  • バグ修正: MySQL が、SELECT ステートメントの途中でクエリを推定すると、btr0sea.c の適応的なハッシュインデックスラッチで InnoDB がハングする可能性があった。

  • バグ修正: 適応的なハッシュインデックスの検索がパージまたは挿入と同時に実行された場合に、InnoDB がテーブルの破損を報告し、page_dir_find_owner_slot() でアサートする可能性があった。

  • バグ修正: Windows 2000 のあるファイルシステムスナップショットツールによって、InnoDB のファイル書き込みがエラー 33 ERROR_LOCK_VIOLATION で失敗する可能性があった。 同期書き込みで、InnoDB が書き込みを 1 秒間隔で 100 回再試行するようになった。

  • 未解決のバグ: MySQL レプリケーションで、SET FOREIGN_KEY_CHECKS=0 が適切にレプリケートされない。この修正は 4.0.11 で行われ、3.23 に移植される可能性は低い。

  • InnoDB page0cur.c の関数 page_cur_search_with_match で、InnoDB が同じページから永久的に動かなくなるバグが修正された。 このバグは、テーブルに複数のページがある場合のみ発生する。

7.5.16.14. MySQL/InnoDB-4.0.9(2003 年 1 月 14 日)

  • 警告メッセージ 'InnoDB: Out of memory in additional memory pool' が削除された。

  • バグ修正: 32 ビットコンピュータで、InnoDB のログファイルを結合した大きさが 2 GB 以上であると、InnoDB が誤った位置にログを書き込んでいた。このため、クラッシュリカバリと InnoDB ホットバックアップが失敗する可能性があった。

  • バグ修正: インデックスカーソルのリストアが、理論的に失敗する可能性があった。

7.5.16.15. MySQL/InnoDB-4.0.8(2003 年 1 月 7 日)

  • InnoDB が FOREIGN KEY (...) REFERENCES ...(...) [ON UPDATE CASCADE | ON UPDATE SET NULL | ON UPDATE RESTRICT | ON UPDATE NO ACTION] もサポートするようになった。

  • テーブルとインデックスがテーブルスペース内で確保する領域が 4% 削減された。 また、既存のテーブルが確保する領域も削減された。4.0.8 にアップグレードすると、SHOW TABLE STATUS"InnoDB free" に表示される空き領域が増える。

  • バグ修正: ローの主キーを更新すると、更新されるローの二次キーを参照するすべての外部キーで外部キーエラーが発生していた。また、参照を行う外部キー制約が、インデックスの最初のカラムのみを参照する場合に、そのインデックスに複数のカラムがあると、その他のカラムを更新する際に外部キーエラーが発生していた。

  • バグ修正: 同じカラムを 2 つ含んでいるインデックスでそのカラムが更新されると、テーブルが破損する。InnoDB でこのようなインデックスが作成されないようになった。

  • バグ修正: ロックを取得する SELECT でデッドロックまたはロック待ちタイムアウトが発生した場合に、.err ログから余分なエラー 149 および 150 が出力されないようになった。

  • バグ修正: btr0sea.c の関数 btr_search_info_update_slow でのアサートが、3 つのスレッドの競合で理論的に失敗する可能性があった。

  • バグ修正: セッションのトランザクション分離レベルを REPEATABLE READ から別のレベルに設定すると、REPEATABLE READ に戻すことができなかった。

7.5.16.16. MySQL/InnoDB-4.0.7(2002 年 12 月 26 日)

  • 4.0.7 の InnoDB は、基本的に 4.0.6 と同じ。

7.5.16.17. MySQL/InnoDB-4.0.6(2002 年 12 月 19 日)

  • innodb_log_arch_dir は、MySQL に関係しないため、my.cnf でこのパラメータを指定する必要がなくなった。

  • AUTOCOMMIT=1 モードの LOAD DATA INFILE が、1 MB 分のバイナリログが書き込まれるたびに暗黙的にコミットを実行しなくなった。

  • 4.0.4 で検出されたバグを修正: LOCK TABLES ... READ LOCAL では、読み取られたローに行ロックを設定できなかった。このため、mysqldump でデッドロックやロック待ちタイムアウトが発生していた。

  • 4.0.4 で検出された 2 つのバグを修正: AUTO_INCREMENT で、REPLACE がカウンタを 1 のままにする可能性があった。 デッドロックまたはロック待ちタイムアウトでも同じ問題が発生する可能性があった。

  • バグ修正: テンポラリテーブルで TRUNCATE を実行すると、InnoDB がクラッシュしていた。

  • 4.0.5 で検出されたバグを修正: バイナリロギングをオフにしたまま INSERT INTO ... SELECT ... または CREATE TABLE ... SELECT ... を実行すると、btr0sea.c の 128 行目で作成されたセマフォで InnoDB がハングする可能性があった。 回避策: バイナリログをオンにする。

  • バグ修正: レプリケーションで、マルチステートメントトランザクションの途中に SLAVE STOP を発行すると、SLAVE START がトランザクションの一部しか実行しなくなる可能性があった。スレーブがクラッシュ後に再起動された場合も、同様のエラーが発生する可能性があった。

7.5.16.18. MySQL/InnoDB-3.23.54(2002 年 12 月 12 日)

  • バグ修正: インデックスツリーでの範囲のエンドポイントへのパスがルート内ですでに分岐している場合に、InnoDB で短いインデックス範囲のサイズが大幅に誇張して推定されていた。 このため、SQL クエリで不必要なテーブルスキャンが実行される可能性があった。

  • バグ修正: テーブルに主キーを作成せずに複数のインデックスを定義し、そのうちの少なくとも 1 つをユニークインデックスにして、そのすべてのカラムを NOT NULL として宣言すると、ORDER BY が失敗する可能性があった。

  • バグ修正: ON DELETE CASCADE に関連するロック待ちタイムアウトによって、インデックスが破損する可能性があった。

  • バグ修正: プライマリインデックスから一意のキーを使って SELECT を実行した場合に、検索で一致したレコードに削除マークが付いていると、InnoDB が誤って次のレコードを返す可能性があった。

  • 3.23.53 で検出されたバグを修正: LOCK TABLES ... READ LOCAL では、読み取られたローに行ロックを設定できなかった。このため、mysqldump でデッドロックやロック待ちタイムアウトが発生していた。

  • バグ修正: 同じカラムを 2 つ含んでいるインデックスでそのカラムが更新されると、テーブルが破損する。InnoDB でこのようなインデックスが作成されないようになった。

7.5.16.19. MySQL/InnoDB-4.0.5(2002 年 11 月 18 日)

  • InnoDB がトランザクション分離レベル READ COMMITTED および READ UNCOMMITTED をサポートするようになった。READ COMMITTED は Oracle を厳密にエミュレートしているため、Oracle から MySQL へのアプリケーションの移植が容易になる。

  • デッドロックが選択的に解消されるようになった。つまり、ローの変更または挿入が少ないトランザクションを選んで犠牲にするようにした。

  • 外部キーの定義で、my.cnflower_case_table_names の設定が考慮されるようになった。

  • 外部キー定義で参照されるテーブルが作成されるテーブルと同じデータベースにある場合は、SHOW CREATE TABLE で外部キー定義にデータベース名が出力されない。

  • InnoDB は、インデックスページをデータファイルに書き込む前に、そのほとんどのページに対して整合性チェックを実行する。

  • innodb_force_recovery に 0 より大きい値を設定すると、InnoDB はテーブルに対して SELECT * FROM を実行する際に、破損したインデックスレコードとページをとばす。これはダンプの際に役立つ。

  • InnoDB が、Windows 2000 と XP で再びバッファなしの非同期 I/O を使用するようになった。NT、95/98/ME ではバッファなしのシミュレートされた非同期 I/O のみが使用される。

  • バグ修正: インデックスツリーでの範囲のエンドポイントへのパスがルート内ですでに分岐している場合に、InnoDB で短いインデックス範囲のサイズが大幅に誇張して推定されていた。このため、SQL クエリで不必要なテーブルスキャンが実行される可能性があった。 この修正は、下位の 3.23.54 にも移植される。

  • 3.23.52、4.0.3、4.0.4 で検出されたバグを修正: 一部の Windows 95/98/ME コンピュータで、InnoDB の起動に時間がかかり、クラッシュすることもあった。

  • バグ修正: ロック待ちの後で付与された AUTO-INC ロックが、トランザクションが終了するまで維持されていた。これによって、不必要なデッドロックが発生する可能性があった。

  • バグ修正: SHOW INNODB STATUS、innodb_monitor、または innodb_lock_monitor で、1 回のレポートに数百のトランザクションを出力する必要がある場合に出力が切り捨てられると、srv0srv.c の 1621 行目で作成された mutex に対する多数の待ちをエラーログに出力する際に InnoDB がハングしていた。

  • バグ修正: Unix で SHOW INNODB STATUS を実行すると、平均ファイル読み取りサイズが常に 0 バイトとして報告されていた。

  • 4.0.4 の潜在的なバグを修正: InnoDB が、MyISAM のように ORDER BY ... DESC を実行するようになった。

  • バグ修正: テーブル上でロールバックが実行されているときに DROP TABLE を実行すると、クラッシュまたはハングが発生する可能性があった。これが実際にユーザにとって問題であると見なされた場合のみ、この修正が下位の 3.23 にも移植される。

  • バグ修正: テーブルに主キーを作成せずに複数のインデックスを定義し、そのうちの少なくとも 1 つをユニークインデックスにして、そのすべてのカラムを NOT NULL として宣言すると、ORDER BY が失敗する可能性があった。

  • バグ修正: ON DELETE CASCADE に関連するロック待ちタイムアウトによって、インデックスが破損する可能性があった。

  • バグ修正: プライマリインデックスから一意のキーを使って SELECT を実行した場合に、検索で一致したレコードに削除マークが付いていると、InnoDB が次のレコードを返す可能性があった。

  • 未解決のバグ: 4.0.4 で、AUTO_INCREMENT に関する 2 つのバグが検出された。1 つは REPLACE がカウンタを 1 のままにする可能性があること、もう 1 つはデッドロックまたはロック待ちタイムアウトで同じ問題が発生する可能性があることである。これらは 4.0.6 で修正される。

7.5.16.20. MySQL/InnoDB-3.23.53(2002 年 10 月 9 日)

  • Windows で、データファイルに対してバッファなしのディスク I/O を再び使用するようになった。 通常の I/O では、Windows XP および Windows 2000 での読み取りのパフォーマンスに支障が出ると思われる。

  • 範囲推定機能を調整して、インデックス範囲のスキャンがフルインデックススキャンより優先されるようにした。

  • innodb_force_recovery が設定されている場合でも、テーブルを破棄または作成できるようになった。これを利用して、ロールバックまたはパージでクラッシュを引き起こすテーブルを破棄したり、テーブルインポートが失敗した後のリカバリでロールバックが暴走した場合にテーブルを破棄することができる。

  • 3.23.52、4.0.3、4.0.4 で検出されたバグを修正:一部の Windows 95/98/ME コンピュータで、InnoDB の起動に時間がかかり、クラッシュすることもあった。

  • バグ修正: 高速シャットダウン(デフォルト)が、パージまたは挿入バッファのマージによって遅くなることがあった。

  • バグ修正: 読み取り一貫性で、可視のローが存在しないテーブルに対して大規模な SELECT を実行すると、btr0cur.c の 310 行目で長時間(600 秒より長い)のセマフォ待ちが発生する可能性があった。

  • バグ修正: ロック待ちの後で付与された AUTO-INC ロックが、トランザクションが終了するまで維持されていた。 これによって、不必要なデッドロックが発生する可能性があった。

  • バグ修正: LOCK TABLES 内でテンポラリテーブルを作成し、そのテーブルを使用すると、ha_innobase.cc でアサートエラーが発生していた。

  • バグ修正: SHOW INNODB STATUS、innodb_monitor、または innodb_lock_monitor で、1 回のレポートに数百のトランザクションを出力する必要がある場合に出力が切り捨てられると、srv0srv.c の 1621 行目で作成されたミューテックスに対する多数の待ちをエラーログに出力する際に InnoDB がハングしていた。

  • バグ修正: Unix で SHOW INNODB STATUS を実行すると、平均ファイル読み取りサイズが常に 0 バイトとして報告されていた。

7.5.16.21. MySQL/InnoDB-4.0.4(2002 年 10 月 2 日)

  • Windows で、バッファなしのディスク I/O が再び使用されるようになった。通常の I/O では、Windows XP および Windows 2000 での読み取りのパフォーマンスに支障が出ると思われる。

  • InnoDB テーブルの最大キー長が 500 バイトから 1,024 バイトに拡大された。

  • SHOW TABLE STATUS のテーブルコメントフィールドが拡大されて、外部キー定義を 16,000 文字まで出力できるようになった。

  • ローの挿入が実行直後にエラーで失敗した場合に、オートインクリメントカウンタがインクリメントされないようになった。

  • innodb_force_recovery が設定されている場合でも、テーブルを破棄または作成できるようになった。これを利用して、ロールバックまたはパージでクラッシュを引き起こすテーブルを破棄したり、テーブルインポートが失敗した後のリカバリでロールバックが暴走した場合にテーブルを破棄することができる。

  • バグ修正: 4.0.3 で ORDER BY primarykey DESC を使用すると、btr0pcur.c の 203 行目でアサートエラーが発生していた。

  • バグ修正: 高速シャットダウン(デフォルト)が、パージまたは挿入バッファのマージによって遅くなることがあった。

  • バグ修正: 読み取り一貫性で、可視のローが存在しないテーブルに対して大規模な SELECT を実行すると、btr0cur.c の 310 行目で長時間(600 秒より長い)のセマフォ待ちが発生する可能性があった。

  • バグ修正: MySQL クエリキャッシュが使用された場合に、ON DELETE CASCADE または ...SET NULL で変更が実行されてもクエリキャッシュが無効にならなかった。

  • バグ修正: LOCK TABLES 内でテンポラリテーブルを作成し、そのテーブルを使用すると、ha_innodb.cc でアサートエラーが発生していた。

  • バグ修正: innodb_flush_log_at_trx_commit を 1 に設定すると、SHOW VARIABLES でその値が1,600 万として表示されていた。

7.5.16.22. MySQL/InnoDB-4.0.3(2002 年 8 月 28 日)

  • 挿入の際に、ロックを取得する読み取り、更新、または削除を待ってそのネクストキーロックを解放する必要がある場合に、不必要なデッドロックが発生しないようになった。

  • MySQL の HANDLER SQL コマンドが、InnoDB 型のテーブルでも動作するようになった。InnoDB は、HANDLER の読み取りを常に一貫した読み取りとして実行する。HANDLER は、ダイレクトアクセスパスとしてテーブルのインデックスを個別に読み取る。場合によっては、HANDLER をサーバ側カーソルの代わりに使用できる。

  • 4.0.2 でのバグを修正: 単純な挿入であっても、AIX バージョンがクラッシュする可能性があった。

  • バグ修正: DROP TABLE で、テーブル名に文字コードが 127 より大きい文字を使用すると、pars0sym.c の 155 行目で InnoDB がアサートする可能性があった。

  • ソースからのコンパイルで、HP-UX-11 および HP-UX-10.20 の両方に作業バージョンが提供されるようになった。以前は、4.0.2 のソースが 11 でのみ機能し、3.23.52 のソースが 10.20 でのみ機能していた。

  • バグ修正: 64 ビットの Solaris でコンパイルすると、InnoDB の起動時にバスエラーが発生していた。

7.5.16.23. MySQL/InnoDB-3.23.52(2002 年 8 月 16 日)

  • 3.23 の機能セットは、このバージョンより凍結される。新しい機能は 4.0 ブランチで導入され、3.23 ブランチではバグの修正のみが行われる。

  • CPU バウンドの結合クエリの多くで、実行が速くなった。Windows では、その他の CPU バウンドのクエリも実行が速くなった。

  • 新たな SQL コマンド SHOW INNODB STATUS によって、InnoDB モニタの出力がクライアントに返されるようになった。InnoDB モニタで、最後に検出されたデッドロックに関する詳細情報が出力されるようになった。

  • InnoDB では、SQL クエリオプティマイザがインデックスのみの範囲スキャンを多用する代わりにフルテーブルスキャンを使用していた。これが修正された。

  • 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 が役立つ。

  • セカンダリインデックスに UNIQUE 制約を設定している場合に SET UNIQUE_CHECKS=0 を指定すると、InnoDB へのテーブルのインポートが速くなる。 このフラグは、入力レコードが UNIQUE 制約に違反していないことが確実である場合のみ使用できる。

  • SHOW TABLE STATUS で、想定される ON DELETE CASCADEON DELETE SET NULL もテーブルのコメントフィールドに列挙されるようになった。

  • InnoDB 型のテーブルで CHECK TABLE を実行すると、すべてのテーブルのハッシュインデックスもチェックされるようになった。

  • ON DELETE CASCADE または SET NULL を定義し、親レコードで参照キーを更新すると、InnoDB によって子レコードが削除または更新されていた。この動作が変更され、SQL-92 に準拠するようになった。つまり、エラー 'Cannot delete parent row' が表示されるようになった。

  • オートインクリメントのアルゴリズムが改善され、最初の挿入または SHOW TABLE STATUS でテーブルのオートインクリメントカウンタが初期化されるようになった。 これによって、SHOW TABLE STATUS で突然デッドロックが発生することがほぼなくなった。

  • データファイルへの読み取りと書き込みに使用されていた一部のバッファが調整された。 これによって、Linux でバッファなしのローデバイスをデータファイルとして使用できるようになった。

  • バグ修正: 大文字と小文字を変更するだけの目的でテーブルの主キーを更新した場合に、主に page0page.ic の 515 行目でアサートエラーが発生する可能性があった。

  • バグ修正: 外部キー制約で参照されているレコードを削除または更新する場合に、外部キーチェックでロック待ちが発生すると、そのチェックから誤った結果が報告されることがある。これは、ON DELETE... 操作にも影響する。

  • バグ修正: InnoDB でデッドロックまたはロック待ちタイムアウトエラーが発生すると、InnoDB はトランザクション全体をロールバックするが、MySQL が以前の SQL ステートメントを(InnoDB がそれらをロールバックした後も)バイナリログに書き出す可能性があった。このため、たとえばレプリケートされたデータベース間で同期が取れなくなることがあった。

  • バグ修正: コミットの途中でデータベースがクラッシュした場合に、リカバリでテーブルスペースのページがリークされることがあった。

  • バグ修正: latin1 以外のキャラクタセットを my.cnf で指定した場合に、マニュアルの記述に反して、外部キー制約の文字列型カラムの長さの指定を参照元テーブルと参照先テーブルで同じにしなければならなかった。

  • バグ修正: CREATE TABLE の実行中に DROP TABLE または DROP DATABASE を実行すると、失敗する可能性があった。

  • バグ修正: 32 ビットコンピュータで 2 GB を超えるバッファプールを設定した場合に、InnoDB が buf0buf.ic の 214 行目でアサートしていた。

  • バグ修正: 64 ビットコンピュータで、あるカラムに SQL NULL を含んでいるレコードを更新した場合に、UNDO ログと通常のログが破損する可能性があった。

  • バグ修正: innodb_log_monitor でページに対してロック出力を抑制するとハングが発生していた。

  • バグ修正: HP-UX-10.20 バージョンでは、ミューテックスによって InnoDB コードのあらゆる部分でリーク、競合、クラッシュが発生していた。

  • バグ修正: AUTOCOMMIT モードで SELECT を実行した直後に RENAME TABLE を実行すると、RENAME が失敗し、MySQL からエラー 1192 が返されていた。

  • バグ修正: 64 ビットの Solaris でコンパイルすると、InnoDB の起動時にバスエラーが発生していた。

7.5.16.24. MySQL/InnoDB-4.0.2(2002 年 7 月 10 日)

  • InnoDB は基本的に InnoDB-3.23.51 と同じ。

  • innodb_data_file_path が指定されないときは、InnoDB がデータベース作成時に 10 MB の自動拡張データファイル ibdata1 を MySQL のデータディレクトリに作成するようになった。4.0.1 では、ファイルが 64 MB に固定され、自動的に拡張されなかった。

7.5.16.25. MySQL/InnoDB-3.23.51(2002 年 6 月 12 日)

  • バグ修正: テーブル内の BLOB または TEXT カラムに SQL NULL 値が含まれていると、結合でこれらのカラムをコピーする際にセグフォルトが発生する可能性があった。

  • バグ修正: ON DELETE CASCADE を指定して自己参照型の外部キー制約をテーブルに追加した場合にレコードを削除すると、カスケード削除のために InnoDB が同じレコードを 2 回削除しようとしてアサートエラーが発生していた。

  • バグ修正: MySQL の 'ユーザレベルロック' を使用して接続を閉じると、InnoDB が ha_innobase.cc の 302 行目でアサートする場合がある。

7.5.16.26. MySQL/InnoDB-3.23.50(2002 年 4 月 23 日)

  • InnoDB が自動拡張する最後のデータファイルをサポートするようになった。データベース起動時に、データファイル全体を先に割り当てる必要がなくなった。

  • InnoDB ホットバックアップツールを使いやすくするためにいくつかの変更が行われた。この独立した non-free ツールでは、サーバをシャットダウンしたりロックを設定したりすることなく、データベースをオンラインでバックアップできる。

  • 自動拡張するデータファイルで InnoDB ホットバックアップツールを実行する場合は、このツールをバージョン ibbackup-0.35 にアップグレードする必要がある。

  • クラッシュリカバリでのログスキャンが速くなった。

  • このサーババージョンより、ホットバックアップツールがバックアップ InnoDB データファイルの末尾の未使用部分を切り捨てるようになった。

  • ホットバックアップツールを機能させるために、Windows でバッファなしの I/O またはネイティブの非同期 I/O ではなく、Unix のようにシミュレートされた非同期 I/O が使用されるようになった。

  • 外部キーで ON DELETE CASCADE または ON DELETE SET NULL 節を定義できるようになった。

  • 外部キー制約が、ALTER TABLE および CREATE INDEX で生き残るようになった。

  • チェックされる外部キーまたは参照キーに含まれるカラム値のいずれかが SQL NULL であると、外部キーチェックが抑制される。この動作は、Oracle などと互換である。

  • SHOW CREATE TABLE で外部キー制約も列挙されるようになった。mysqldump でも、テーブル定義内の外部キーが出力されるようになった。

  • 新しい外部キー制約を、ALTER TABLE ... ADD CONSTRAINT FOREIGN KEY (...) REFERENCES ... (...) で追加できるようになった。

  • 外部キーの定義で、テーブル名とカラム名をバッククォートで囲めるようになった。

  • MySQL のコマンド 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 で失敗していた。

7.5.16.27. MySQL/InnoDB-3.23.49(2002 年 2 月 17 日)

  • バグ修正: クエリが実行されているデータベースに対して DROP DATABASE を呼び出すと、MySQL サーバがクラッシュまたはハングする可能性があった。クラッシュについては修正されたが、完全に修正されるには、MySQL コードレイヤでの変更を待つ必要がある。

  • バグ修正: Windows で DROP DATABASE を実行するには、データベース名を小文字で指定する必要があった。3.23.49 で修正: Windows では大文字と小文字が区別されなくなった。Unix では引き続きデータベース名で大文字と小文字が区別される。

  • バグ修正: latin1 以外のキャラクタセットをデフォルトのキャラクタセットとして定義すると、外部キー制約の定義が dict0crea.c でのアサートエラーで失敗し、内部エラー 17 が報告される可能性があった。

7.5.16.28. MySQL/InnoDB-3.23.48(2002 年 2 月 9 日)

  • SQL オプティマイザで、テーブルスキャンよりも頻繁なインデックス検索が優先されるようになった。

  • マルチプロセッサ搭載の Linux コンピュータで、複数の大規模な SELECT クエリが同時に実行された場合のパフォーマンス上の問題が解消された。 CPU バウンドの大規模な SELECT クエリの実行も、すべてのプラットフォームで一様に速くなる。

  • MySQL のバイナリログが使用される場合に、InnoDB がクラッシュリカバリ後に最新の MySQL バイナリログファイルの名前と、そのファイルのどの位置までリカバリできたか(= バイトオフセット)を出力するようになった。これは、レプリケーションでマスタデータベースとスレーブデータベースを再同期させる場合などに役立つ。

  • インストールの問題で役立つように、よりわかりやすいエラーメッセージが追加された。

  • InnoDB テーブルスペース内で親テーブルを失った MySQL テンポラリテーブルもリカバリできるようになった。

  • 外部キーの宣言で、参照元整数カラムと参照先整数カラムで符号の有無が異なっている場合に、InnoDB がその宣言を阻止するようになった。

  • バグ修正: SHOW CREATE TABLE または SHOW TABLE STATUS を呼び出すと、メモリが破損し、mysqld がクラッシュする可能性があった。 特に、SHOW CREATE TABLE を頻繁に呼び出す mysqldump では危険性が高かった。

  • バグ修正: Unix で InnoDB テーブルに対して ALTER TABLE とクエリを同時に実行した場合に、mysqldrow0row.c の 474 行目でアサートエラーを起こしてクラッシュする可能性があった。

  • バグ修正: オートインクリメントカラムを含んでいる複数のテーブルへの挿入が 1 つの LOCK TABLES 内にラップされていると、InnoDB が lock0lock.c でアサートしていた。

  • 3.23.47 では、一意のセカンダリインデックスに複数の NULL 値があってもかまわなかった。 ところが、CHECK TABLE からはテーブルが破損していると報告されていた。 CHECK TABLE がこの状況でエラーを報告しなくなった。

  • バグ修正: ビッグエンディアンを採用する Sparc などのプロセッサで SHOW VARIABLES を実行すると、innodb_flush_log_at_trx_commit などのブール値の起動パラメータが、オンであっても常に OFF と表示されていた。

  • バグ修正: Windows NT/2000 で mysqld-max-nt をサービスとして実行した場合に、そのサービスのシャットダウンが、InnoDB シャットダウンの完了を待たずに実行されていた。

7.5.16.29. MySQL/InnoDB-3.23.47(2001 年 12 月 28 日)

  • リカバリが、特に負荷の軽いシステムで速くなった。これは、バックグラウンドでより頻繁にチェックポイントが実行されるようになったためである。

  • 一意のセカンダリインデックスで類似する複数のキー値に SQL NULL が含まれていれば、InnoDB がそれらの値を許可するようになった。これで、MyISAM テーブルと同じ規則になった。

  • BLOB を含んでいるテーブルについて InnoDB がレコード数を推定する精度が向上した。

  • 外部キー制約で、InnoDB がカラム名(Windows ではテーブル名も)の大文字と小文字を区別するようになった。

  • InnoDB では、CHAR 型の外部キーカラムによる VARCHAR 型のカラムの参照、およびその逆の参照が可能である。MySQL によって、一部のカラムの型が CHAR と VARCHAR の間で暗黙的に変更される。この変更が外部キーの宣言を妨げることがなくなった。

  • ログファイルの破損に対するリカバリが強化された。

  • テンポラリテーブルを生成するクエリから、不要な統計情報の計算が取り除かれた。一部の ORDER BY クエリと DISTINCT クエリの実行が速くなった。

  • InnoDB テーブルのテーブルスキャンが主キーを通して実行されていることを、MySQL が認識するようになった。これによって、一部の ORDER BY クエリでソートが不要になる。

  • InnoDB テーブルの最大キー長が再び 500 バイトに制限された。 MySQL インタープリタはこれより長いキーを処理できない。

  • innodb_lock_wait_timeout のデフォルト値が無限から 50 秒へと変更され、innodb_file_io_threads のデフォルト値が 9 から 4 に変更された。

7.5.16.30. MySQL/InnoDB-4.0.1(2001 年 12 月 23 日)

  • InnoDB は 3.23.47 と同じ。

  • 4.0.0 では、MySQL インタープリタが構文 LOCK IN SHARE MODE を認識しなかった。これが修正された。

  • 4.0.0 では、トランザクションテーブルに対して複数テーブルの削除を実行できなかった。 これが修正された。

7.5.16.31. MySQL/InnoDB-3.23.46(2001 年 11 月 30 日)

  • 3.23.45 と同じ。

7.5.16.32. MySQL/InnoDB-3.23.45(2001 年 11 月 23 日)

  • これはバグ修正用リリースである。

  • バージョン 3.23.42 ? .44 では、Windows でテーブルを作成する際に、データベース名に小文字を使用しないとテーブルにアクセスできなかった。これが 3.23.45 で修正された。

  • InnoDB が stdout と stderr を 10 秒おきにフラッシュするようになった。これらがファイルにリダイレクトされれば、エディタでファイルの内容を参照しやすくなる。

  • .44 では、.frm ファイルは作成されているが InnoDB に存在しないテーブルを破棄すると、trx0trx.c の 178 行目でアサートエラーが発生していたが、これが修正された。

  • 挿入バッファでのバグが修正された。 挿入バッファツリーが矛盾した状態となり、クラッシュおよびリカバリのクラッシュも引き起こす可能性があった。このバグは、特にテーブルの大規模なインポートや変更で発生していた。

  • リカバリでのバグが修正された。InnoDB が、バッファプールに空きブロックがないことを通知する警告メッセージを表示して絶えず無限ループに入る可能性があった。

  • バグ修正: InnoDB 型のテンポラリテーブルを作成し、そのテーブルに対して ALTER TABLE を実行すると、MySQL サーバがクラッシュする可能性があった。

  • MySQL のシステムテーブル 'mysql.user''mysql.host'、または 'mysql.db' を InnoDB 型で作成できないようになった。

  • srv0srv.c の 1728 行目でアサートエラーを引き起こすおそれのある 3.23.44 でのバグが修正された。

7.5.16.33. MySQL/InnoDB-3.23.44(2001 年 11 月 2 日)

  • InnoDB テーブルで外部キー制約を定義することができる。例: FOREIGN KEY (col1) REFERENCES table2(col2)

  • ファイルシステムで許可されていれば、4 GB を超えるデータファイルを作成することができる。

  • InnoDB モニタが改善された。これには、InnoDB 内部データディクショナリの内容を出力するための新たなパラメータ innodb_table_monitor も含まれる。

  • DROP DATABASE が InnoDB テーブルでも動作するようになった。

  • デフォルトのキャラクタセットである latin1 のアクセント文字が、MySQL の順序に従って並べられるようになった。 注意: latin1 を使用している場合に、インデックスの付いた CHAR カラムに 127 より大きなコードの文字を挿入したときは、3.23.43 にアップグレードするときに、テーブルに対して CHECK TABLE を実行する必要がある。CHECK TABLE からエラーが返された場合は、テーブルを破棄してもう一度インポートする必要がある。

  • InnoDB によるテーブルカーディナリティの推定値の計算精度が向上した。

  • デッドロックの解決方法が変更された。 .43 ではデッドロックが発生すると SQL ステートメントのみがロールバックされるが、.44 ではトランザクション全体がロールバックされる。

  • デッドロック、ロック待ちタイムアウト、および外部キー制約違反(親レコードがない、子レコードが存在する)が、それぞれネイティブの MySQL エラーコード 1213、1205、1216、1217 を返すようになった。

  • my.cnf の新しいパラメータ innodb_thread_concurrency は、並行処理が頻繁に行われる環境でのパフォーマンスチューニングに役立つ。

  • my.cnf の新しいオプション innodb_force_recovery は、破損したデータベースからテーブルをダンプする際に役立つ。

  • my.cnf の新しいオプション innodb_fast_shutdown は、シャットダウンを高速化する。通常、InnoDB はシャットダウン時に完全なパージと挿入バッファのマージを実行する。

  • 最大キー長が以前の制限値である 500 バイトから 7,000 バイトに引き上げられた。

  • 複数行挿入を伴うオートインクリメントカラムのレプリケーションでのバグが修正された。

  • インデックスの付いたセカンダリカラムの更新で、大文字と小文字が変更されるときのバグが修正された。

  • データファイルの数が 24 個を超えた場合に発生するハングが修正された。

  • 空のテーブルから MAX(col) が選択される際に、col が複合インデックスの最初のカラムでない場合に発生していたクラッシュが修正された。

  • クラッシュの原因となりうるパージでのバグが修正された。

7.5.16.34. MySQL/InnoDB-3.23.43(2001 年 10 月 4 日)

  • 基本的に InnoDB-3.23.42 と同じ。

7.5.16.35. MySQL/InnoDB-3.23.42(2001 年 9 月 9 日)

  • 8,000 バイトを超えるレコードの主キーが更新された場合にテーブルが破損するバグが修正された。

  • InnoDB モニタの種類が、innodb_monitorinnodb_lock_monitorinnodb_tablespace_monitor の 3 つになった。 innodb_monitor で、バッファプールのヒット率、および挿入、更新、削除、読み取りが行われたレコードの総数が新たに出力されるようになった。

  • RENAME TABLE でのバグが修正された。

  • オートインクリメントカラムを伴うレプリケーションでのバグが修正された。

7.5.16.36. MySQL/InnoDB-3.23.41(2001 年 8 月 13 日)

  • 4 GB 未満のレコードをサポートする。以前の制限は 8,000 バイト。

  • 二重書き込みファイルフラッシュ方式を採用した。

  • ローデバイスをデータファイルとしてサポートする。

  • InnoDB モニタ。

  • ハングの原因となる複数のバグ、および ORDER BY のバグ('Sort aborted')が修正された。

7.5.16.37. MySQL/InnoDB-3.23.40(2001 年 7 月 16 日)

  • 発生頻度の低い数個のバグのみが修正された。

7.5.16.38. MySQL/InnoDB-3.23.39(2001 年 6 月 13 日)

  • CHECK TABLEInnoDB テーブルでも動作するようになった。

  • my.cnf の新しいパラメータ innodb_unix_file_flush_method が追加された。このパラメータは、ディスク書き込みパフォーマンスのチューニングに使用できる。

  • オートインクリメントカラムが、トランザクションメカニズムを通さずに新しい値を取得するようになった。これによって、新しい値を割り当てる際の CPU 時間が削減され、トランザクションデッドロックが排除される。

  • 3.23.38 での複数のバグ、特にロールバックでのバグが修正された。

7.5.16.39. MySQL/InnoDB-3.23.38(2001 年 5 月 12 日)

  • 新たな構文 SELECT ... LOCK IN SHARE MODE が導入された。

  • ディスクへの書き込みが行われるたびに、InnoDBfsync() を呼び出し、書き込みまたは読み取りを行うすべてのデータベースページのチェックサムを計算してディスクの不具合を明らかにするようになった。

  • 複数のバグが修正された。

7.5.17. InnoDB についての問い合わせ先

InnoDB エンジンの製造元である Innobase Oy の問い合わせ先は次のとおりです。 Web サイト: http://www.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

7.6. BDB または BerkeleyDB テーブル

7.6.1. 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 項1.4.1. 「MySQL AB によって提供されるサポート」

7.6.2. BDB のインストール

BerkeleyDB をサポートする MySQL のバイナリバージョンをダウンロードした場合は、MySQL のバイナリバージョンをインストールするための指示に従ってください。 See 項2.2.9. 「MySQL バイナリディストリビューションのインストール」。 See 項4.8.5. 「mysqld-max(拡張 mysqld サーバ)」

Berkeley DB をサポートする MySQL をコンパイルするには、MySQL バージョン 3.23.34 以降をダウンロードし、--with-berkeley-db オプションを使って MySQL をコンフィギャします。 See 項2.3. 「MySQL ソースディストリビューションのインストール」

cd /path/to/source/of/mysql-3.23.34
./configure --with-berkeley-db

詳しい最新情報については、BDB ディストリビューション付属のマニュアルを参照してください。

Berkeley DB 自体は十分にテストされていて信頼できますが、MySQL とのインタフェースはまだガンマ品質と見なされています。 当社は、このインタフェースの 1 日も早い安定化を目指して、積極的に改善と最適化を行っています。

7.6.3. BDB 起動オプション

AUTOCOMMIT=0 で実行している場合、BDB テーブルでの変更は COMMIT を実行するまで反映されません。コミットの代わりに ROLLBACK を実行すると、変更が無効になります。 See 項6.7.1. 「START TRANSACTIONCOMMITROLLBACK の各構文」

AUTOCOMMIT=1(デフォルト)で実行している場合は、変更が直ちにコミットされます。SQL コマンド BEGIN WORK で拡張トランザクションを開始できます。その場合、COMMIT を実行するまで(または変更の ROLLBACK を実行するまで)変更がコミットされません。

次に示す mysqld のオプションを使用すると、BDB テーブルの動作を変更できます。

オプション説明
--bdb-home=directoryBDB テーブルのベースディレクトリ。--datadir に使用するディレクトリと同じでなければならない。
--bdb-lock-detect=#Berkely のロック検出。DEFAULTOLDESTRANDOM、または YOUNGEST のいずれか。
--bdb-logdir=directoryBerkeley DB のログファイルディレクトリ。
--bdb-no-syncログを同期的にフラッシュしない。
--bdb-no-recoverBerkeley DB をリカバリモードで起動しない。
--bdb-shared-dataBerkeley DB をマルチプロセスモードで起動する(Berkeley DB を初期化する際に DB_PRIVATE を使用しない)。
--bdb-tmpdir=directoryBerkeley DB のテンポラリファイルディレクトリ。
--skip-bdbBDB テーブルの使用を無効にする。
-O bdb_max_lock=1000ロックの数の上限を設定する。 See 項4.6.8.4. 「SHOW VARIABLES

--skip-bdb を使用すると、MySQL は Berkeley DB ライブラリを初期化しなくなるため、大量のメモリを節約できます。このオプションを使用しているときは BDB テーブルを使用できません。BDB テーブルを作成しようとすると、代わりに MyISAM テーブルが作成されます。

BDB テーブルを使用する予定であれば、通常は --bdb-no-recover を指定せずに mysqld を起動する必要があります。ただし、BDB ログファイルが破損している場合に mysqld を起動しようとすると、問題が発生する可能性があります。 See 項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 項6.7.1. 「START TRANSACTIONCOMMITROLLBACK の各構文」

7.6.4. BDB テーブルの特性

  • BDB ストレージエンジンは、トランザクションをロールバックできるようにログファイルを保持している。パフォーマンスを最大限に高めるには、--bdb-logdir オプションを使ってこれらのログファイルをデータベースとは別のディスクに配置する必要がある。

  • MySQL は、新しい BDB ログファイルが開始されるたびにチェックポイントを実行し、現在のトランザクションに不要なログファイルを削除する。FLUSH LOGS を任意の時点で実行して、Berkeley DB テーブルをチェックポイントすることもできる。

    障害リカバリには、テーブルバックアップおよび MySQL のバイナリログを使用する必要がある。 See 項4.5.1. 「データベースのバックアップ」

    警告: 使用中の古いログファイルを削除すると、問題が発生した場合に BDB がリカバリを実行できなくなり、データが失われるおそれがある。

  • MySQL は、前に読み取られたレコードを参照できるように、各 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 テーブルよりも若干大きくなる。

  • オプティマイザには、テーブル内のおおよそのレコード数を通知する必要がある。MySQL は、挿入の回数をカウントし、その数値を BDB テーブル内の独立したセグメントで管理することで、これに対応している。DELETE または ROLLBACK ステートメントを大量に発行しない限り、この数値は MySQL オプティマイザにとって十分に正確であるが、MySQL はクローズ時までこの数値を格納しないため、MySQL が突然中断されると数値が不正確になる場合がある。この数値が 100% 正確でなくても、致命的な問題にはならない。レコードの数は、ANALYZE TABLE または OPTIMIZE TABLE を実行することで更新できる。 See 項4.6.2. 「ANALYZE TABLE 構文」。 See 項4.6.1. 「OPTIMIZE TABLE 構文」

  • BDB テーブルでディスクがいっぱいになると、エラー(おそらくエラー 28)が発生し、トランザクションがロールバックされる。これとは対照的に、MyISAM テーブルと ISAM テーブルでは、十分な空きディスクが確保されるのを待って、mysqld が処理を続行する。

7.6.5. 近い将来に修正する必要がある BDB の問題

  • 多数の BDB テーブルを同時に開く際にかなり時間がかかる。BDB テーブルを使用する場合は、テーブルキャッシュをあまり大きくしないようにする(たとえば 256 以下にする)必要がある。また、mysql クライアントで --no-auto-rehash を使用する必要がある。これについては、4.0 で部分的に修正する予定である。

  • SHOW TABLE STATUSBDB テーブルに関して提供される情報がまだ十分でない。

  • パフォーマンスを最適化する。

  • テーブルをスキャンするときにページロックを一切使用しないように変更する。

7.6.6. BDB でサポートされているオペレーティングシステム

今のところ、BDB ストレージエンジンは次のオペレーティングシステムで動作することが確認されています。

  • Linux 2.x Intel

  • Sun Solaris(sparc および x86)

  • FreeBSD 4.x/5.x(x86、sparc64)

  • IBM AIX 4.3.x

  • SCO OpenServer

  • SCO UnixWare 7.1.x

次のオペレーティングシステムでは動作しません。

  • Linux 2.x Alpha

  • Linux 2.x AMD64

  • Linux 2.x IA64

  • Linux 2.x s390

  • Max OS X

注意: 上記の一覧は完全ではありません。情報が入り次第更新する予定です。

BDB テーブルのサポート付きで MySQL をビルドする場合に、mysqld を起動するとログファイルに次のエラーが記録されることがあります。

bdb: architecture lacks fast mutexes: applications cannot be threaded
Can't init databases

これは、使用するアーキテクチャで BDB テーブルがサポートされていないことを意味します。 この場合は、BDB テーブルのサポートなしで MySQL をビルドし直す必要があります。

7.6.7. BDB テーブルの制限事項

次に挙げるのは、BDB テーブルを使用する場合の制限事項です。

  • BDB テーブルは、.db ファイルに、作成されたときのこのファイルへのパスを格納する (これは、シンボリックリンクをサポートするマルチユーザ環境でロックを検出できるようにするために行われる)。

    このため、BDB テーブルはディレクトリ間で移動できない。

  • BDB テーブルのバックアップを取るときは、mysqldump を使用するか、すべての table_name.db ファイルと BDB ログファイルのバックアップを取る必要がある。BDB のログファイルは、ベースデータディレクトリにある log.XXXXXXXXXX(10 桁)という名前のファイルである。 BDB ストレージエンジンは未完了のトランザクションをログファイルに格納する。これらのログは、mysqld が起動されるときに存在していなければならない。

7.6.8. BDB テーブルを使用するときに起こりうるエラー

  • mysqld を起動する際に hostname.err ログに次のエラーが記録されることがある。

    bdb:  Ignoring log file: .../log.XXXXXXXXXX: unsupported log version #
    

    これは、新しいバージョンの BDB が古いログファイル形式をサポートしていないことを意味する。この場合は、データベースディレクトリからすべての BDB ログ(log.XXXXXXXXXX という形式の名前を持つファイル)を削除し、mysqld を再起動する必要がある。また、古い BDB テーブルの mysqldump --opt を実行したうえで、そのテーブルを削除し、ダンプをリストアした方がよい。

  • オートコミットモードで実行していない場合に、別のトランザクションで参照されているテーブルを削除すると、MySQL エラーログに次のエラーメッセージが記録されることがある。

    001119 23:43:56  bdb:  Missing log fileid entry
    001119 23:43:56  bdb:  txn_abort: Log undo failed for LSN:
                           1 3644744: Invalid
    

    これは致命的なエラーではないが、この問題が修正されるまでは(小さな修正ではない)、オートコミットモードでないときにテーブルを削除しない方がよい。


This is a translation of the MySQL Reference Manual that can be found at dev.mysql.com. The original Reference Manual is in English, and this translation is not necessarily as up to date as the English version.

アダルトレンタルサーバー