MySQL サーバ(バージョン 3.23-max
およびすべてのバージョン 4.0
以降)では、InnoDB
トランザクションストレージエンジンでトランザクションがサポートされています。InnoDB
は完全に
ACID
に準拠しています。詳しくは
章 13. ストレージエンジンとテーブルタイプ
を参照してください。トランザクションエラーの取り扱いにおける、標準SQLとInnoDB
との違いについては項13.5.15. 「InnoDB
エラー処理」を参照してください。
MySQL
サーバのその他の非トランザクションストレージエンジン(MyISAM
など)は、「アトミックオペレーション」と呼ばれる別のデータ整合性のパラダイムに従います。トランザクションの観点では、MyISAM
テーブルは事実上、常に
AUTOCOMMIT=1
モードで動作すると言えます。アトミックオペレーションでは多くの場合、パフォーマンスの高さに値する整合性が確保されます。
両方のパラダイムをサポートする MySQL サーバでは、ユーザはアトミックオペレーションの速度を必要とするか、アプリケーションでトランザクション機能を使用する必要があるかを選択することができます。この選択は、テーブルごとに行うことができます。
ほとんどの場合、トランザクションストレージエンジンと非トランザクションストレージエンジンのどちらを選ぶかの決め手となるのはパフォーマンスです。トランザクションテーブルの場合、はるかに大きなメモリとディスク領域が必要で、CPU
のオーバーヘッドも大きくなります。しかし、InnoDB
のようなトランザクションストレージエンジンには特有の機能も多数あります。MySQLサーバのモジュール設計によって、このようなさまざまなストレージエンジンすべてを同時に使用することができるので、さまざまな要件に合わせて、あらゆる条件で最適なパフォーマンスを確保することが可能です。
しかし、MySQL
サーバの機能を使用して、非トランザクションの
MyISAM
テーブルでも厳密な整合性を確保するにはどのようにすればよいのでしょうか。また、非トランザクションテーブルの機能はトランザクションストレージエンジンにどのように対抗できるのでしょうか。
トランザクションパラダイムでは、重大な状況で
COMMIT
ではなく
ROLLBACK
の呼び出しに依存するようにアプリケーションが作成されている場合、トランザクションの方が便利です。また、トランザクションでは、完了していない更新や失敗した活動がデータベースにコミットされることはない。サーバには自動ロールバックを行う機会が与えられ、データベースは保護されます。
非トランザクションテーブルを使用している場合、MySQL サーバでは、ほとんどの場合、更新前に簡単なチェックを組み込んだり、データベースの不整合をチェックして、不整合が発生した場合には自動的に修復または警告する簡単なスクリプトを実行したりすることで、発生する可能性がある問題を解決することができます。MySQL ログを使用したり、別のログを 1 つ追加したりするだけで、通常、データの整合性が失われることなく、完全にテーブルを修復することができます。
ほとんどの場合、重要なトランザクション更新はアトミックな更新に記述し直すことができます。通常、トランザクションによって解決される整合性の問題はすべて、LOCK
TABLES
またはアトミックな更新を使用して解決することができます。これによって、サーバが自動的に停止するという、トランザクションデータベースシステムと共通する問題が回避されます。
MySQL サーバを安全に使用するには、トランザクションテーブルを使用するかどうかに関係なく、バックアップを作成し、バイナリログをオンにするだけです。これにより、他のトランザクションデータベースシステムで可能なように、どのような状況からもリカバリすることができます。使用するデータベースシステムに関係なく、どのような場合でもバックアップを作成することが望ましいのは当然です。
トランザクションパラダイムには長所と短所があります。多数のユーザおよびアプリケーション開発者は、停止が発生する、または停止が必要な問題に関するコード化の容易さに頼っています。しかし、アトミックオペレーションのパラダイムに慣れていなかったり、トランザクションの方が詳しいという場合でも、非トランザクションテーブルの速度が、最も高速で最適に調整されたトランザクションテーブルの速度の 3 倍から 5 倍も速いという長所を考えてみてください。
整合性が最も重要な状況では、MySQL
サーバは、非トランザクションテーブルでもトランザクションレベルの信頼性と整合性を提供します。LOCK
TABLES
を使用してテーブルをロックすると、整合性チェックが行われるまで、すべての更新が延期されます。テーブルエンドで同時挿入が可能なREAD
LOCAL
ロック(書き込みロックの逆)が設定されている場合、他のクライアントによる読み取りと挿入は引き続き行うことができます。新しく挿入したレコードは、読み取りがロックされているクライアントには、読み取りロックが解除されるまで表示されません。INSERT
DELAYED
を使用すると、ロックが解除されるまで挿入をローカルキューに入れることができ、クライアントは挿入が完了するまで待機する必要はありません。項6.3.3. 「同時挿入」
および 項12.2.4.2. 「INSERT DELAYED
構文」
を参照してください。
ここでいう「アトミック」とは、魔法のようなものではありません。個々の更新の実行中は、他のユーザがそれを妨害できないようにするとともに、自動ロールバック(あまり注意を払わなかった場合に、トランザクションテーブルで行われることがあります)が行われないようにすることができるというだけです。また、MySQL サーバでは、ダーティリードが行われることもありません。
非トランザクションテーブルを使用する際のテクニックは、以下のとおりです。
LOCK
TABLES
を使用して、通常はトランザクションを必要とするループをコード化することができる。そのため、実行中にレコードを更新するカーソルが不要です。
ROLLBACK
を使用しないように、以下の方法を使用することができます。
LOCK TABLES
を使用して、アクセスするすべてのテーブルをロックします。
更新する前に条件をテストします。
すべてに問題がなければ、更新します。
UNLOCK
TABLES
を使用して、ロックを解除します。
常にではありませんが、これは通常ロールバックが行われる可能性があるトランザクションを使用するよりも、はるかに速い方法です。ただし、更新の途中でスレッドが停止された場合は、この方法では対応することができません。その場合、すべてのロックが解除されるが、一部の更新が実行されていない可能性があります。
関数を使用して、1 回の操作でレコードを更新することもできます。次のようなテクニックを使用すると、非常に効率的なアプリケーションを取得することができます。
現在の値に関連してカラムを変更します。
実際に変更されたカラムのみを更新します。
たとえば、顧客情報に対して更新を行う場合、変更された顧客データのみを更新し、変更されたデータ、または変更されたデータに依存するデータが元のレコードと比較して変更されていないことだけをテストします。変更されたデータのテストは、UPDATE
ステートメントで
WHERE
節を使用して行われます。レコードが更新されなかった場合、「Some
of the data you have changed has been changed by another
user.」というメッセージがクライアントに表示されます。その場合、ウィンドウに元のレコードと新しいレコードを表示して、使用する必要がある顧客レコードのバージョンをユーザが決定できるようにします。
これによって、カラムロックと類似しているが、現在の値に関連する値を使用して、カラムの一部のみが更新される点で、実際はカラムロックよりすぐれた機能が実現します。つまり、一般的なUPDATE
ステートメントは次のようになります。
UPDATE tablename SET pay_back=pay_back+125; UPDATE customer SET customer_date='current_date', address='new address', phone='new phone', money_owed_to_us=money_owed_to_us-125 WHERE customer_id=id AND address='old address' AND phone='old phone';
これは非常に効率的で、別のクライアントがpay_back
または
money_owed_to_us
カラムの値を変更していても動作します。
多くの場合、ユーザは、複数のテーブルの一意の識別子を管理するために
LOCK TABLES
や
ROLLBACK
を使用していました。これは、AUTO_INCREMENT
カラムおよびSQL関数LAST_INSERT_ID()
またはC
API関数mysql_insert_id()
を使用することで、ロックやロールバックをせずにはるかに効率的に処理することができます。項11.10.3. 「情報関数」
および 項23.2.3.37. 「mysql_insert_id()
」
を参照してください。
通常、行レベルのロックをコード化することができます。状況によっては実際にこれが必要なので、InnoDB
テーブルでは行レベルのロックがサポートされています。MyISAM
テーブルでは、テーブルでフラグカラムを使用し、以下のようなことを実行することができます。
UPDATE tbl_name
SET row_flag=1 WHERE id=ID;
レコードが見つかり、元のレコードで
row_flag
がすでに1
でなくなっていた場合、MySQL
は、影響を受けた行の数として1
を返します。MySQL
サーバでは前述のステートメントが次のように変更されると考えることができます。
UPDATE tbl_name
SET row_flag=1 WHERE id=ID AND row_flag <> 1;