Don't Repeat Yourself

Don't Repeat Yourself (DRY) is a principle of software development aimed at reducing repetition of all kinds. -- wikipedia

『NewSQL徹底入門』

NewSQLというキーワードを最近よく聞くようになってきました。TiDBはその代表格で、私の所属する会社でもよくその動向に関する情報が共有されています。最近話題になっていたものだとモンスターハンターワイルズへの利用でしょうか。[*1]最近だとAWSがDSQLを発表していて、書き込みがスケールするという話を見かけ、一体どういう仕組みで書き込みがスケールするようになったのかとても中身が気になっていました。

ただ、私が現在仕事で扱っているのはtoB向けのSaaSであり、ゲームや広告配信と比較してそれほど大きなトラフィックがないプロダクトです。本書でも言及されていましたがNewSQLそれ自体は「Not for me」なのかなと思っていました。が、どうやら違うということが本書を読んでいてわかってきました。私の関わるSaaSPostgreSQLを使用しておりそれで満足しているのですが、よりスマートに物事を解決できる方法がむしろNewSQLにもありそうだということがわかってきました。

本書はNewSQLがどういった特徴を持つ製品群であるかを知りたい方や、NewSQLは旧来のRDBやNoSQLと何が異なるのかを内部構造の視点から理解したい方に向いている一冊だと私は思っています。ただ本書を読むにあたっては、データベースの内部構造に関する前提知識はある程度必要です。たとえばデータベースのオプティマイザやストレージエンジンといわれたときにそれらが何をしているかを知っているのは前提なように見えます。

私自身、RDBの自作経験があり内部構造の理論的な理解はしていましたが、実務での大規模な運用や活用については経験が浅く、いわば「運用の初学者」です。一方キャリアの中で広告配信をやっていた関係でNoSQLの経験が相対的に長く、分散システム側の話はそれなりに機構を理解していました。それゆえ本書の内容がすんなり入ってきた可能性もあることを留意してください。

また、Google Cloud環境でCloud SQL、AlloyDB、Spannerのどれを選択すべきか、アーキテクチャ選定に悩んでいる現場の方も一読の価値がある内容です。

NewSQLとは結局なんだったのか

RDBとNoSQLのいいところどりをしたという理解です。どういいとこどりをしたかというと、RDBの整合性とNoSQLの分散性を組み合わせたものです。RDBは厳格なスキーマ定義と強力なACIDトランザクション、そして表現力豊かなSQLを備えています。一方で、その構造ゆえに書き込みのスケーラビリティ向上には制約が生じやすく、可用性の面でも(分散を前提とした)NoSQLと比較すると課題になりがちでした。NoSQLはその逆で、ACIDトランザクションをもたない代わりにスケーラビリティと可用性に振り切っている傾向にあります。RDBは逆にその構造からくる水平スケーリング性能にやや制約があります。これらはトレードオフなのが少し前までは通例でした。[*2]

NewSQLはさまざまな分散システムで使われてきた技術を用いながら、スケーラビリティや可用性を担保できるように設計されています。本書によればNewSQLの工夫ポイントは分散データにあるそうです。これはつまり、NewSQLのポイントは分散ストレージ + SQLにあると言い換えることもできるでしょう。ストレージエンジン上のデータを分散させるためには、読み取り時・書き込み時ともにデータを複製させ分散配置する必要があります。この複製のタイミングでさまざまな困難があるわけですが、ここを分散システムの技術を用いてなんとかデータの分散を実現していると言うわけです。

もちろん、NewSQLが全部を解決するわけではありません。分散させるということはネットワーク周りのレイテンシーが気になる可能性があるなど想像に難くないでしょう。ある課題に対してどのような解決手法を取るかによって、何かしら捨てなければならなかったポイントがあります。これらはNewSQLの製品ごとにかなり差があるように見受けられます。本書でもきちんと製品ごとの差を説明するよう努めており参考にすることはできますが、使う場合にはその製品がどのような課題をどう解決しており、どのようなトレードオフが生じているのかを把握してから利用すべきなのだろうと思いました。これは基本的な話ではあるのですが、日々の開発現場ではどうしても解像度の低めな「これを入れれば深く考えずとも全解決」的な議論が展開されてしまいがち(経験談)なので、注意が必要なポイントだと思います。NewSQLも銀の弾丸ではないのです。

現場での活用の方向性

さまざまな活用事例

もちろんモンスターハンターワイルズのようなゲームや、数億人が同時に使う可能性のある高トラフィックなサービスでの利用にはかなり向いているのではないかと考えています。あるいは、これまでNoSQL製品とRDB製品のふたつを組み合わせて解決をがんばってきた領域についてはとくに、NewSQLをひとつ導入すれば済ませられる可能性があります。これはアーキテクチャのシンプル化に大きく貢献します。結果的にインフラコストの低下や運用コストの低下につながるはずです。

私が特に強い関心を寄せたのは、極端な高トラフィックではない「一般的な規模のサービス」におけるマイクロサービスへの導入事例です。本書の例では、さまざまなデータをさまざまなミドルウェアに投入し、それらのトランザクションをSagaで担保するという手を取っていた事例が紹介されています。主にはRDBの可用性の問題にあたって別の特性を持つミドルウェアを投入してその形になったようなのですが、Sagaはご存知の通り実装も大変だし運用も大変という代物です。[*3]NewSQLひとつに置き換えてSagaを廃止することに成功した事例が登場します。そこまで高トラフィックなサービスではないものの可用性の課題に当たっているサービスというのはまま国内でも見受けられるようには思い、有用な事例と言えるのではないでしょうか。

マイクロサービスではTransactional Outboxパターンを用いている現場も多いと思います。この機能の実装はかなりシンプルではあるものの、Outboxを置くテーブルへの書き込みがボトルネックになることがあります。この部分にCockroachDBのCDCを使ってKafkaにメッセージを転送する例も登場し、なるほどその手があったかそういえば…ともなりました。この辺りを理解しておくと、DDDの文脈でよくセットで語られるCQRSやイベントソーシングの際に無駄に複雑な構成を組まずに済みそうだなとも思いました。

実際の活用事例は7章に豊富にまとまっており、NewSQLを使う予定がなかったとしても一読の価値ありだと思います。

既存のRDBからの移行

NewSQLはPostgreSQLMySQL互換を謳う製品がある一方で、既存のRDBからの移行はすんなりいくのか?というといくつか注意点がありそうにも思いました。

ひとつはトランザクション分離レベルです。NewSQLの各製品は、デフォルトの分離レベルでREPEATABLE READ以上を使用していることが多く、中にはSERIALIZABLEなものもあります(CockroachDBとSpanner)。一部のORMでは、デフォルトとしてREAD COMMITTED(PostgreSQLなどの標準)を想定していることがあります。そうした状況下では、RDBからNewSQLに移行してアプリケーションを動かしてみると想定外のエラーに見舞われる可能性も大いにありえます。なので、すんなりマイグレーションできない可能性があり、追加で支払わなければならない想定外のコストがありうるというのを頭に入れておく必要があります。加えて、製品によっては同じREPEATABLE READでもたとえばMySQLのそれとは異なるケースもあるようです。トランザクション分離レベルの変更は移行時に注意すべきだと思います。

もうひとつはID生成問題です。とくにAUTO_INCREMENTを使ってIDを生成しているプロダクトでの利用は要注意そうです。というのも連番を利用すると、特定のシャードに書き込みの発生するホットスポット問題が発生しやすくなるからです。一部のORMはデフォルトでAUTO_INCREMENT(連番)を利用する設定になっており、これをそのままNewSQLに持ち込むと特定のノードに負荷が集中する「ホットスポット」が発生します。既存プロダクトのマイグレーションにおいては、これが大きな障壁(ノックアウトファクター)になる可能性があると感じました。

分散システム

NewSQLはいわゆる分散システムの技術の上に乗っかる形でACID、スケーラビリティ、可用性を担保していると理解しています。そうした分散性を支える基礎技術に関する解説も本書にはいくつか記載されています。シャーディングやRaftの解説、分散トランザクション周りの話が含まれます。本書での説明を読んだ後、ぜひ猪本ことデータ指向アプリケーションデザインの7章〜9章あたりを読み返すと理解が深まると思いました。私も読み返そうと思っています。

まとめ

一家に一冊欲しいですね。ぜひ!

*1:余談ですがワイルズはユーザーが影響を感じる不具合が多いですね…PC版も含めて全対応したのが今回初だったから実装が難しかったとかなんでしょうか。その割にライズは不具合少なかったような気もするし…

*2:ただしAmazon DynamoDBのように途中からACIDトランザクションが実装されたり、逆にPostgreSQLなどではJSONのような半構造データも扱えるようになってきていますが。

*3:AWSならStep Functionsを使ってオーケストレーションするとそれなりに楽になるんですが、今度はStep Functionsの見守りが大変になるんですよね。参考: AWS Step Functions を使用して、サーバーレス Saga パターンを実装する - AWS 規範的ガイダンス