データベース●●●○○

NoSQL

NoSQL ・ のーえすきゅーえる

表と SQL 以外のデータモデルを採るデータベースの総称。水平分散のしやすさや柔軟な構造を重視する。

✦ 30秒で試す — 航海同じデータを、わざと何か所にも重ねて書くのが定石のDBこのデータベースに「名前が佐藤の人」を探させると?予想してみる

概要

NoSQL は、RDBMS のリレーショナルモデル(行と列の表を、SQL で結合しながら問い合わせる)以外の方法でデータを持つデータベースの総称です。キーと値の対で持つキーバリュー型(Redis、DynamoDB)、JSON のような入れ子のドキュメントで持つドキュメント型(MongoDB、Firestore)、行キーごとに並んだ列の束で持つワイドカラム型(Cassandra、HBase)、点と線のつながりをそのまま持つグラフ型(Neo4j)など、性格の違う複数のファミリーを一括りにした呼び名です。いまは「SQL を使わない」ではなく「Not Only SQL(SQL だけではない)」と読むのが一般的です。

ファミリーごとに、得意なデータ構造がはっきり違います。キーを渡せば値が返るだけの単純さでマイクロ秒の応答を出すもの、アプリケーションのオブジェクトをほぼそのまま保存できるもの、大量の書き込みを何百台にも分散して受け止めるもの、関係を何段もたどれるもの。共通するのは、RDBMS が一律に提供してきた「どんな問い合わせでも後から書ける柔軟さ」や「複数の表をまたぐトランザクション」の一部を手放す代わりに、台数を増やして性能を伸ばす水平スケーリングのしやすさや、用途に合ったデータの形を手に入れている点です。

実際の現場では、キャッシュやセッションの置き場、大量のログやセンサーデータの蓄積、スキーマが頻繁に変わるコンテンツ、推薦のためのつながりのデータなど、RDBMS だけでは苦しい場面を補う形で使われます。

なぜ生まれたか

2000年代半ば、Google や Amazon のような超大規模 Web サービスは、RDBMS の限界に直面していました。RDBMS は1台のマシンの中で ACID を守るように設計されていて、性能を上げるには基本的にマシンを強くする(垂直スケール)しかありません。しかし世界中からのアクセスは1台の限界をあっさり超えます。データを複数台に分けて置けば、JOIN や複数の行をまとめて確定するトランザクションを台をまたいで保証するのは、急に難しく遅くなります。

さらに、データを複製して持てば CAP定理の問いにぶつかります。ネットワークが分断されたとき、複製どうしが最新の値をそろえること(C)と、生きているノードが必ず応答すること(A)の、どちらかを選ばなければなりません。ショッピングカートのように「止まるくらいなら少し古い値でもよい」データには、A を選ぶほうが事業にかなっていました。

そこで両社は、守るものを絞り込んだ独自のデータストアを作りました。Google は巨大な分散表の Bigtable(2006年の論文)、Amazon は止まらないキーバリューストアの Dynamo(2007年の論文)です。これらに触発されて HBase、Cassandra、Riak などのオープンソース実装が次々に登場し、MongoDB や Redis も同じ時期に広まりました。2009年にサンフランシスコでこうした分散データベースの勉強会が「NoSQL」という名前で開かれ、この言葉がムーブメントの旗印になりました。「守る性質を用途に合わせて選べば、1台の限界を超えたスケールと可用性が手に入る」という発見が、この語彙の原点です。

詳細

同じデータを、4つのファミリーはどう持つか

ファミリーの違いは、名前の一覧より、同じデータをどう持つかを並べると見えてきます。ここでは「ユーザーの佐藤さんが、本とペンを注文した」というデータを例にします。RDBMS なら、ユーザー表と注文表に分けて正規化し、読むときに JOIN でつなぎます。

キーバリュー型Redis・DynamoDBuser:u1佐藤の情報(ひと塊)order:o1本の注文(ひと塊)キーを知っていれば最速で引ける値の中身で探す・つなぐのは苦手ドキュメント型MongoDB・Firestore{ _id: u1, name: 佐藤,orders: [{ item: 本 }, { item: ペン }] }一緒に読むものを1つの文書に埋め込む文書をまたぐ結合は苦手ワイドカラム型Cassandra・HBaseu109-01本09-12ペン…u209-03本ユーザーごとに注文を日付順に並べて持つ「u1 の今月の注文」は速い。条件を変えた検索は苦手グラフ型Neo4j注文した佐藤本ペン鈴木関係そのものを持ち、線を何段もたどる「同じ本を買った人」がすぐ出る
同じ「ユーザーと注文」を、4つのファミリーはそれぞれ違う形で持つ。形が違えば、速く答えられる問いも違う

キーバリュー型は「キーを渡すと値が返る」だけの最小のモデルです。データベースは値の中身を解釈しないので、キーを知っていれば最速で取り出せる反面、「名前が佐藤の人」のように値の中身で探すことはできません(DynamoDB のように、キーを2段にして範囲で読めるものもあります)。構造が単純なぶん速く、キャッシュやセッションの置き場、ランキングの集計に使われます。Redis はデータをメモリ上に置き、マイクロ秒単位で応答します。

ドキュメント型は、JSON に似た入れ子のドキュメント単位でデータを持ちます。事前のスキーマ定義が要らず、ドキュメントごとに項目が違っても構いません。図のようにユーザーの文書の中に注文を埋め込めば、「ユーザーと注文をまとめて表示する」画面はドキュメント1件を読むだけで済みます。その代わり、埋め込んだ注文を「商品ごとに集計する」ような、文書の境界をまたぐ問い合わせは苦手です。

ワイドカラム型は、パーティションキー(図の u1)ごとに、並び順のキー(日付)でソートした行の束を持ちます。同じパーティションのデータは同じノードにまとまって置かれるので、「u1 の今月の注文」のような範囲の読み取りや、IoT のセンサー値・メッセージ履歴のような大量の追記を、数十〜数百ノードに分散して受け止めるのに向きます。逆に、パーティションキーを指定しない検索は全ノードを舐めることになります。

グラフ型は、点(ノード)と線(関係)を第一級のデータとして持ちます。「佐藤さんと同じ本を買った人が、ほかに買ったもの」のように関係を何段もたどる問い合わせは、RDBMS では JOIN の連鎖になって急に遅くなりますが、グラフ型では線をたどるだけで答えられます。

分散の仕組みと整合性 — 「NoSQL=結果整合性」ではない

多くの NoSQL は、キーのハッシュ値などでデータを複数ノードに自動で分け(シャーディング)、さらに各データを複数ノードに複製(レプリケーション)して、故障に耐えます。初期の Dynamo 系の設計は、書き込みが全レプリカへ行き渡る前に応答を返し、「いずれ全ノードがそろう」ことだけを約束する結果整合性を既定にしていました。そのため「NoSQL は結果整合性、RDBMS は強い整合性」と対にして語られがちです。

しかし、いまではこの対比は単純すぎます。DynamoDB は読み取りごとに「強い整合性の読み取り」を選べ、2018年からは複数の項目をまとめて確定するトランザクションも持っています。MongoDB は 4.0(2018年)から複数ドキュメントにまたがる ACID トランザクションを備え、書き込みを過半数のレプリカに届けてから完了とする設定もできます。Cassandra は読み書きごとに何台の返事を待つか(クォーラム)を選べ、条件付き更新には合意のアルゴリズムを使えます。逆に、RDBMS でも非同期のレプリカから読めば古い値が見えます。整合性はデータベースの種類で決まるのではなく、どの読み書きにどの保証を選ぶかという設定と設計で決まる、と考えるほうが正確です。

使いどころと落とし穴

NoSQL は RDBMS の置き換えではなく、適材適所の道具です。実務では「本体のデータは PostgreSQL、キャッシュとセッションは Redis、行動ログはワイドカラム型」のように併用する(ポリグロット・パーシステンスと呼ばれます)のが定石です。まず RDBMS を既定の選択とし、スキーマが本質的に不定形である、書き込み量が1台の限界を超える、ミリ秒未満の応答が要る、といった明確な理由があるときに NoSQL を足すのが健全です。

最大の落とし穴は、「クエリから逆算した設計」が必須だという点です。RDBMS は正規化しておけば、後からどんな SQL でも投げられます。NoSQL の多くは JOIN や自由な検索を持たないので、「どう読むか」を先に決め、その読み方に合わせてデータを持ちます。ワイドカラム型では、同じデータを読み方ごとに別の表へ重複して書くことすら普通です。要件が固まらないうちに NoSQL を選ぶと、後から「この条件では検索できない」という壁に突き当たります。また「スキーマレス」は「スキーマを考えなくてよい」ではなく、「スキーマの整合性を守る責任がデータベースからアプリケーションに移る」という意味です。古い形のドキュメントと新しい形のドキュメントが混ざったまま何年も読み続けることになるので、読み込み側でどの版も扱えるようにしておく必要があります。

試問

NoSQL と呼ばれるデータベースには、データの持ち方が違う4つのファミリーがあります。キーを渡すと値が返るキーバリュー型、JSON のような入れ子の文書で持つドキュメント型、区切りのキーごとに行を並び順で束ねて多数の台に分けて置くワイドカラム型、点と線のつながりで持つグラフ型です。8つの用途は、それぞれどれがいちばん得意でしょうか。

✦ 命題挑戦中

8つの用途を、それぞれいちばん得意なデータベースのファミリーへ仕分けよ

キーバリュー型キーを渡すと値が返る
ドキュメント型入れ子の文書を丸ごと持つ
ワイドカラム型キーごとに並び順で束ね、多数の台へ分ける
グラフ型点と線のつながりを持つ

項目を選んでから、入れる箱を選びます