SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Glenn Paulley氏 データベース関連ブログ 翻訳記事(AD)

UPDATE文と下位独立性レベル

原文:UPDATE statements and lower isolation levels

  • X ポスト
  • このエントリーをはてなブックマークに追加

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

  • X ポスト
  • このエントリーをはてなブックマークに追加

 本稿はデータベースソフトウェア「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のメジャーリリースで、標準文書内に掲載する予定です。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加

【AD】本記事の内容は記事掲載開始時点のものです 企画・制作 株式会社翔泳社

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/6117 2011/09/07 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング