生涯未熟

生涯未熟

プログラミングをちょこちょこと。

Spannerで書き込みの速が出なかったお話

Spanner、皆さんどう使ってるでしょう?めちゃくちゃ雑に使っても大概のシステムの要件を満たせるくらいに捌いてくれる良い子なので、利用シーンは色々あるかとおもいます。値段はお高いですが・・・👀

さて、そんなSpannerで唯一"速" が出なかったパターンを発見したので記述しておく。

どういう状況か?

Spannerはベストプラクティスに従ったスキーマ設計をした場合(hot-spotを作らないetc...)、1ノードあたり読み取りで10000QPS・書き込みで2000QPSのパフォーマンスが出ます。ノードを増やしていくと20000QPS、30000QPSてな感じで性能は上がっていくのですが(あくまでもカタログスペック通りなら)、あるテーブルに対して負荷試験をしている時にどれだけノードを増やしても書き込みで5000~7000QPS以上の速が出ないという状況に陥りました。

調査

今回、負荷試験としてはGKE + Locust + Go(Echo)といった感じでGCPの例Pixivさんの例を参考に環境構築しました。で、上記のような状況になった際に「もしかしてLocustのWorker力が足りないか?」とか「いやいやGoで作ったアプリがおかしいのか?」とか「きっとSpannerのClientライブラリにあるSessionPoolの設定値がおかしいんだ!」とか色々頭を悩ませましたが、Spannerのメトリクスをモニタリングしていたら異常にLatencyが増えていることに気付き、「Spanner側の問題では・・・?」と疑いを持つこととなった。

この時に頭にあったのは「カタログスペックは1レコード1kbを想定しているので、もしや1レコードが重すぎた?」というのと「Unique Indexを使っているテーブルなのでこれが原因か?」という2つでした。問題の切り分けとして、以下のようなテーブルを作成し、試してみることに。

  1. UUIDを主キーとして持つ1カラムのテーブル
  2. ↑のテーブルに1カラムとUnique Indexを追加したテーブル
  3. 今回の状況の際に対象としたテーブルと同じカラム構造だがUnique Indexを持たないテーブル

これらのテーブルに対して、Spannerのノード数を5にしLocustから10000RPSで負荷をかけたところ

  1. 10000QPS
  2. 6000QPS
  3. 10000QPS

という結果になりました。

で、Unique Indexがなんでこんな悪さをしとるのじゃろ?と調べていましたが、sinmetalさんが発表されていたスライドにこんな一文が。

f:id:syossan:20200823191147p:plain 引用:Google Cloud Spanner Deep Dive - Google スライド

うわ〜〜〜〜これか〜〜〜〜。実はUnique Indexは特に分散させなくとも良いだろーと負荷をかける際にInsertしていたデータがhotspotを気にしていないものだったのが原因でした・・・

感想

うーん、Unique Indexを使う際はhotspotに気を付けねばならんですね。(Spannerの公式ドキュメントに書いといてよというお気持ちが少しある) ちなみに気になったので、②のパターンでノード数を上げてみるとどうなるのかやってみたのですが20ノードに上げてみたら逆に5000QPSに下がってしまいました。まぁhotspotなところにどれだけノード数増やしても無駄ですもんねぇ。

結論:Unique Indexのhotspotに気を付ける

現場からは以上です。