初めに
TwitterのDB界隈で少し話題になっていた特集の記事について、個人的に気になった指摘事項の一覧です。
記事自体は限られた紙面数で簡潔に読みやすくまとまっており、特にAurora/RDSについては要注意なポイントについてもまとめられていてわかりやすいものでした。
しかしながら、私知識と経験の範囲内での判断で、説明不足や技術的に誤解を招く表現等が見られたのでまとめてみます。
※執筆者は普段の業務も忙しい中で限られた時間、紙面数で対象読者に向けて記事をまとめるので必死でしたでしょうし、どんな人でもどうしても経験や知識の範囲は限られてしまうことから、誰もが満足できる完璧なアウトプットを出すことは不可能であり、決して執筆者を貶める意図のある記事ではありません
ACID特性、CAP定理、BASEについて
様々な記事で指摘されている通り、CAP定理やBASE特性を持ち出すと誤解や混乱を招き、むしろ理解が難しくなるので避けるべきだったのではないでしょうか。
例えば紙面上で取り上げられているRDS, AuroraはシングルWriter構成で書き込みのスケーラビリティの制約が存在し、明確な弱点となっています。
その一方で「非同期」Read Replicaをたくさん並べることで読み取りスケーラビリティを稼ぐことができ、Auroraに至ってはRead Replicaがフェイルオーバー対象となり可用性の向上にも利用されています。
前述のとおり、整合性の要件を緩和したレプリカをたくさん並べて性能を向上させる手法はNoSQLに限らずデータベースとして一般的であり、Eventual Consistency等をNoSQLの特徴として挙げることには違和感があります。
また、紙面全体を読んでいて感じた違和感ですが、「個別のアイテムレベルの操作」と「複数パーティション/アイテムを跨った操作」、それに加えて各操作の提供する整合性モデルは明確に区別すべきではないでしょうか。
確かにDynamoDBはTransactGet/WriteItemsを除き、複数パーティション/アイテムを跨るACIDトランザクションはサポートしていないものの、それは個別アイテムレベルの操作でAtomicity, Isolation, Consistency, Durabilityをサポートしていないことを必ずしも意味していません。
書き込みは3AZに対して永続化されますし、各操作は中途半端に実行されたり、変更中の他の処理の内容が見えることは(おそらく)ないはずです。
※ConsistencyについてはDynamoDBは外部キーやcheck句は提供していないものの、conditional expressionでできる範囲で担保すると思われます
2022/5/29更新
補足記事を作成しました。
「なぜRDBMSにACID特性が必要なのか」の箇所について
教科書的な知識しなく恐縮ですが、ACIDトランザクションのサポートはRDBMS固有の技術ではなく、RDBMS誕生以前からTransaction Monitor + IMS(ほかにもいろいろありそうですが、知識経験ともないですorz)という形で実用化、提供されており、社会を支えているのではないでしょうか。
※もっと言うとIMSが1966年登場であることからもわかる通り、NoSQL DBの方が登場が先でRDBは後から登場したと思われます
https://vsis-www.informatik.uni-hamburg.de/oldServer/teaching/ss-09/hms2009/materialien/dgaebler/ims_tm.pdf
例えば現代のRDBMSのトランザクション・リカバリーの手法であるARIESの論文でも比較対象としてDB2, System RのほかにIMSも同列に比較されており、提供するデータモデルに関わらず、DBMSとしてトランザクションのリカバリーはサポートすべき重要な機能であることがわかります。
https://cs.stanford.edu/people/chrismre/cs345/rl/aries.pdf
IMSや他のNoSQLのDBについて網羅的に調査したわけではないですが、恐らくDBMSの仕組みとして必要な要素はrelational modelを提供するためのクエリの実行エンジン部分を除いてNoSQLも大差ないのではないでしょうか。
もっと言うとNoSQL(Non-SQL)とRDBの比較、競争は今に始まったことではなく、1987年の論文に以下のような記述が見つかります。
速いけれども生産性が低いnon-SQL DB vs 生産性が高いけれども遅いRDBの戦いは何十年も前から行われており、恐らく一度はRDBに傾いたパワーバランスがインターネットのスケールで性能要件が非常に厳しくなったことで再びNon-SQL DBが脚光を浴びている、というのが実情に近いのではないでしょうか。
※この論文の記述の通り、「スケールするShared-Nothing構成のRDB」自体がそもそも最近登場したものではなくて、実は古くから存在することもわかりますね。使ったことがないので気になります...
https://www.hpl.hp.com/techreports/tandem/TR-87.4.pdf
Prior SQL implementations are marketed as information center tools or as productivity tools. Their easy-of-use is accompanied by a significant performance penalty. These vendors typically offer a second, non-SQL, system for production applications. Tandem rejected this "dual database" strategy as being too expensive to support, and too expensive and cumbersome for customers to use.
DynamoDBの記事におけるSNSアプリの例について
ShardingされたGSIに対して、1000回Query APIの呼び出しが必要だから向いていないという個所について
そもそも通常のWebアプリケーションですべての結果を一度に取得する必要はないのではないでしょうか。
RDBでもN + 1問題は起こりえますし、ジョインについてもそもそも大量にリソースが必要となる重い処理なので、各画面で毎回少なくとも1,000行以上項目を取得するような処理はRDBでも普通は実施しないのではないでしょうか。
フォローしたユーザーのつぶやきを表示する項目について
そもそもの問題として、RDBやDynamoDB関係なく膨大なアクセス数が発生する可能性があるSNSにて、すべての処理を1DB, 1サービスに集約して複雑なクエリで全データを整合性を保ったまま結果を取得することは一般的に行っていないのではないでしょうか。
次のページの説明を信じるならば、User Timeline serviceにおいてTimeline情報はデータストアを一切利用しないin-memoryのデータ構造(linked-listらしいです)を利用しており、当然非同期実行に見えます。
※in-memoryのデータ構造なのは恐らく揮発性のデータであることや、レイテンシーやスケーラビリティの要件が非常に厳しいためだと思われます
※現実のTwitterもTimelineを時系列で全部たどれるわけではなくて、途中から見えなくなるようですね
https://medium.com/interviewnoodle/twitter-system-architecture-8dafce16aec4
マスターデータアクセスについて
それこそ大量の読み取りアクセスがあるならばキャッシュすればよいのではないでしょうか。
※データの整合性の管理が必要ですし、キャッシュの容量管理やワーストケースでの性能をどこまで担保するのか等の考慮は必要です。
RDBとの違いについて
DynamoDBはリレーションやホットスポットなど、RDBMSでは存在しない注意点が多いため
RDBMSでもホットスポットは発生しますし、ジョインはノーコストの魔法の技術ではなく性能問題は普通に起こります。
多数のセッションで同じ行を更新すればロックの競合が発生しますし、ページレベルでの競合が発生したり、リソースの切迫が発生することもあります。
https://atmarkit.itmedia.co.jp/ait/articles/0502/19/news020_3.html
https://atmarkit.itmedia.co.jp/ait/articles/0502/19/news020_4.html
ジョインについても、そもそもジョイン自体が重い操作であること、特にhash joinは多くの場合大量の一時ファイルの出力が必要になりますし、大量行同士をインデックスなしでNested Loopでジョインすれば非常に遅くなりますし、ジョインの高速化のためのインデックスの付与もコストがかかります。
ジョイン対象のテーブル数が増えれば更新の影響などで実行計画が変化するリスクも増加し、特に巨大テーブルに対するクエリの実行では細心の注意が必要です。
※巨大テーブルのジョインで大騒動になっている状況を何度も見たことがあります
DynamoDBにおけるジョインのサポートがない件に関しては、次の動画で説明があるように、DynamoDBの設計は「Don't allow operations that won't scale」を目標としていることから、「ジョインが苦手」というよりも初めから設計から除外し、非正規化、あるいは他のデータストアとの併用を前提としています。
※後述のDynamoDB Transactionsの応用でone-to-oneの関連ならば、複数パーティション/アイテムを跨いで一貫性を保ったジョインの実施は技術的に十分「可能」ではあると思われますが、前述のとおりスケーラビリティが最優先なので、「ジョインが必要ならば非正規化するか他のデータストアを使え」がDynamoDBのやり方です。今後も恐らく実装されることはないのでは。
https://www.youtube.com/watch?v=fiP2e-g-r4g&t=322s
近年では、Amazon DynamoDB Transactionsにより一部のトランザクション処理が可能となっていますが、DynamoDBは基本的にはアイテムを越えた一貫性のある処理は実施できません。
初めに、日本語として「可能だけれども~基本的に実施できない」は意味を取りづらいのではないでしょうか(できるの、できないの、どっち?)
加えてDynamoDBのトランザクションは確かにRDBのものとは異なるものの、KVSでトランザクションが必要となるユースケースに最適化されており、複数アイテムを跨る更新や一貫性のある結果の取得が可能なので、実用性の乏しい機能や禁じ手ではなく、必要性が認められる状況ならば利用を検討すべきではないでしょうか。
https://www.youtube.com/watch?v=gXo-Dl6bU9I
次の記事で説明されている通り、DynamoDB Transactionsは安定したレイテンシー、スケーラビリティを阻害しないことを念頭に設計されています。Concurrency Control Schemeとして一般的なRDBの2PLではなくTimestamp Ordering Protocolによりロックフリーでのトランザクションを実現しているなど、DynamoDBのワークロードに最適化されたアーキテクチャとなっています。https://www.mydistributed.systems/2021/12/dynamodb.html#:~:text=The%20timestamp%20ordering%20approach%20used,one%20with%20a%20larger%20timestamp.
※上記記事中のSpannerとの比較でもわかる通り、特に複数ノードを跨るGetに関してはアーキテクチャの制約上、non-blockingで実施できないことから必要性を見極めて実施すべきとは思います。また、そもそも通常のCRUD操作よりもレイテンシーが高いことやCUの消費が多いことから、利用するかどうかの見極めが必要だという認識です。
DynamoDBには、一貫性を担保するしくみがほとんどないため
あくまで予想ですが、このコンテキストの「一貫性」とは、ACID特性のConsistencyの話ではなくて、「MVCC(多くの場合Snapshot Isolation)により、non-blockingに複数ノードを跨り一貫性のあるビューを見ること」を本当は言いたいのではないでしょうか。
もし私の予想が正しいならば、読者に伝わるように正確な用語の使用を心掛けるべきです。
Streamsへの言及がない点について
紙面の都合で省略されたのかもしれませんが、Streamsについて軽く触れるべきだったのではないでしょうか。
スナップショットのS3へのエクスポート、RedshiftのCOPY文での直接取り込み等、他の連携手法も存在するものの、多く場合、Streamsで他のデータストアに連携しているでしょうし、DynamoDBにとって非常に重要な機能という認識です。
https://aws.amazon.com/blogs/database/dynamodb-streams-use-cases-and-design-patterns/
まとめ
私の知識、経験のレベルで気になった点をまとめてみました。雑誌への執筆経験などもなく、限られた知識と経験での記述のため、多くの誤りや誤解が含まれているかもしれません... また、DynamoDBが完全無欠のスーパーDBとも思っていませんし、RDBに親が殺されたわけでもないと主張しておきます。