PSQL Programmer's Guide (v11)

並行制御 (リレーショナル インターフェイス)

トランザクショナル データベース エンジンとその自動修復機能は、データベースの物理的な整合性を制御します。Pervasive PSQL は、トランザクショナル データベース エンジンのトランザクションとレコード ロック機能を使用して、論理的なデータの整合性を提供します。Pervasive PSQL はトランザクショナル データベース エンジンと共に、以下のタイプの並行制御を提供します。

トランザクション処理

トランザクション処理は、単一のテーブル内であっても複数のテーブルにまたがっていても、論理的に関連する一連のデータベース変更を識別できるようにし、これを 1 つの単位として完了させるように要求します。トランザクション処理には 2 つの重要な概念があります。

START TRANSACTION ステートメントがトランザクションを開始します。トランザクション中に完了したいすべてのステートメントを発行したら、COMMIT WORK ステートメントを発行してトランザクションを終了します。COMMIT WORK ステートメントはすべての変更を保存し、これを恒久的なものにします。


メモ

START TRANSACTION および COMMIT WORK は、ストアド プ ロシージャでのみ使用できます。これら 2 つの SQL ステートメントの詳細については、『SQL Engine Reference』を参照してください。


操作の 1 つでエラーが発生した場合、トランザクションをロールバックし、エラーを修正した後、再試行することができます。たとえば、いくつかのテーブルに関連する更新を行う必要があるけれども、更新の 1 つが失敗した場合、既に行った更新をロールバックすることができるので、データは矛盾しません。

2 つのタスクが ログイン セッションを共有していて、セッションを開いたタスクが、もう 1 つのタスクがトランザクションを完了する前にログアウトした場合にも Pervasive PSQL は自動的にロールバック操作を行います。

トランザクションの開始と終了

トランザクションを開始するには、ストアド プロシージャ内で START TRANSACTION ステートメントを発行します。トランザクション中に完了したいすべてのステートメントを発行したら、COMMIT WORK ステートメントを発行して変更をすべて保存し、トランザクションを終了します。

START TRANSACTION;  
UPDATE Billing B  
   SET Amount_Owed = Amount_Owed - Amount_Paid  
   WHERE Student_ID IN  
   (SELECT DISTINCT E.Student_ID  
   FROM Enrolls E, Billing B  
      WHERE E.Student_ID = B.Student_ID);  
COMMIT WORK;  

START TRANSACTION ステートメントの詳細については『SQL Engine Reference』を参照してください。

ネストされたトランザクションへのセーブポイントの使用

SQL トランザクションでは、セーブポイントと呼ばれるマーカーを定義することができます。セーブポイントを使用すると、トランザクション内のセーブポイント以降の変更を元に戻して最後のコミットを要求する前までの変更を継続して追加したり、トランザクション全体を中止することができます。

トランザクションを開始するには、START TRANSACTION ステートメントを使用します。ROLLBACK または COMMIT WORK ステートメントを発行するまでトランザクションはアクティブです。

セーブポイントを設定するには、SAVEPOINT ステートメントを使用します。

SAVEPOINT SP1; 

セーブポイントにロールバックするには、ROLLBACK TO SAVEPOINT ステートメントを使用します。

ROLLBACK TO SAVEPOINT SP1; 

セーブポイント名は、現在の SQL トランザクション内の現在アクティブなセーブポイントを指定する必要があります。このセーブポイントを設定した後の変更はキャンセルされます。

セーブポイントを削除するには、RELEASE SAVEPOINT ステートメントを使用します。

RELEASE SAVEPOINT SP1; 

このステートメントは SQL トランザクションがアクティブな場合のみ使用できます。

COMMIT WORK ステートメントを発行した場合、現在の SQL トランザクションによって定義されたセーブポイントはすべて破棄され、トランザクションがコミットされます。


メモ

ROLLBACK TO SAVEPOINT と ROLLBACK WORK を混同しないでください。前者は指定したセーブポイントまでの操作をキャンセルし、一方後者は最も外側のトランザクションとその中にあるセーブポイントをすべてキャンセルします。


セーブポイントはトランザクションをネストする方法を提供します。これによりアプリケーションは、一連のステートメントが正常に完了するのを待つ間、トランザクション内の前の操作を保存することができます。たとえば、この目的で WHILE ループを使用することができます。最初の試行で失敗する可能性のある一連のステートメントの開始前にセーブポイントを設定することができます。トランザクションが進行する前に、このサブトランザクションが正常に完了する必要があります。失敗した場合、サブトランザクションはセーブポイントにロールバックし、そこから再試行できます。サブトランザクションが成功した場合、トランザクションの残りの部分が続行されます。

SAVEPOINT ステートメントを発行するときは SQL トランザクションがアクティブである必要があります。


メモ

MicroKernel は各トランザクションが内部的にネストするレベルを合計 255 まで許可します。ただし、Pervasive PSQL は INSERT、UPDATE、DELETE ステートメントでアトミシティを保証するために内部的にこれらのレベルをいくつか使用します。したがって、1 つのセッションでは事実上 253 を越えるセーブポイントを一度にアクティブにすることはできません。トランザクション中に INSERT、UPDATE、DELETE ステートメントが含まれていると、トリガーによってこの制限はさらに厳しくなります。この制限に達した場合は、セーブポイントの数か、トランザクションに含まれるアトミック ステートメントの数を減らします。


セーブポイント内でロールバックされた操作は、外側のトランザクション(1 つまたは複数)が正常に完了してもコミットされません。ただし、セーブポイント内で完了した操作は、最も外側のトランザクションによって、物理的にデータベースにコミットされる前にコミットされます。

たとえば、サンプル データベースで、学生をいくつかのクラスに登録するトランザクションを開始するとします。最初の 2 つのクラスで学生を正常に登録したとしても、3 番目のクラスで失敗する可能性があります。これは、クラスが定員を満たしていたり、学生が登録している別のクラスと衝突するためです。学生をこのクラスに登録するのに失敗したとしても、前の 2 つのクラスへの登録をやり直したいとは考えないでしょう。

次のストアド プロシージャは、まず最初にセーブポイント SP1 を設定し、次に Enrolls テーブルにレコードを追加して学生をクラスに登録します。それからクラスへの現在の登録を決定し、クラスの最大定員と比較します。比較に失敗した場合、SP1 にロールバックします。成功した場合はセーブポイント SP1 を解放します。

CREATE PROCEDURE Enroll_student( IN :student ubigint, IN 
:classnum integer);  
BEGIN  
  DECLARE :CurrentEnrollment INTEGER;  
  DECLARE :MaxEnrollment INTEGER;  
  SAVEPOINT SP1;  
  INSERT INTO Enrolls VALUES (:student, :classnum, 0.0);  
  SELECT COUNT(*) INTO :CurrentEnrollment FROM Enrolls
    WHERE class_id = :classnum;  
  SELECT Max_size INTO :MaxEnrollment FROM Class 
    WHERE ID = :classnum;  
  IF :CurrentEnrollment >= :MaxEnrollment  
  THEN  
    ROLLBACK to SAVEPOINT SP1;  
  ELSE  
    RELEASE SAVEPOINT SP1;  
  END IF;  
 
END; 


メモ

SQL レベルで操作する場合、トランザクションはインターフェイスによって異なる方法で制御されます。ODBC では、トランザク ションは SQLSetConnectOption API の SQL_AUTOCOMMIT オプショ ンを使用することにより、関連する SQLTransact API も使用して制御 されます。


これらのステートメントの構文についての詳細は、『SQL Engine Reference』の各ステートメントの項を参照してください。

特に考慮すべき点

トランザクションは、次の操作には影響しません。

トランザクション内でこれらの操作のいずれかを試行し Pervasive PSQL がステートメントを完了した場合、結果をロールバックすることはできません。

トランザクション中に、既にあるテーブルを参照している場合、トランザクション中にそのテーブルを変更または削除することはできません。つまり、辞書定義を変更することはできません。たとえば、トランザクションを開始し、Student テーブルにレコードを挿入し、Student テーブルを変更しようとすると、ALTER ステートメントは失敗します。このトランザクションから操作をコミットし、それからテーブルを変更する必要があります。

分離レベル

同様にトランザクション中にあるほかのユーザーからトランザクションが分離する範囲を定義することにより、分離レベルはトランザクション ロック単位の適用範囲を決定します。分離レベルを使用すると、Pervasive PSQLは、指定した分離レベルに応じて自動的にページまたはテーブルをロックします。これらの自動ロックは、Pervasive PSQL が内部的に制御するものですが、暗黙ロックまたはトランザクション ロックと呼びます。アプリケーションが明示的に指定したロックは明示的ロックと呼びます。以前はレコード ロックと呼んでいました。詳細については、明示的ロックを参照してください。

Pervasive PSQL はトランザクションのために 2 つの分離レベルを提供します。

分離レベルは、ODBC API の SQLSetConnectOption を使用して設定します。

排他的分離レベル(SQL_TXN_SERIALIZABLE)

排他的分離レベルを使用する場合、ロック単位はデータ ファイル全体です。排他トランザクション内で 1 つまたは複数のファイルにアクセスすると、ファイルは、トランザクション内のほかのユーザーが行う同様のアクセスからロックされます。このタイプのロックは、同時に同一テーブルにアクセスを試みるアプリケーションが非常に少ない場合や、トランザクションが行われている間にファイルの大部分がロックされるような場合に最も有効です。

Pervasive PSQL は、トランザクションが終了するとファイルのロックを解除します。排他トランザクション中にテーブルにアクセスする場合、次の状態になります。

排他的分離レベルを使用して結合ビューを介してテーブルにアクセスする場合、Pervasive PSQL はビュー内でアクセスされたすべてのファイルをロックします。

カーソル安定性分離レベル(SQL_TXN_READ_COMMITTED)

トランザクショナル データベース エンジンはデータ ファイルを一連のデータ ページとインデックス ページとして保持します。カーソル安定性分離レベルを使用する場合、ロック単位はデータ ファイルではなく、データ ページまたはインデックス ページです。カーソル安定性トランザクション内でレコードを読み込むと、Pervasive PSQL はこれらのレコードが含まれるデータ ページをロックし更新可能にします。しかし、複数のトランザクション中のタスクによってテーブルが並行アクセスされることは許可します。これらのレコード ロックは、ほかのレコードのセットを読み込む場合にのみ解放されます。Pervasive PSQL はレベル カーソル安定性をサポートします。これによりアプリケーションが同時に複数のレコードをフェッチできるためです。

さらに、データ ページまたはインデックス ページに対する変更は、次の読み込み操作を発行したとしても、トランザクションの継続中これらのレコードをロックします。操作をコミットまたはロールバックするまで、トランザクション中のほかのユーザーはこれらのロックされたレコードにアクセスすることはできません。ただし、ほかのアプリケーションは、それぞれのトランザクション内から、同一ファイルの別のページをロックすることはできます。

カーソル安定性トランザクション中にファイルにアクセスする場合、Pervasive PSQL はデータ ページおよびインデックス ページを次のようにロックします。

カーソル安定性は、ほかのユーザーが同一データ ファイルのほかのデータ ページにアクセスすることを許可しながら、読み込んだデータを確実に安定した状態に保つことができます。カーソル安定性分離レベルでは、一度に読み込める行の数を制限することにより、一度にロックされるデータ ページ数が少なくなり、一般的にすべてのタスクでより優れた並行性を実現できます。これにより、ロックするページが少ないため、ほかのネットワーク ユーザーは、データ ファイルのより多くのページにアクセスできます。

ただし、アプリケーションが多数の行をスキャンまたは更新する場合、影響するテーブルからほかのユーザーを完全にロックする可能性が高くなります。したがって、小さなトランザクションで読み込み、書き込み、コミットを行う場合にカーソル安定性を使用するのが最も良い方法です。

カーソル安定性はサブクエリ内のレコードをロックしません。カーソル安定性は、行が返された状態が変更されないことを保証するのではなく、実際に返された行が変更されないことを保証します。

トランザクションと分離レベル

トランザクション内でデータにアクセスする場合はいつでも、Pervasive PSQL はアクセスされたページまたはファイルをそのアプリケーションのためにロックします。ほかのアプリケーションは、ロックが解除されるまで、ロックされたページまたはファイルに書き込むことはできません。

カーソル安定性分離レベルを使用すると、結合ビューでテーブルにアクセスする場合、Pervasive PSQL はビュー内のすべてのテーブルのアクセスされたページをロックします。カーソル安定性分離レベルを使用すると、結合ビューでテーブルにアクセスする場合、Pervasive PSQL はビュー内のすべてのテーブルのアクセスされたページをロックします。

Pervasive PSQL はノーウェイト トランザクションを実行します。別のタスクがロックしているレコードに、トランザクション内からアクセスした場合、Pervasive PSQL はページまたはテーブルがロックされているか、デッドロックが検出されたことを知らせます。いずれの場合にも、トランザクションをロールバックし、再試行してください。Pervasive PSQL では、同一のアプリケーション内から同一のデータ ファイルにアクセスする複数のカーソルを使用できます。

次の手順は、2 つのアプリケーションがトランザクション内から同一テーブルにアクセスする場合にどのように相互作用するかを示しています。手順には番号が付けられていて、発生した順を示します。

タスク 1
タスク 2
1. ビューをアクティブにします。
 
 
2. ビューをアクティブにします。
3. トランザクションを開始します。
 
 
4. トランザクションを開始します。
5. レコードをフェッチします。
 
 
6. 同一データ ファイルからレコードのフェッチを試行します。
 
7. 両方のタスクがカーソル安定性を使用していて、タスク 2 が、既にタスク 1 がロックしているのと同じレコードをフェッチしようとすると、ステータス コード 84(レコードまたはページがロックされている)を受け取ります。どちらか一方のタスクが排他トランザクションを使用している場合は、ステータスコード 85(ファイルがロックされている)を受け取ります。
 
8. 必要に応じフェッチを再試行します。
9. レコードを更新します。
 
10. トランザクションを終了します。
 
 
11. フェッチに成功します。
 
12. レコードを更新します。
 
13. トランザクションを終了します。

トランザクションは、ほかのアプリケーションの更新に対し、一時的にレコード、ページ、またはテーブルをロックするため、アプリケーションはトランザクション中にオペレーター入力のための中断を行ってはいけません。これは、オペレーターが応答するかトランザクションが終了されるまで、トランザクションからアクセスされているレコード、ページまたはテーブルを、ほかのどのアプリケーションも更新できないためです。


メモ

カーソル安定性トランザクション内でのレコードの読み込みは、それに続く更新処理が競合なしに成功することを保証するものではありません。これは、Pervasive PSQL が更新を完了するのに必要とするインデックス ページを、ほかのアプリケーションが既にロックしていることがあるためです。


デッドロックの回避

デッドロック状態は、2 つのアプリケーションが、一方が既にロックしたテーブル、データ ページ、インデックス ページ、またはレコードに対し操作を再試行する場合に発生します。デッドロックの発生を最小限に抑えるには、アプリケーションでトランザクションのコミットを頻繁に行います。アプリケーションから操作の再試行を行わないでください。Pervasive PSQL はエラーを返す前に妥当な回数の再試行を行います。

排他的分離レベル下のデッドロック状態

排他的分離レベルを使用する場合、Pervasive PSQL は、データ ファイル全体をほかのアプリケーションの更新からロックします。したがって、アプリケーションが同じ順序でデータ ファイルにアクセスしない場合、次の表のようにデッドロックが起こる可能性があります。

タスク 1
タスク 2
1. トランザクションを開始します。
 
 
2. トランザクションを開始します。
3. ファイル 1 からフェッチします。
 
 
4. ファイル 2 からフェッチします。
5. ファイル 2 からフェッチします。
 
6. ロックのステータス コードを受け取ります。
 
7. 手順 5 を再試行します。
 
 
8. ファイル 1 からフェッチします。
 
9. ロックのステータス コードを受け取ります。
 
10. 手順 8 を再試行します。

カーソル安定性分離レベル下のデッドロック状態

カーソル安定性分離レベルを使用する場合、アプリケーションがアクセスしているファイルのレコードまたはページ(アプリケーションがロックしていないレコードまたはページ)を、ほかのアプリケーションが読み込み、更新することができます。

明示的ロック

トランザクション外で並行制御を行いたい場合は、Pervasive PSQL ODBC ドライバー拡張を使用して SQLSetStmtOption に明示的ロックを使用することができます。

これらのロックは、タスクがそのロックの設定を行う必要があるため、明示的ロックと呼びます。明示的ロックでは、トランザクションのように操作をロール バックすることはできません。

排他的ロックの実行を可能にする ODBC ドライバー拡張について、次の表に示します。

fOption
vParam
説明
1153
0(デフォルト)を指定するとテーブル ロックをオフにします。
1 を指定するとテーブル ロックをオンに切り替えます。
Pervasive ODBC エンジン インターフェイスの拡張:vParam が 1 に設定されている場合、hStmt でインデックスの選択、更新、挿入、削除、または作成ステートメントが実行されるとき、hStmt により使用されるすべてのテーブルは排他的にロックされます。
このテーブルのロックは、SQL_DROP オプションを伴う SQLFreeStmt 呼び出 しによって hStmt が削除されるか、vParam に 0 を設定して hStmt が再度実 行されるまで解除されません。ロックされたテーブルは、ロックを行っている hStmt だけが使用できます。ほかの hStmt からは使用できません。

この ODBC ドライバー拡張の詳細については、『SQL Engine Reference』を参照してください。

パッシブ コントロール

アプリケーションが単一レコードのフェッチを行い、論理的に関連しない一連の更新処理を行う場合、Pervasive PSQL の並行制御であるパッシブ メソッドを使用することができます。この方法を使用すると、トランザクションやレコード ロックを行わずに、レコードをフェッチ、更新、または削除することができます。これらの操作は楽観的更新および削除と呼ばれます。

タスクがトランザクションも明示的レコード ロックも使用しないで更新および削除操作を行う場合、デフォルトで、そのタスクはほかのタスクの変更を上書きできません。このデータの整合性を確実にするこの機能は、パッシブ コントロールで、楽観的並行制御と呼ばれることもあります。パッシブ コントロールでは、タスクはどのような種類のロックも行いません。既にフェッチしてあるレコードを別のタスクが変更した場合、更新または削除オペレーションを実行する前に、そのレコードを再度フェッチする必要があります。

パッシブ コントロールの下では、レコードをフェッチしてから更新または削除操作をする間に別のアプリケーションがそのレコードを更新または削除した場合、競合のステータス コードが返されます。これは、最初にデータをフェッチしてから、別のアプリケーションがそのデータに変更を加えたことを示します。競合のステータス コードを受け取った場合、更新または削除操作を実行する前にもう一度そのレコードをフェッチする必要があります。

パッシブ コントロールを使用すると、シングル ユーザー システムで設計されたアプリケーションを、ロック呼び出しを実装することなくネットワーク上で実行することができます。ただし、パッシブ コントロールは、負荷の軽いネットワーク環境で使用されるか、データがほとんど変化しないような場合にのみ有効です。負荷の高いネットワーク環境や変化の激しいデータの場合、パッシブ コントロールは有効ではありません。


データベース セキュリティの管理 (リレーショナル インターフェイス)

Pervasive PSQL データベースのアトミシティ(リレーショナル インターフェイス)