Halloween Protection (HP) について

Halloween Protection (HP) について

Rate This
  • Comments 0

今回はHalloween Protection (以降は HP とします) についてお話します。この問題はデータベース全般的に発生する可能性のあるハロウィーン問題に対する対策を意味したもので、日本ではハロウィーン保護とも呼ばれています。

HPの名前は、30年以上も前ですがIBMの研究者が問題を発見した日がハロウィーンの日(October 31)だったことにちなんでいます。この問題はUPDATEステートメントを実行する際に発生する可能性があることが指摘された問題ですが、UPDATEステートメント以外でもINSERT、DELETE、そして MERGE ステートメントが実行される際にも発生する可能性のある問題として指摘されています。

ここでは、問題が最初に発見されたUPDATEステートメントにおける問題を説明します。この問題が発生する可能性がある場合は、UPDATEステートメントのUPDATE対象が、UPDATEするために選択されてきたリストに存在する場合です。

それではこの問題を具体的に見ていきましょう。

HPの問題が最初に発覚したのは、ある会社で年収 $25,000(当時)以下の社員全員に10%の昇給を行うことが決定され、そのデータ更新が社員データベースに実行されたときでした。データ更新はエラーなく終了し、年収約$25,000以下の社員に限定されていたはずが、データ更新のクエリは すべての 社員が最低でも$25,000 になるまで10%の昇給をし続けました。つまり、例えば $10,000 の人は $11,000 になるべきところを、$25,000 以上になるまで(WHERE句の対象から外れるまで)更新の対象となり続けたことを意味します。

どうしてそのようなことが起きたのでしょうか?

まず、UPDATEが実行されるときの動作をみてみましょう。一般的にUPDATEには以下の3つのフェーズがあります。

1.WHERE句の条件に合致する行を読み取ります。
2.1で取得された行に対しUPDATEが実行されます。
3.UPDATE実行時に制約違反がないかどうか等、データベースの整合性が保たれるよう確認がされます。

このフェーズは1回1行ずつ取得され実行されます。WHERE句の条件に合致すれば、10%の昇給が実施され、最終的にすべての行がWHERE句の条件に合致しなくなるまで繰り返し実行されました。

たとえば以下のようなイメージです。
注意:ここでは例としてHPの名前の由来となった状況をもとに説明しますが、現在SQL Server上で同様のクエリを実行しても同様の実行プランは得られません。これは都度問題が発見され次第修正されてきていることを示します。都度修正が発生するのはハロウィーン問題が現在は複雑かつ特殊な条件下で起こりうる可能性があるものの、発見することが容易ではないことが関係しています。

以下のような社員名簿があり、こちらの年収が $25,000 以上の人を 10%昇給するとします。

CREATE TABLE dbo.Employees

(

名前 nvarchar(50) NOT NULL,

年収 money NOT NULL

);

INSERT dbo.Employees (名前, 年収)

VALUES

('佐藤', $22000),

('田中', $21000),

('鈴木', $25000);

CREATE NONCLUSTERED INDEX nc1

ON dbo.Employees (年収);

UPDATE Employees

SET 年収= 年収* $1.1

FROM dbo.Employees AS e

WHERE 年収< $25000;

UPDATEが実行される前は以下のような状態です。

clip_image002

クエリ実行プランは以下のようなイメージです。1回1行ずつ non-clustered index からWHERE句に合致する行が取得されます。
clip_image003

取得された行に対し、Compute Scalar、たとえば佐藤さんの22,000 が対象になり佐藤さんの年収は 24,200に昇給されます。次に田中さんの21,000 が対象になります。田中さんの年収は同様に計算され23,100 に昇給されます。

clip_image005

さらにWHERE句の条件に合致する行が検索され、佐藤さんの 24,200 が対象となり、26,620 に昇給されます。田中さんも同じく23,100 から 25,410に昇給されます。

clip_image007

ここでようやく WHERE句の条件に合致する行がなくなったのでUPDATEの実行が終了します。しかし、これは意図していた結果ではありません。

うしたらこのようなことを防ぐことができるでしょうか?

それは、クエリオプティマイザーにおいて、UPDATEをするために選択されてきた行のリストと、UPDATE対象のリストをそれぞれ個別のものとして扱うように強制することによって互いに影響されることを防ぐことができます。

たとえば上記の例では以下のように Table Spool(Eager Spool)が入ります。

clip_image008

このTable Spool(Eager Spool)が入ることにより、UPDATE対象の行がすべて最初に取得されます。そのリストをもとに、更新が実行されるため上記で発生したような繰り返し更新を実行する状況を避けることができます。

上記の例では、以下のリストがTable Spool(Eager Spool)されます。

clip_image010

このリストにある行を1行ずつ順番にUPDATEするため、繰り返して更新されることを避けることができます。

なお、マイクロソフトはハロウィーン問題に真摯に取り組むため、ハロウィーン問題の可能性が発見された場合はすぐにハロウィーン保護を導入し製品を日々改善しています。 また Craig Freedman’s SQL Server Blogでも触れられていますが、これらの修正によりデータをスプール(Eager Spool)する分時間がかかる場合もあり、パフォーマンスに影響がでる場合もありますが、常にデータの一貫性が最優先事項として選択されるため、このような動きとなっています。

参考

マイクロソフト SQL Serverチームからのハロウィーン保護についての取り組みについては以下をご覧ください。(英文)

Craig Freedman’s SQL Server Blog

Paul Whiteによるハロウィーン問題についての完結シリーズ

--

SQL Server Support Escalation Engineer
Kayoko Gray

Leave a Comment
  • Please add 3 and 4 and type the answer here:
  • Post