要旨
オートバキュームはブラックボックスであり、公正とされるテスト方法を考案することは不可能です。この制限を考慮して、使用されたテスト方法では、ほとんどアイドル状態のクラスターに30秒ごとに1つの取り越し苦労のないクエリが行われた非常に軽いサーバーロードで、オートバキュームを完全に防止することができました。これは、24時間365日稼働するクラスターでは、オートバキュームが発生しないことを意味しています。40秒、50秒、60秒というより長い間隔では、15分間のテスト実行中に5回オートバキュームが実行されました。観察されたオートバキュームのうち、削除バキュームのみが観察されました。ソートバキュームは観察されませんでした。
イントロダクション
Redshiftはソートされたリレーショナルデータベースであり、過去には表をソートすることがコストがかかるため、手動でSQLコマンドを発行する必要がありました。Redshiftのユーザーは、表をソートするためにVACUUM
コマンドを発行する必要がありました。
さらに、VACUUM
には、現在または将来のどのクエリでも読み取ることができないすべての行を表から削除するという第二の目的があります。
この第二の目的は、MVCCを使用してACIDを実装するために生じます(頭字語を調べるためのWikipediaリンクを示していますが、コンセプトを理解するためにそれらのページを実際に読もうとしないでください、それは不可能です)。
MVCCでは、各テーブルには、行が有効である期間を示すデータベースによって維持される2つの追加の列があります。例えば、UPDATEが発生した場合、更新される行は現在の有効期限が設定され、その後、更新からの変更を組み込んだ行のコピーが作成され、その有効期限の開始が現在に設定され、その有効期限の終了がNULLに設定されます(これは進行中を意味します)。
これの全体的なポイントは、クエリが実行されるときに、開始時刻を知り、その時刻で有効な行のバージョンのみを表示することです。
最終的には、テーブルには、有効期限が設定された行が残りますが、時間が経過し、現在実行されているすべてのクエリがその有効期限を超えているため、これらの行は今後完全に無用になります。 -どのクエリもそれらを再び見ることはできない-そして、VACUUM
の仕事は、最終的にテーブルからそのような行を永久に削除することです。
RedshiftのVACUUM
コマンドは、ソートのみを実行するように実行できます。削除された行のみを実行するように実行できます。または両方を実行することができます(「完全」VACUUM
)。
(実際には、VACUUM
の仕組み上、ソートのみの削除はせいぜいフルバキュームと同じくらいの時間がかかりますが、削除行が多ければ多いほどフルバキュームは速くなります。この点については、後日、VACUUM
に関するホワイトペーパーで説明します。今のところ、ソートのみのバキュームは絶対に使わないでくださいということです。deleteかfullしか使わないでください(笑)。
最近、2018年末には、自動バックグラウンド削除専用VACUUMが導入されました。
さらに最近、2019年末には、自動バックグラウンドソート専用VACUUMが導入されました。
どちらも必須であり、無効にすることはできません。
両方とも、システム管理者や開発者がこのタスクについて心配する必要がないようにVACUUM
を処理するとして文書に記載されています。
しかし、Redshiftの管理者と開発者との時間の経過とともに行われた議論により、自動VACUUMは頻繁に実行されすぎていないため、何も変化をもたらすことができない - また、まったく気づかれない可能性があることがわかりました。
残念ながら、私は多くのRedshift管理者や開発者と話をしてきましたが、彼らはクラスターやテーブルを調べることなく、自動VACUUMがすべてのVACUUM要件を完全に処理すると仮定しています。彼らはドキュメントを読んで、VACUUM
について考えるのを止めています。
したがって、このホワイトペーパーは、自動VACUUMの振る舞いと特性に関するいくつかの証拠を提供するための調査です。
テスト方法
10個のテーブルを作成します。テーブルはすべて同一で、1つのbigint列を持ち、完全にRAWエンコードされたブロックで構成され、キー配布されています。各スライスに1つの値があり、各スライスには同じ数のブロックがあります。2ノード4スライスのテストクラスターでは、1スライスあたり50ブロックあります。テーブルはすべて完全にVACUUMおよび分析されています。
クラスターは10スロットの1つのキューで構成されています。すべてのテーブルが準備できたら、各テーブルごとに1つのクエリを同時に発行し、各テーブルのすべての行を更新し、新しい値がそれぞれのスライスにある行のスライスに保たれるようにします。ここでのアイデアは、更新をできるだけ早く行い、同時にクラスターをビジー状態に保ち、テストが開始される前に自動VACUUMが開始されないようにすることです。
この時点で、各テーブルには、ソートされたが削除された行の200ブロックと、ソートされていない行の200ブロックがあります。
その後、300秒間、10秒ごとにシステムテーブルを調べて、VACUUMが発生するかどうかを確認し、ソートされたブロックと未ソートのブロックの数に変化があるかどうかを検出します。
その後、すべてのテーブルを削除して再作成し、ポーリング間隔を10秒増やします。
このプロセスを、最終的なポーリング間隔である60秒になるまで続けます。
削除バキュームが発生した時点、およびソートバキュームが発生した時点を記録します(どのポーリングイテレーションのどのポーリング間隔か)。
すべてのテストセットを5回繰り返します。
(このテスト設計の理由は、実験中に、クラスターにわずかな負荷がある場合でも(例えば、10秒ごとにシステムテーブルを調べる場合)、自動VACUUMを完全に防止できることが明らかになったためです。)
結果
結果の辞書のPython pprint
ダンプについては、付録Aを参照してください。
X軸はテストのイテレーション(5回ありました)を示しています。
Y軸はポーリングのイテレーションを示しています(各ポーリング間隔は300秒のテストです)。
10のテストテーブルがあり、各フィールドの値は「VACUUMできたテーブルの数 / ソートバキュームができたテーブルの数」です。
VACUUMされたテーブルの数が上昇するにつれ、その数は0(テーブルがVACUUMされなかった場合)から10(すべてのテーブルがVACUUMされた場合)まで上昇します。
テストの実行時間は15,388秒でした(サーバーの起動およびシャットダウン時間を含みます)。
dc2.large, 2 nodes (1.0.30840)
Polling Interval 10 Seconds
Poll Iteration | Iteration #0 | Iteration #1 | Iteration #2 | Iteration #3 | Iteration #4 |
---|---|---|---|---|---|
0 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
1 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
2 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
3 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
4 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
5 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
6 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
7 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
8 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
9 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
10 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
11 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
12 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
13 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
14 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
15 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
16 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
17 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
18 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
19 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
20 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
21 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
22 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
23 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
24 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
25 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
26 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
27 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
28 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
29 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
Polling Interval 20 Seconds
Poll Iteration | Iteration #0 | Iteration #1 | Iteration #2 | Iteration #3 | Iteration #4 |
---|---|---|---|---|---|
0 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
1 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
2 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
3 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
4 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
5 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
6 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
7 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
8 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
9 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
10 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
11 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
12 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
13 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
14 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
Polling Interval 30 Seconds
Poll Iteration | Iteration #0 | Iteration #1 | Iteration #2 | Iteration #3 | Iteration #4 |
---|---|---|---|---|---|
0 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
1 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
2 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
3 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
4 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
5 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
6 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
7 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
8 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
9 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
Polling Interval 40 Seconds
Poll Iteration | Iteration #0 | Iteration #1 | Iteration #2 | Iteration #3 | Iteration #4 |
---|---|---|---|---|---|
0 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
1 | 0/0 | 0/0 | 0/0 | 10/0 | 0/0 |
2 | 10/0 | 0/0 | 0/0 | 10/0 | 0/0 |
3 | 10/0 | 0/0 | 0/0 | 10/0 | 0/0 |
4 | 10/0 | 0/0 | 0/0 | 10/0 | 0/0 |
5 | 10/0 | 0/0 | 0/0 | 10/0 | 0/0 |
6 | 10/0 | 0/0 | 0/0 | 10/0 | 0/0 |
7 | 10/0 | 0/0 | 0/0 | 10/0 | 0/0 |
Polling Interval 50 Seconds
Poll Iteration | Iteration #0 | Iteration #1 | Iteration #2 | Iteration #3 | Iteration #4 |
---|---|---|---|---|---|
0 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
1 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
2 | 0/0 | 2/0 | 0/0 | 0/0 | 10/0 |
3 | 0/0 | 10/0 | 0/0 | 0/0 | 10/0 |
4 | 0/0 | 10/0 | 0/0 | 0/0 | 10/0 |
5 | 0/0 | 10/0 | 0/0 | 0/0 | 10/0 |
Polling Interval 60 Seconds
Poll Iteration | Iteration #0 | Iteration #1 | Iteration #2 | Iteration #3 | Iteration #4 |
---|---|---|---|---|---|
0 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
1 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
2 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
3 | 0/0 | 0/0 | 0/0 | 0/0 | 0/0 |
4 | 0/0 | 0/0 | 10/0 | 0/0 | 0/0 |
ディスカッション
まず、自動VACUUMがブラックボックスであるため、その動作に関与する要因を知ることは不可能であり、すべての議論と結論は、この特定のテスト方法からの観察に基づいているため、ほとんど完全に暫定的です。このテスト方法では、自動VACUUMの動作に関与するさまざまな要因が見落とされている可能性があります。
これが事実であることは、この議論の中心的な部分であり、ブラックボックスを含むシステムを正しく設計することはできないことを認識することが不可欠です。さらに、サードパーティによって制御され、予告なしに変更される可能性があるブラックボックスに特に当てはまります。
次に、結果についてですが、明らかになっている最初の行動は、ポーリング間隔が短いほど、自動VACUUMが防止されるように見えるということです。
これは注目すべきことです。
言い換えると、テストテーブルに全く触れず、システムテーブル上で30秒ごとに1つのクエリを発行することで、常に自動VACUUMが実行されなくなりました。
そのため、結果を全体として言葉で表現すると、10秒、20秒、30秒のポーリング間隔では、自動VACUUMは一度も実行されませんでした。
40秒と50秒の間隔では、自動VACUUMは5つのテストイテレーションのうち2つで実行されました(両方の場合、1つまたは2つのポーリング間隔後)。
60秒の間隔で、自動VACUUMは5つのイテレーションのうち1つで実行され、そのテストイテレーションの最後のポーリング間隔で実行されました。
5つのテストイテレーション全体で、30分かかります。
表面的には、これによって、自動VACUUMが実行される頻度が極めて低く、検出不可能であるというRedshift管理者の合意が説明されます。
実際のクラスターでは、数十人、あるいは数百人のユーザーが多数のテーブルを持ち、クエリが永遠に続く状態であるため、現在の自動VACUUMは全く検出されず、埋もれてしまうように見えます。
2番目に明らかな振る舞いは、ソートVACUUMは観測されなかったということです。削除VACUUMのみが発生しました。
AWSは、自動VACUUMをかなり強力に販売しています。
Redshiftの自動テーブルソートを使用すると、管理者はソートするものとソートするタイミングを追跡する必要がなくなります。Redshiftが自動的に処理します。Redshiftはバックグラウンドでソーティングを実行し、テーブル内のデータを再構成して、ソート順を維持し、最適なパフォーマンスを提供します。
この忠告を考慮すると、私が見つけたように、新しいRedshift管理者や開発者は自動VACUUMが完全にVACUUM問題を解決したと想像し、それ以上考えない傾向があるのは驚くべきことではありません。
しかし、この調査で発見した自動VACUUMの振る舞いを考えると、これらの人々は誤解されており、自動VACUUMおよびそのドキュメントは、世界中のRedshiftクラスターの誤った動作に大きく貢献しています。
自動VACUUM自体に戻ると、私たちはここで自動VACUUMがブラックボックスであることを確認しています。内部の動作については全くわかっていないため、外部で見える動作が明らかでなく、本質的に意味のあるものでない場合、何が起こっているのか理解できません。
ブラックボックスを含むシステムを知っている場合に、正しいシステムを知っているように設計することは不可能です。
ただし、正しいシステムを知っている方法がないRedshiftユーザーにとって、これは問題ではありません。そして、私が間違っているかもしれませんが、自動VACUUMは実際には彼らのために意図されたものであると思われます。
私の経験では、すべてではありませんが、非常にほとんどのRedshift管理者や開発者は、正しくRedshiftクラスターを操作する方法を知りません。
私のほとんど常にコンサルタントとしての経験は、契約を開始し、クライアントにRedshiftの内部動作、そしてそれが実際に使用できるものと、使用用途がRedshiftに適しているかを説明し、クライアントがRedshiftを無制限のユーザー、テーブル、クエリなどを持つ非ソートのロー・ストア・データベースのように扱っていることが明らかになります。そして、彼らがRedshiftに適した使用用途を見つけ、それを実現するためにRedshiftを正しく操作するために何をしなければならないかを理解します。
Redshiftの誤った操作は、単に管理者やユーザーが現在の能力を超えたタスクを割り当てられたためである場合もありますが、根本的には私がAWSが公開している意味のないドキュメンテーションの完全な欠如に起因すると考えています。
Redshiftを正しく操作する方法を正確に知ることはできません。そして、それが管理者や開発者の能力の範囲内で完全に可能であっても、それを行うために必要な情報が単に利用できないからです。
この時点で、Snowflakeを取り上げることが有益です。
Snowflakeは、Redshiftと同様にソートされたカラムストアです。ただし、完全に自動化されています。ハードウェアにいくら支払うかを設定する以外は、文字通りの構成はありません。すべての構成と実行はアルゴリズムによって設定されます。
図1:自動化は良い面もあるが、悪い面もあり、醜い面もある。
正しくソート済み列ストアデータベースを操作する方法を知らない場合、Snowflakeは優れています。自動化はあなたよりも優れた結果を出します。
しかし、正しくソート済み列ストアデータベースを操作する方法を知っている場合、Snowflakeは非常に不十分です。自動化はあなたよりも劣る結果を出します。
残念なことに、Snowflakeには選択肢がありません。あなたが得るものは、自動化が提供できるものだけです。
一方、Redshiftには過去に自動化がゼロでした。あなたは何をしているかを知っている必要があります。そうでない場合、Redshiftは失敗します。しかし、知っている場合は、Redshiftは驚くべきものになります。
私の見解では、何も知らない場合は、Snowflakeの方が優れていますが、何か知っている場合は、Redshiftの方が優れています。つまり、データ量が増えるにつれて、Snowflakeの相対的な非効率性は、正しく操作されたRedshiftに比べてますます高価になる(それが醜い部分です)。
これにより、私たちは自動バキュームに戻ります。
自動バキュームはブラックボックス、アルゴリズムです。
あなたに与えるものだけを与えます。
何も分からない場合、自動バキュームは原則として何もないよりも遥かに優れています(ただし、現在は非常に頻繁に実行されないため、実際には基本的に無価値であるようです)。
何かを知っている場合、あなたはブラックボックスアルゴリズムよりも何百万倍も優れています。
これにより、AWSがRedshiftでどこに向かっているのかという問題につながります。
Snowflakeが成功するにつれ、Redshiftの開発は、その振る舞いの様々な側面を自動化するブラックボックスの導入に焦点を当てています。
言い換えると、Redshiftは数年間、Redshiftを正しく操作する方法を知らないユーザーの損害を限定することに焦点を当ててきました。
これらのユーザーの中には、どんな場合でも何もできない人もいるかもしれませんが、私の見解では、AWSが公開した意味のないドキュメントの完全な欠如のために、ユーザーが何をすべきか分からない場合があります。
この問題を解決する正しい方法は、Redshiftを実際の適切なユースケースに向けて改善する機能を開発し、運用方法を公開することであり、情報が不足していることを理由に、未知のユーザーが自分自身に与える自己損傷を緩和する仕組みを開発するために開発チームを離れさせることではありません。
私の見解では、Snowflakeは実際にはRedshiftと競合していないと思います。正しく運用された場合、RedshiftはSnowflakeよりもソート済みのカラムストアに適しており、競合は存在しないためです。Snowflakeが成功したのは、AWSがRedshiftの正しい運用方法に関する情報を公開していないことという事実と、私は考えています。
Redshiftは、自分自身の長所を生かし、正しい運用方法を知っているユーザーのために機能セットを改善し、運用方法を文書化することに注力するべきです。それに対して、AWSは、未知のユーザーが自己損傷を緩和する仕組みを導入することによって、自分の敗北を認めることになります。私の見解では、このゲームに勝つことは決してできないでしょう。Snowflakeは新しい設計であり、最初からこのように機能することを意図しているためです。一方、Redshiftは、私にとって大規模かつ保守が困難なソースコードベースのように振る舞っており、技術的な設計と実装の遺産が数多く残っていると考えられます。
結論
10個の400mbのテーブルがあり、それぞれが200mbのソートされたが削除された行と200mbの非ソートの行を含む。各テストランでは、自動バキュームが発生したかどうかを確認するために、システムテーブルを300秒間ポーリングしました。テスト条件が変化し、ポーリング間隔が10秒、20秒、30秒になった場合、自動バキュームはすべての5回のテストで発生しなかった。
40秒と50秒の間隔では、自動バキュームが5回のテストのうち2回発生しました。
60秒の間隔で、自動バキュームが5回のテストのうち1回発生しました。
私の仮説は、システムテーブルを調べるクエリによって引き起こされる負荷が自動バキュームを防止するのに十分であったことです。
すべての場合において、実行された唯一のバキュームはdeleteバキュームであり、ソートバキュームは観察されませんでした。
おおよその結果から、自動バキュームは非常に稀にしか実行されず、ほとんど影響を与えないと思われます。これは、私が自動バキュームについて話した熟練したRedshift管理者の一般的な合意に一致しています。
自動バキュームは無効にできず、更新されると予告や発表なしに実行されます。現在、自動バキュームは無害であり、未熟なまたは熟練したRedshiftユーザーにとって有益でも有害でもありませんが、その動作はいつでも変わる可能性があります。
付録A:生データのダンプ
元のリンクに移動して表示してください
付録B:SVV_TABLE_INFO
この白書を書くにあたり、バキュームとオートバキュームに関連する公式ドキュメントページを再読しました。
特に興味深いのは、表のバキュームに関するページです。このページの最初の見出しは「自動表ソート」であり、ここでは、システムテーブル[SVV_TABLE_INFO][2]
の列vacuum_sort_benefit
についてかなり詳しく説明しています。
1 https://docs.aws.amazon.com/redshift/latest/dg/t_Reclaiming_storage_space202.html
2 https://docs.aws.amazon.com/redshift/latest/dg/r_SVV_TABLE_INFO.html
テキストによると、この列は、テーブルのスキャンに基づいてRedshiftによって計算され、テーブルがソートされるとどれだけ恩恵を受けるかを示しています。
しかし、このテキストには「自動表ソート」という見出しに対して実際に関係があるわけではありません。ただ、この見出しの下にこの列について多くのことが書かれていることは、この列の値がRedshiftがどの表を自動バキュームするかを決定するために使用されている可能性があることを示唆しています。
私は以前、この列がどのように計算されるかに興味を持ちました。 SVV_TABLE_INFO
(ビュー)のSQLを取得しました。ビューのSQLがどれだけ巨大であるかに驚きました-16.5キロバイトで、12のシステムテーブルに参加しています。
システムテーブルエクスプローラーでここでSQLを確認できます。
私はこのSQLの正確性を疑っています。さらに、長大で多岐にわたるSQLには、意味のあるテストスイートが存在するとは思えません。私は過去の経験から、Redshiftチームから期待できるものではないと確信しています。
コードを追っていくと、この列は、実際にはアクセスできないシステムテーブルから直接取得されていることがわかりました(システムテーブルの多くはrdsdb
ユーザーによってのみアクセスでき、このユーザーはAWSによって所有および管理されています)。
つまり、vacuum_sort_benefit
の値はブラックボックスによって計算され、その出力が次にどのテーブルをvacuumするかを決定するauto-vacuumのブラックボックスに入力される可能性があります(その後、auto-vacuumはvacuumするタイミングを自ら決定する必要があります)。
著作権表示
これはAuto-Vacuumを翻訳したものです,著作権はMax Ganz II @ Redshift Research Projectに帰属します。
原文アドレス
https://www.amazonredshiftresearchproject.org/white_papers/downloads/auto_vacuum.html