デフォルトでは、dbmlsync によって同期対象のテーブルがロックされてからアップロード・スクリプトが呼び出され、このロックはダウンロードがコミットされるまで保持されます。拡張オプション LockTables をオフに設定すると、テーブルのロックを無効にできます。
可能な場合は、テーブルをロックするデフォルトの動作を使用することをおすすめします。テーブルをロックしないでスクリプト化されたアップロードを行うと、考慮する必要がある問題が増え、実行可能な正しい解決法を作成するのが難しくなります。これは、データベースの同時実行性と同期の概念をよく理解している上級ユーザだけが行ってください。
テーブルのロックがオフのときは、アップロードのストアド・プロシージャを実行する独立性レベルが非常に重要です。独立性レベルによって、コミットされていないトランザクションの処理方法が決まるからです。テーブルのロックがオンのときは、テーブルのロックによって、アップロードの構築時に、同期対象のテーブルに対するコミットされていない変更が防止されるので、独立性レベルは問題ではありません。
アップロードのストアド・プロシージャで独立性レベルを明示的に変更しなかった場合、ストアド・プロシージャは、dbmlsync のコマンド・ラインで指定したデータベース・ユーザのデフォルトの独立性レベルで実行されます。
データベースのデフォルトの独立性レベルは 0 ですが、テーブルをロックしないでスクリプト化されたアップロードを行うときは、アップロードのプロシージャを独立性レベル 0 で実行しないことをおすすめします。テーブルをロックしないでスクリプト化されたアップロードを実行するときに独立性レベル 0 を使用すると、コミットされていない変更がアップロードされ、次の問題が発生する可能性があります。
コミットされていない変更がロールバックされると、間違ったデータが統合データベースに送信されます。
コミットされていないトランザクションが完了していない場合は、トランザクションの一部だけがアップロードされ、統合データベースの整合性が失われる可能性があります。
使用できる独立性レベルは 1、2、3、またはスナップショットです。これらの独立性レベルでは、コミットされていないトランザクションはアップロードされません。
独立性レベル 1、2、または 3 を使用した場合、テーブルに対してコミットされていない変更があると、アップロードのストアド・プロシージャがブロックする可能性があります。アップロードのストアド・プロシージャは、dbmlsync が Mobile Link サーバに接続している間に呼び出されるので、サーバ接続が占有される可能性があります。独立性レベル 1 を使用した場合、SELECT 文で READPAST テーブル・ヒント句を使用することでブロックを防止できる場合があります。
スナップショット・アイソレーションを使用すると、ブロックと、コミットされていない変更の読み込みの両方を防止できます。
テーブルをロックしない場合は、同期の発生時にコミットされていない操作を処理するメカニズムが必要です。なぜこれが問題であるかを理解するために、次に例を示します。
スクリプト化されたアップロードでテーブルを同期するとします。わかりやすくするために、挿入だけをアップロードするとします。テーブルには insert_time というカラムがあります。このカラムは、各ローが挿入された時刻を示すタイムスタンプです。
各アップロードは、テーブル内で insert_time が最後の正常なアップロードと、現在のアップロードの構築を開始した時刻 (sp_hook_dbmlsync_set_upload_end_progress フックが呼び出された時刻) の間にあるすべてのコミットされたローを選択して構築します。次の処理が行われるとします。
時間 | |
---|---|
1:00:00 | 正常な同期が発生します。 |
1:04:00 | ロー R がテーブルに挿入されますが、コミットされません。R の insert_time カラムが1:04:00 に設定されます。 |
1:05:00 | 同期が発生します。insert_time が 1:00:00 と 1:05:00 の間にあるローがアップロードされます。ロー R はコミットされていないのでアップロードされません。同期の進行状況が 1:05:00 に設定されます。 |
1:07:00 | 1:04:00 に挿入されたローがコミットされます。R の insert_time カラムは1:04:00 のままです。 |
1:10:00 | 同期が発生します。insert_time が 1:05:00 と 1:10:00 の間にあるローがアップロードされます。ロー R は、insert_time がこの範囲内にないのでアップロードされません。つまり、ロー R はアップロードされることはありません。 |
一般に、同期の前に発生し、同期の後にコミットされた操作は、このように失われる可能性があります。
コミットされていないトランザクションを処理する方法として最も簡単なのは、sp_hook_dbmlsync_set_upload_end_progress フックを使用して、各同期の終了進行状況を、フックの呼び出し時にコミットされていない最も古いトランザクションの開始時刻に設定する方法です。この時刻は、次のように sa_transactions システム・プロシージャを使用して確認できます。
SELECT min( start_time ) FROM sa_transactions() |
この場合、アップロードのストアド・プロシージャでは、sa_transactions を使用して sp_hook_dbmlsync_set_upload_end_progress フックで計算され、#hook_dict テーブルを使用して渡される終了進行状況を無視する必要があります。ストアド・プロシージャでは、単に開始進行状況後に発生したコミットされた操作をすべてアップロードします。このようにすると、変更内容をアップロードする必要があるローがダウンロード内容で上書きされません。また、コミットされていないトランザクションがあっても、操作が適時にアップロードされます。
この解決法では、操作は失われませんが、一部の操作が複数回アップロードされる可能性があります。サーバ側のスクリプトは、複数回アップロードされる操作を処理するように記述する必要があります。この設定でローが複数回アップロードされる例を次に示します。
時間 | |
---|---|
1:00:00 | 正常な同期が発生します。 |
2:00:00 | ロー R1 が挿入されますが、コミットされません。 |
2:10:00 | ロー R2 が挿入され、コミットされます。 |
3:00:00 | 同期が発生します。1:00 と 3:00 の間に発生した操作がアップロードされます。ロー R2 はアップロードされます。コミットされていない最も古いトランザクションの開始時刻が 2:00 なので、進行状況は 2:00 に設定されます。 |
4:00:00 | ロー R1 がコミットされます。 |
5:00:00 | 同期が発生します。2:00 と 5:00 の間に発生した操作がアップロードされ、進行状況が 5:00 に設定されます。アップロードには R1 と R2 の両方のローが含まれます。いずれもタイムスタンプがアップロード範囲内にあるからです。したがって、R2 は 2 回アップロードされます。 |
統合データベースが SQL Anywhere の場合は、統合データベースの upload_insert スクリプトで INSERT ... ON EXISTING UPDATE 文を使用することで、複数回アップロードされる挿入操作を処理できます。
その他の統合データベースについては、upload_insert スクリプトから呼び出すストアド・プロシージャで似たような論理を実装できます。挿入するローとプライマリ・キーが同じであるローが統合データベースにあるかどうかを確認するだけです。ローがある場合は更新し、ない場合は新しいローを挿入します。
サーバ側に競合を検出または解決する論理がある場合は、削除操作と更新操作が複数回アップロードされることが問題になります。サーバ側で競合の検出と解決のスクリプトを記述する場合は、スクリプトで複数回のアップロードを処理できる必要があります。
統合データベースでプライマリ・キーの値を再利用できる場合は、削除操作が複数回アップロードされることが重大な問題になります。次の一連のイベントを考えてみます。
プライマリ・キーが 100 のロー R がリモート・データベースに挿入され、統合データベースにアップロードされます。
ロー R がリモート・データベースで削除され、削除操作がアップロードされます。
プライマリ・キーが 100 の新しいロー R' が統合データベースに挿入されます。
手順 2 のロー R の削除操作がリモート・データベースからもう一度アップロードされます。これにより、ロー R' が統合データベースから間違って削除される可能性があります。
Copyright © 2009, iAnywhere Solutions, Inc. - SQL Anywhere 11.0.1 |