Cloud Spanner のトランザクションあたりの更新回数が 2 倍に増加
Google Cloud Japan Team
※この投稿は米国時間 2022 年 9 月 30 日に、Google Cloud blog に投稿されたものの抄訳です。
このたび、Cloud Spanner では、commit あたり 40,000 件(従来の 2 倍)のミューテーションが追加費用なしでサポートされるようになりましたのでお知らせいたします。Cloud Spanner は、高可用性でグローバルに複製されます。このデータベースは外部的に一貫性があり、ACID に準拠しています。金融サービスやゲームなど、さまざまな分野のお客様が、迅速かつ正確な体験を提供するために、外部からの一貫した挿入、更新、削除を大規模に行うことに依存しています。
ミューテーションは、Cloud Spanner データベース内のさまざまな行やテーブルに対して、Cloud Spanner によってアトミックに適用される一連の操作(挿入、更新、削除)を表します。高速で一貫性のある書き込みを実現するために、Cloud Spanner では 1 つのトランザクションに含めることができるミューテーションの数に上限が設けられています。以前は、SQL を使った DML であっても、より低レベルの Mutation API であっても、クエリは 1 回のトランザクションにつき 20,000 件のミューテーションまでに制限されていました。一括更新や削除を簡素化するために、この上限がこれまでの 2 倍となる 40,000 件に引き上げられました。本日より、Cloud Spanner のすべてのお客様にご利用いただけます。1 回のトランザクションのサイズ制限は 100 MB で変更ありません。
ミューテーションとは
ミューテーションとは、データベース内の値を変更することです。Cloud Spanner は複数のデータ更新の方法を提供します。
トランザクション内のスタンドアロン DML ステートメント
バッチ DML ステートメントにより、Spanner フロントエンドの呼び出し件数(つまり往復レイテンシ)を削減
大規模な更新を自動的に複数のトランザクションにパーティション分割された DML
プログラムによる Mutation API で、一度に 1 つまたは複数のセルを変更することが可能セルとは、行と列が交差する部分です。API は、ユーザーから提供された行と列から、更新するセルを計算します。
これらのアプローチにおけるミューテーションは、上述のトランザクションあたりのミューテーションの上限と同じように集約されます。PartitionedDML の場合、ミューテーションの上限はクエリ自体には適用されませんが、Spanner では複数のトランザクションを作成する際にこの上限が適用されます。それ以外のアプローチでは、ユーザーが責任を負うことになります。1 回のトランザクションには、プログラムによる API 呼び出しに加え、スタンドアロンおよびバッチの DML ステートメントを組み合わせることができます。しかし、DML ステートメントで行われた変更は、後続のステートメントで表示されます。
DML または Mutation API は、ミューテーションの影響を受けるプライマリ テーブルを記述します。このトランザクションの一部として、影響を受ける列のインターリーブ テーブルとインデックスも更新する必要があります。この追加された場所をエフェクターと呼びます。エフェクターとは、ミューテーションのターゲットに加えて、ミューテーションの影響を受けるテーブルと考えることができます。ミューテーションの上限には、これらのエフェクターの変化も含まれます。
変更ストリームは、Spanner テーブルの更新を監視し、同じトランザクションでデータベースの他の場所で何が変更されたかのレコードを書き込みます。これらの書き込みはミューテーション上限に含まれません。
ミューテーションの推定方法
Spanner は、トランザクションの commit レスポンス メッセージの一部としてミューテーション数を返します。また、トランザクションの一部として、セカンダリ インデックスを含むすべてのテーブルで更新されたユニークなセルの数をカウントすることで、トランザクションのミューテーションの数を推定できます。セルとは、スプレッドシートのように行と列の交点です。R 行 C 列のテーブルには、R × C 個のセルがあります。新しい行を挿入すると、C 個のミューテーションとしてカウントされます。これは、各行に C 個のセルがあるからです。同様に、1 つの列を更新すると R 個のミューテーションとしてカウントされます。
より正式には、commit がプライマリ テーブルと 1 つ以上のセカンダリ インデックスへの挿入で構成されている場合、commit ごとのミューテーション数の計算式は以下のとおりです。
R = 挿入された行 / オブジェクト数、
C = トランザクションの一部として更新された列数、
Ii = 現在の列に対するセカンダリ インデックス数です。
つまり、各行に対して、プライマリ テーブルに C 個のセル、その列下のセカンダリ インデックスにそれぞれ 1 個のセルを書き込むという更新を行います。たとえば、10 行にわたって 4 つの列(行の列数に関係ない)を更新し、そのうちの 2 つの列にセカンダリ インデックスがある場合、上述の式に当てはめると以下のようになります。
ここで Ii はセカンダリ インデックスを持つ 2 つの列のそれぞれについて 1、それ以外については 0 です。(4 × 10)+(2 × 10)= 60 個のミューテーション。
論理的に隣接するセルに関しては、削除は異なる方法でカウントされます。これらは、テーブルの順序や記憶において、隣り合わせに配置されるセルのことです。最も一般的な例は、論理的に隣接するセルです。
一行に並んだ列
連続する行
インターリーブされたテーブル
これらのセルの削除は 1 つのミューテーションとしてカウントされます。つまり、行全体を削除すると 1 回のミューテーションとしてカウントされます。一緒に配置されていないセルの削除は、上述の挿入と同じようにカウントされます。つまり、セカンダリ インデックスや外部キーの削除は、1 セルにつき 1 件とカウントされます。列の削除は、インデックスの削除や変更を含まないため、R 個のミューテーションとしてカウントされます。
ミューテーションが増加した場合のコスト
トランザクションでミューテーションが増えると、作業量も増えます。Spanner は水平方向にスケールできるため、作業の多くを複数のノードに分散できます。この追加作業によりインスタンスの動作が忙しくなると、アプリケーションのテール レイテンシに影響が及ぶ可能性があります。より大きなトランザクションは、より多くのメモリと、追加のバイトをディスクに書き込むためのより多くのコンピューティング サイクルを必要とします。キースペース全体に広がるミューテーションは、複数のデータベース分割にまたがります。トランザクションは外部との整合性が保証されているものの、完了までに時間がかかる場合があります。トランザクションを構築する際はこれらの要因を考慮し、インスタンスをスケールアップして追加の負荷に対応するようにします。幸いなことに、Spanner ではダウンタイムなしに数分でスケールアップまたはスケールダウンが可能で、コンピューティング容量は比例配分されます。
ヒント: トランザクション内のミューテーションの数が 2 倍になると、他に変更がない場合はトランザクション サイズも 2 倍になります。
キースペース全体に広がるミューテーションや多くのインデックスを含むミューテーションは、(整合性を維持するために)ノード間の調整が必要です。より具体的には、キースペースの異なる部分が異なる Paxos グループに属することがあります。Spanner では、Paxos の各グループはクォーラムによってコンセンサスを得ます。複数の Paxos グループでクォーラムに達するには時間がかかり、いずれかの Paxos グループがクォーラムに達することができない場合、トランザクションを中断する必要があります。ミューテーションが多いトランザクションほど、より多くの Paxos グループが含まれる可能性が高くなります。
要約すると、ミューテーションが多いとより多くのリソースが必要となり、レイテンシが著しく高くなる可能性があるということです。これらの影響は、トランザクションのサイズとミューテーションのキー範囲を小さくすることで改善できます。
今後の予定
ここまで、ミューテーションの書き方、数え方、より多くの作業を行うためのトランザクションの組み立て方を紹介しました。ぜひ以下をご検討ください。
アプリケーションでミューテーション 40,000 個の制限が有効な場合は、トランザクションのミューテーションの数を増やす。
CPU 使用率とレイテンシをモニタリングし、インスタンスが追加の負荷を処理できるようにする。
これらの指標を改善するために、ノードを増やし、トランザクションのサイズやキースペースの範囲を縮小する。
90 日間の無料トライアルまたは月額 65 ドルから使用を開始できます。