書き込みと読み取りでは、処理の特性も負荷の伸び方も違います。CQRS(Command Query Responsibility Segregation)は、この2つを分けて考えるアーキテクチャパターンです。パフォーマンスとスケーラビリティを上げることが目的です。
コマンド(書き込み)とクエリ(読み取り)を分離し、よくあるレプリケーションと比べながら、何が違うかを見ます。
用語
CQRS
Command Query Responsibility Segregation(コマンド・クエリ責務分離)です。書き込みと読み取りの責務を分け、別々のデータモデルや DB に最適化します。
コマンド / クエリ
コマンドは書き込み、クエリは読み取りです。CQRS ではこの2つを明確に分離します。
結果整合性
最終的には整合しますが、ある時点では不整合なデータになっている可能性がある、という前提です。CQRS では同期が非同期になりやすいため、この前提が付くことが多いです。
レプリケーション
マスター DB のデータをスレーブ DB にコピーする仕組みです。テーブル構成は同一で、読み取り性能の向上が主な目的です。
イベントソーシング
書き込み DB から読み取り DB へデータを同期する手法です。書き込み側を追記だけの履歴テーブルとして扱うこともあります。
CQRS
クラウドでは、負荷に応じてリソースをそれぞれスケールするのが自然です。書き込みと読み取りでは処理特性が違います。クラウドネイティブなアプリでは、CQRS が自然な考え方になります。単純な CRUD(書き込みと読み取りで同じ DB)は例外パターンになる、という説明があります。
CQRS にすると独立してスケーリングでき、それぞれのスキーマを定義できます。クラウドの特性を活かせます。複雑なビジネスロジックを管理しながら、システム全体の負荷を軽減し、拡張しやすくするのが狙いです。
ただしデータの整合性は結果整合性になります。ある時点では不整合なデータになっている可能性があります。システム全体に入れる必要はなく、一部に導入するなど柔軟に設計できます。
レプリケーションとの比較
よくされる設計のレプリケーションと比べると、違いは次のとおりです。
| CQRS | レプリケーション | |
|---|---|---|
| DB定義 / データモデル | 書き込みと読み取りで異なる | 書き込みと読み取りで同一 |
| パフォーマンス最適化 | 書き込みと読み取りのそれぞれを最適化 | 読み取り性能の向上 |
| データ同期タイミング データ整合性 | 非同期 結果整合性 | リアルタイム同期 |
| 実装コスト | 煩雑 | 比較的容易 |
DB定義 / データモデル
CQRS では、更新用 DB と参照用 DB が異なるテーブル構成を持つことが一般的です。書き込み用 DB は整合性を保つために正規化します。読み取り用 DB はクエリ性能を上げるために非正規化します。
レプリケーションでは、更新用のマスター DB と参照用のスレーブ DB が、基本的に同じテーブル構成を持ちます。マスターへ書いたデータをスレーブへコピーし、スレーブは主に読み取り専用です。テーブル設計そのものは同一です。クエリ性能の向上は、インデックスの最適化やキャッシングに依存することが多いです。

図: CQRS は書き込みと読み取りでテーブル構成が違う。レプリケーションは同じ構成をコピーする。
パフォーマンス最適化
CQRS は書き込みと読み取りを完全に分離し、それぞれの用途に最適化します。書き込みはデータ整合性やトランザクション管理を重視します。通常は正規化した DB で効率よく書きます。読み取りは非正規化したデータモデルで、クエリの性能を最大化します。双方に最適化した設計が可能です。
レプリケーションは、主に読み取り性能の向上が目的です。スレーブが読み取り専用の負荷を引き受けます。マスターの負担が減り、全体の読み取り性能が上がります。書き込みはマスターにだけ行われるため、書き込み性能の最適化は行われません。同じ DB 構造を複製するので、読み取り性能の強化に特化しています。
データ同期タイミング / データ整合性
CQRS では書き込みと読み取りが異なる DB を使うため、同期は非同期が一般的です。書いた内容が読み取りへ反映されるまで、タイムラグが出ることがあります。最終的には整合しますが、リアルタイムに最新が取れるとは限りません。非同期にすることでスケーラビリティを上げ、読み取りと書き込みそれぞれに最適化したデータモデルを使えます。その一方で、一時的な不整合への考慮が必要です。
レプリケーションでは、マスターへの書き込みがリアルタイム、またはほぼリアルタイムでスレーブへ複製されます。スレーブは読み取り専用として負荷を分散します。スレーブがマスターと強い整合性を保つよう設計されています。ただしレプリケーション遅延で、わずかなズレが出ることはあります。それでも、一貫性を高いレベルで維持しつつ読み取り性能を上げる手法として有効です。
実装コスト
実装コストは、CQRS の方が高くなる傾向があります。
書き込みと読み取りを完全に分離するため、異なるデータモデルやアーキテクチャをそれぞれ設計する必要があります。システム全体は複雑になります。書き込み用と読み取り用の DB を別々に管理するので、インフラの設計・運用と、非同期同期の仕組みが要ります。開発コストも運用コストも増えます。ソースコードも書き込みと読み取りで分けることが一般的です。開発・テスト・保守のリソースもそれぞれ必要です。
レプリケーションの実装コストは比較的低くなります。既存の DB 構造をコピーする仕組みなので、新たに設計や開発が必要になる部分は少ないです。DBMS が提供するレプリケーション機能を使えば、設定と管理は比較的容易です。設計もシンプルに保てます。開発やメンテナンスのコストは、CQRS と比べて低くなります。
まとめ
CQRS は、コマンド(書き込み)とクエリ(読み取り)を分け、それぞれに最適化したデータモデルや DB を使うパターンです。独立してスケールでき、クラウドの特性を活かしやすい一方、結果整合性が前提になります。許容できない要件なら導入できません。全体に入れず、一部だけ導入することもできます。
レプリケーションは同じテーブル構成をコピーし、読み取り性能を上げる手法です。実装は CQRS より容易ですが、書き込み側の最適化やスキーマの分離はしません。
関連として、イベントソーシングがあります。書き込み DB への更新を許さず、追記だけの履歴テーブルとして扱います。その内容を読み取り DB へ同期します。イベントソーシングの解説 がわかりやすいです。