アナログCPU:5108843109

ゲームと音楽とプログラミング(酒と女とロックンロールのノリで)

('ω') < イザユケエンジニャー

NoSQLのDB設計ってどうやるの

今MongoDBのおべんきょしているのですが、SQL脳のわたしとしては、NoSQLは以下の2点がかなり気になるわけです。

なにそれクソい
特に、複雑なクエリのパフォーマンス改善が趣味みたいになってるわたしとしては、JOINができないとか面白くない(知らんがな)


とりあえずググったら安定の奥野先生にぶち当たる。キャーオクノセンセー

参考:その選択、ちょっと待った!NoSQLデータベースへ乗り換える前に検討すべき3つのポイント
http://nippondanji.blogspot.jp/2013/12/nosql3.html

アプリケーションが同等のロジックを実装するか、何らかの代替の方法で不整合を解決するしかない。

ですよねー

ドキュメント型データベースはデータを「非正規化」された状態で持つというコンセプトに基づいて作られている。

ウイッス

ついでに

データベースでは、単一の処理が速い、つまりレスポンスが良いということと、並列操作にリクエストを送信してどれだけ処理をたくさん実行できるか、即ちスループットが良いかということは別の話である。リレーショナルデータベースはトランザクションの開始・終了のオーバーヘッドが大きくなる傾向にある。そのため、一つのスレッドで連続して同じ操作を行うというのは苦手になりがちである。例えば1行ずつデータを挿入するといった操作は極めて苦手な種類の処理だ。実際のシステムでは、リクエストは並列して送られてくることになるので、直列化した処理がどれだけ速いかを競うベンチマークはあまり意味がない。スループットが重要なのだ。従ってデータベースを評価する際には、現実に近い負荷でベンチマークを実施することをおすすめする。

期待ほど速くなるとも限らないとか。


偉い人が「なんかNoSQLって速いんでしょ? これからはNoSQL使ってよ!」みたいなことを言ってると、控えめに言ってぶちころがしたろかと思ってますが、こういうのをサルにも分かるように分かりやすく説明できるコミュ力というか説得力のようなものが欲しい。


それはともかくとして、非正規化が前提っすかー…そっすかー…
ごく簡単なパターンだと、ログ保存とかには向いてそうですね。
複数テーブルの同時更新や複雑なJOINみたいな操作がないし、ついでに過去のデータの更新はそもそもあり得ないし。

例えばユーザのログイン履歴とかであれば、
SQLなら

  • user_master
    • user_id ユーザID
    • user_name ユーザ名
  • user_login
    • user_id ユーザID
    • login_datetime ログイン日時

とかにするところを、NoSQLでは

  • user_login
    • user_id ユーザID
    • user_name ユーザ名
    • login_datetime ログイン日時

としちゃう感じかな?
ログであれば、ユーザ名を最新の状態で取得する必要もないかもしれないし。
最新の状態に保ちたいのであれば、

  • ログイン情報を取得&現在のユーザ名を取得 → アプリ側で結合
  • ユーザ名に更新があればログイン情報のユーザ名も更新

くらいが考えられる感じですかね。後者すげーアホっぽいけど。

お客様からガッチガチの仕様もらって…であれば難しいですが、開発側からある程度提案できる状況であれば、それなりの落とし所を見つけることはできそう。

簡単なシステム(しかもログ的データが大部分を占めるようなやつ)であれば全部NoSQLでもいけるかもしれませんが、まあ、そういう案件は少ないでしょうし、結局↓のような結論になるんでしょうね。

最も理想的な使い方は、リレーショナルデータベースと併用するということだ。

例えばNoSQLデータベースを使用する部分はすべて「キャッシュ」ととして扱うような使い方が考えられる。NoSQLデータベースが得意な検索を、NoSQLに任せるというパターンだ。その場合、リレーショナルデータベース上のデータから全て再構築できるような構造にするべきということになる。他にも、単にデータの種類によって格納先を分けるというような使い方も考えられる。

ぐぬぬ
でも、非正規化前提であればあまり深く考えず、単純な構造にしてしまえばいいってことかー。
(もちろん、トランザクションなりが不要な場面という前提で)

あとは、フィールドに配列を持つことができるので、それを利用して…って感じですかねえ。
さっきのログイン履歴の例だと、マスタテーブルにくっつけて

  • user_master
    • user_id ユーザID
    • user_name ユーザ名
    • login_datetime ログイン日時(配列)

みたいな。