Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

UPDATE文と下位独立性レベル

原文:UPDATE statements and lower isolation levels

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2011/09/07 14:00

 アプリケーション開発者は、直列化可能トランザクションのセマンティクスを犠牲にし、ロック競合の可能性を制限することで実行時間のパフォーマンス向上を優先させるという選択を日常的に行っています。(原文:UPDATE statements and lower isolation levels、2011/07/21投稿)

 本稿はデータベースソフトウェア「SQL Anywhere」およびデータベース全般に関する英語ドキュメントを翻訳する形で提供しています。図など、部分的に英語のままになっていますが、製品のSQL Anywhere自体は完全に日本語化されていますのでご安心ください。
※編集部注:掲載しているブログ内容は執筆時点での情報のため、現在とは異なる場合があります。

 ISO/ANSI SQL独立性レベル3(SERIALIZABLE)で実行されるアプリケーションはほとんどありません。実際のところ、SQL Anywhereのデフォルトの独立性レベルは0(READ UNCOMMITTED)で、JDBCアプリケーションに限り、デフォルトはREAD COMMITTEDとなっています。

 SQL AnywhereREAD UNCOMMITTED独立性レベルでは、操作中にスキーマロックとローの書き込みロックのみがトランザクションによって取得され、ローの読み込みロックはまったく取得されません。したがって、READ UNCOMMITTEDでは、書き込みトランザクションは読み込みトランザクションをブロックしません。しかしその一方、SQL AnywhereはREAD UNCOMMITTED独立性レベルでのセマンティクスを保証していません。俗な言葉で言うなれば、支払っただけのものしか手に入らない、ということです。多くのアプリケーションでは、コミットされていないローの危険性や影響は限られているため、時として、READ UNCOMMITTEDの本当の意味が正しく理解されていない場合があります。本稿では、その影響がよりはっきりとする例を示してみたいと思います。

セットレベルのUPDATE操作

 1997年頃のSQL Anywhereバージョン5.5リリースで、セットレベルUPDATE文のフルサポートが導入され、当時の最新SQL標準であったISO SQL/1992標準のサポートにおいて、テーブルのPRIMARY KEYUNIQUE制約の一部、またはユニークインデックスの一部であるカラムを変更することができました。説明のために、次のようなテーブルを想定してみます。

CREATE TABLE updkey ( a INTEGER PRIMARY KEY, b INTEGER UNIQUE, c VARCHAR(500) )

 ここで、次のINSERT文を使用してデータを挿入してみます。

INSERT INTO updkey(a,b,c) SELECT row_num, row_num, 'test string' 
FROM rowgenerator WHERE row_num <= 10

 この例では、単一のアトミック文を使用して10個のローすべての「b」値を再番号付けしたいと思います。これは、以下で実行することができます。

UPDATE updkey SET b = 11-b, c = 'New value'

 おわかりと思いますが、このUPDATE文をロー単位で実行することはできません。updkeyテーブル内のどのローを更新しても、カラム「b」の一意性制約に即座に違反するからです(ちなみに、ここでWAIT_ON_COMMIT接続オプションを使用すればうまくいくと思うかもしれませんが、WAIT_ON_COMMITは参照整合性制約のみに作用するもので、一意性制約には影響しません)。したがって、SQL Anywhereバージョン5.5にはこのような更新を実行するために別のメカニズムが用意されています。詳しくはこれから見ていきますが、このメカニズムは同時制御の下位レベルに影響を与えます。

HOLD一時テーブル

 SQL AnywhereサーバーがUPDATEまたはMERGE文を処理する際に、プライマリーキー、ユニークインデックス、または一意性制約で一意性制約違反が発生した場合、サーバーは自動的に名前のない「HOLD(保留)」一時テーブルを作成して、問題のあるローを一時的に格納します。この一時テーブルには、ローの変更前と変更後の両方の値が含まれているので、AFTERローおよびAFTER文トリガーが正常に機能できます。ローの処理は、ロー単位で次のように実行されます。

  1. 一意性制約違反なしでローを変更できる場合は、更新は通常通り処理されます。
  2. 変更によって一意性制約の違反が発生した場合は、次のようにします。
  3. ローの内容がその新しい値とともに保留一時テーブルにコピーされます。
  4. ローは、そのインデックスエントリとともに、「一時的に」ベーステーブルから削除されます。この一時的な削除ではDELETEトリガーは起動しません。
  5. 該当するAFTERロートリガーがこのローに対して起動します。

 すべてのローが処理された後で、保留一時テーブルにコピーされた削除済みローが、UPDATEまたはMERGE文による変更値とともにベーステーブルに再挿入されます。保留一時テーブルからのローの処理順序は保証されていません。保存されたローの再挿入で一意性違反が''まだ''発生する場合は、UPDATE文またはMERGE文全体がロールバックされて、一意性制約違反がアプリケーションにレポートされます。

 すべてのロー変更が正常に実行された場合のみ、要求に応じてAFTER文トリガーが起動されます。

影響

 INSERTまたはMERGE文の実行中にローを削除すると、以下の結果に影響する可能性があります。

  • このアクションを開始したUPDATE文またはMERGE文に対して起動されたAFTERロートリガー内で発行される、同じテーブルを問い合わせるSQL文
  • SERIALIZABLEまたはSNAPSHOT独立性レベルで実行していないその他の接続(イベントハンドラを含む)

 セットレベル更新操作におけるこの処理のセマンティクスは、直感的には理解しにくいものです。というのも、表面的に考えれば、テーブルを同時に問い合わせている別の接続は、少なくとも古いロー値と新しいロー値のどちらかを「見る」だろうと思われるからです。しかし、一意性制約のあるテーブルでのセットレベルの更新操作では、使用されている独立性レベルによっては、別の接続が特定のローを''まったく見ない''可能性もあります。別の接続がSERIALIZABLE独立性レベルで実行中の場合は、更新を実行しようとするトランザクションがCOMMITまたはROLLBACKを発行するまではブロックされます。一方、別の接続がSNAPSHOT独立性レベルで実行中の場合、そのトランザクションは、トランザクションの期間中、変更されたローの元値を見ることになります。

 この複雑多岐な動作は、今まで文書化されていませんでした。これについては、次回のSQL Anywhereのメジャーリリースで、標準文書内に掲載する予定です。



  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • Glenn Paulley(Glenn Paulley)

    カナダ オンタリオ州 ウォータールー R&amp;DセンターにてSQL Anywhere 開発における Director of Engineering としてクエリ・オプティマイザなどの開発をリードしている。 ・IvanAnywhere

バックナンバー

連載:Glenn Paulley氏 データベース関連ブログ 翻訳記事

もっと読む

All contents copyright © 2005-2019 Shoeisha Co., Ltd. All rights reserved. ver.1.5