社長が脱RDBと言い出して困りましたが、開き直って楽しんでいる話

井上 誠一郎
アリエルネットワークCTO
ワークスアプリケーションズ エグゼクティブフェロー

自己紹介
井上誠一郎
書籍 「パーフェクトJava 第1版/第2版」「パーフェクトJavaScript」「実践JS サーバサイドJavaScript入門」「P2P教科書」「15時間でわかるJava集中講座」(監修)自己紹介（続）1990年代後半 Lotus Notes開発
2000年代前半 P2Pアプリ開発
2000年代後半 Webアプリ開発(RDB脳化が進行)
2010年代前半 脱RDB(今ここ)今日のトピック
RDBを使わずに業務アプリを作れそうに思えてきた経験談
RDBなしのほうが良い...とまでは言いません。念のため

今日話さないトピック

NoSQL実装の個々の比較

CAP定理とかその周辺の話

本プレゼンで使う用語についてAP
クライアントやプロセスと呼ぶ流儀もありますが、本資料はDBにアクセスする主体はAPで統一

DBとりあえず結論APAPDBAPAP

DBretry

ロックと直列化

楽観的CC(イミュータブルデータとリトライ)とりあえず結論
APAPDB

APAP

DB

実装レベル:MVCC
Shadow Paging

ロックと直列化

retry

楽観的CC
(イミュータブルデータとリトライ)

目次
1. ちょっとした前置き
2. 何を目指しているのか
3. アーキテクチャ決定の道のり
4. RDBなしの不安
5. まとめ1. ちょっとした前置き

最初の自分の反応「RDBが遅い」は誤解
仮にNoSQLが速くても、必要機能を実装したら同程度の速度になるはず。
理論上同程度であれば、実装の差で、枯れたRDBMSが有利になるはず。

一方で教科書的なリレーショナルモデルだけで、元々業務アプリを作っていないのは事実
各種キャッシュ(キャッシュサーバ、ORM層のキャッシュ)非正規化テーブル(結合済みテーブル、サマリーテーブル)
アプリ側のバリデーション処理(RDBの制約だけでは不充分)
アプリ側のトランザクション管理(楽観的並行制御)

今の自分のスタンス「脱RDB、これはこれで面白い」
脱RDBしても業務アプリを作れそうな気がしてきた
ただ、脱RDBで圧倒的に速くするには並列処理に適したデータモデルや処理に変えていく必要がある2. なにを目指しているのかそもそも我々はなにを目指しているのか

応答速度100ミリ秒の業務アプリ

応答性能100ミリ秒の業務アプリが提供できる価値

トライアンドエラーを待たずにできるEnterprise SearchEnterprise SearchInsta ReportEnterprise Spreadsheet

3. アーキテクチャ決定の道のり

複雑なクエリをDBに寄せるかアプリに寄せるか?
DBを使うアプリ設計の長年のジレンマ
DBに寄せたほうが効率的(データローカリティ)
一方、アクセス制御や複雑なバリデーション処理はDBだけではできない

RDBの世界観
リアルタイムインデクシングとリッチクエリの両立トランザクション管理(並行制御)の隠蔽
リアルタイムインデクシング

Simple indexing
リレーショナル演算

Rich query宗教論争
DBMSを高機能化してOLTPとOLAPを統合する世界(SAP HANA、VoltDB)
リアルタイムインデクシングを諦める世界

自分の中でも揺れてきた。

リアルタイムインデクシングの限界

本当にリッチなクエリ(自然言語処理や機械学習の活用など)は困難クエリのオプティマイザが優秀になってもなお性能劣化する現実バックグラウンド処理で割り切る世界

表示に必要なデータを(可能な限り事前に)作っておく

非正規化データを恐れない
データ増大を恐れないバックグラウンド処理で割り切る世界; 更新系
更新系処理は、遅延可能な処理と不可能な処理がある
遅延できない処理は、並列処理と投機処理の工夫で高速化バックグラウンド処理で割り切る世界
並列処理のためのデータ構造
イミュータブルデータ
追記型データ(チェンジセット、イベントデータ)

並列処理の基本戦略
楽観的並行制御(OCC:Optimistic Concurrency Control)
投機実行
ロックフリーアルゴリズム

[参考] プログラミングとの対比
イミュータブルデータ
楽観的CC

NoSQL
余計なディスク消費
GC相当処理が必要
並列性が必要余計なメモリ消費
GCが必要
並列性が必要

メニーコア
メモリの余力クラウド

バックグラウンド処理で割り切った世界が失うモノ「one fact in one place」を失う
リアルタイムな一貫性の維持が困難
厳密に言うと、一貫性と更新性能がトレードオフになる現アーキテクチャの技術スタック

Cassandraの特徴;
データモデルKVS(Row-Keyとカラム群)• ワイドローデータモデル(Map<RK,SortedMap<CK,CV>>相当)
http://www.slideshare.net/patrickmcfadin/the-data-model-is-dead-long-live-the-data-model• CQL(クエリ言語)により開発者の概念モデルをRDB風にしている• スーパーカラム(非推奨。未使用)• その他
軽量トランザクション(CAS相当のアトミック操作)

Cassandraの特徴;
ネットワークモデル• 分散ハッシュテーブル(1ホップ)• Row-Keyがパーティションキー(=シャードキー)• レプリケーション数などはキースペースごとのチューニング項目

Cassandraの特徴;
ストレージモデル•••••

行指向ストアディスクベース(コミットログ(WAL相当))
インメモリの追記型領域Memtable
Log-structuredデータ構造
データ圧縮とコンパクション処理

選択した戦略;
ネットワークモデルの設定
ランダムパーティション
Pros: 高いスケーラビリティ
Cons: レンジ(範囲)クエリができない
レンジクエリはワイドロー(後述)もしくはElasticsearchで代替

レプリケーション
レプリケーション数 N=3
デフォルトのコンシステンシレベルはquorum

選択した戦略;
データモデルの指針[参考] 先人の知恵:
REA(リソース、イベント、エージェント)
人、場所、モノ、事象
マスター系データとトランザクション系データ選択した戦略;
データモデルの指針
event:
事実の蓄積
クエリの主要な関心ごとは「いつ何があったか」changeset:changeの蓄積
changeとは: 変更というセマンティックを持つevent

クエリの主要な関心ごとは「変更の結果」

選択した戦略;
データモデルの指針

event型テーブル: event保存changeset型テーブル: changeset保存
relation型テーブル: changeset間の関係
analyze型テーブル: eventの分析結果

選択した戦略;
アプリ開発者に見える世界
アプリ開発者はJavaコードのDTOクラスだけを意識
裏側のクエリを隠蔽
データベースマイグレーションもDTOベースで実施

自動インデクシングの仕組みを独自実装
アプリ開発者はDTOクラスにアノテーションを付与
バックグラウンド処理で別テーブルを自動更新
RDBのCREATE INDEXと同じ感覚で使う
Cassandraのセカンダリインデックスは未使用(遅いので)トランザクション管理層を独自実装

選択した戦略;
主キーの設計例
CREATE TABLE post (volume_number INT,
code text,start_time TIMESTAMP,
name text,
disabled BOOLEAN,
PRIMARY KEY (volume, code, start_time));=> volume:パーティションキー、(code, start_time)でソート=> リレーショナルモデルではなく、スケールのオーダーを意識した巨大な表を意識して主キーを設計
縦(行数)のオーダーは無限横(列数)のオーダーは10万程度まで選択した戦略;
自動インデクシングのコード例@lombok.Data@AutoIndex(Employee.class)@Join(with = AverageSalary.class, as = "a", where = {"deptId = a.deptId", "sectionId = a.sectionId"})
class EmployeeWithSalaryAboveAverageIndex {@Keyprivate UUID id;
private String name;private String deptId;
private String sectionId;@Column(from = "a.averageSalary")
private double averageSalary;}選択した戦略;
自動インデクシングのコード例@lombok.Data@AutoIndex(value = Employee.class, elasticsearchType="{ElasticsearchTypeName}")@Join(with = Project.class, as = "p", where = "projectId = p.id")class EmployeeWithProjectIndex {@Keyprivate UUID id;
private String name;@Column(from = "p.name")
private String projectName;}

選択した戦略;
独自のトランザクション管理層
追記オンリー
トランザクション開始
APサーバ

Commit-log
テーブル

Realテーブル

トランザクションコミット
Realテーブルへの反映を待たずにトランザクション成功

遅延してRealテーブルへの反映（リトライ込みの高可用性書き込み）可能な範囲でAtomic処理に集約
トランザクション開始
APサーバ

Commit-log
テーブル

トランザクションロールバック
Realテーブルに反映させずに破棄選択した戦略;
トランザクション管理の戦略(考える順序)
0. トランザクション管理の分割を考える(MessageQueueで処理分割)
1. アトミック更新(CassandraのライトウェイトTX)で解けるならそれで解く

2. アプリ側の楽観的並行性制御で解けるならそれで解く
3. 結果整合性だけで解ける(収束するマージ操作やアンドゥ操作を定義可能)ならそれで解く

4. イミュータブルデータ(元データの更新の代わりに、新しいデータを追記オンリーで生成)で解けるならそれで解く
5. べき等性のある操作だけで実現可能ならそれで解く (参考http://songofcloud.gluegent.com/2009/11/blog-post_18.html)

6. commit log tableを使う
7. 分散ロックを使って処理を直列化
3から6は「クライアントコンシステンシ(=更新の反映の遅れ)」の課題がある選択した戦略;
トランザクション管理の戦略(クライアントコンシステンシ対策)
ラムダアーキテクチャ(read-your-writesコンシステンシ)

最悪、アプリ側でのread repair相当処理(なるべく避けたい...)現アーキテクチャで得られるモノ

高速な参照性能
裏でインデクシングして単純なクエリにしているから高速な更新性能
複雑なインデクシングを後回しにしているから

[参考] 次期Cassandra

グローバルインデックス(裏で別テーブルを自動生成)
独自実装の自動インデクシング機能の一部は不要になるかも

RAMPトランザクション(低遅延の分散トランザクション)
独自実装のトランザクション管理層の一部は不要になるかも4. RDBなしの不安

現アーキテクチャで失うモノ
リレーショナルモデル
ACIDトランザクション
枯れた実装
開発者の慣れ運用ツールやノウハウ

リレーショナルモデルがなくても大丈夫か?
なければないでどうにかなる
ワイドローデータモデル + Elasticsearch

発想の転換(リレーショナルモデルは実行状態よりスナップショット的な永続状態に適したデータ構造)リレーショナルモデルがなくても大丈夫か?
アプリ開発者の典型的不安
任意カラムのインデックス
結合処理
集計処理
ソート処理

リレーショナルモデルがなくても大丈夫か?
アプリ開発者の典型的不安
別テーブルで代替
任意カラムのインデックス(次ページ)
結合処理
別テーブルで代替
集計処理
ソート処理
ワイドローまたはElasticsearchで代替

結合処理の戦略
アプリ側で結合IN句相当に制限があるのでクエリ数の 1+N 問題が発生

非正規化
別テーブル
ワイドロー

ACIDトランザクションがなくても大丈夫か?
Relaxing ACIDが原則
既に説明したトランザクション管理戦略
必要なら分散ロックで直列化する割り切りコンシステンシ
文脈によっていくつかある•••

レプリケーションの同期、非同期(準同期)の話
外部制約のような整合性の話
one fact in one placeでないためにアプリから見て整合が取れない話

コンシステンシ• レプリケーションの同期、非同期(準同期)に関連する話
RDBMSでも発生する話。
CassandraのQuorumはひとつの解• 外部制約のような整合性の話
Cassandraには、(ある意味)存在しない• one fact in one placeでないためにアプリから見て整合が取れない話
read-your-writesコンシステンシで妥協枯れた実装がなくても大丈夫か?

今のところCassandraの安定性に困ってはいない
ノード障害が適度に起きても、サービス全体が死なない(クラウド脳)

開発者の慣れの問題は大丈夫か?

RDBのアンチパターン(1+N問題、EAVなど)もアンチパターンとは限らないなどの発想の転換が必要

運用ツールやノウハウは大丈夫か?

社内に5年近い運用経験あり
監視ツール
無停止スケールアップ
ローリングアップデート

Cassandraの課題;
当然良いことばかりではない
テーブル数の上限
ソート機能の制約
レンジクエリの制約
read-repairの同期/非同期の使い分け
削除が弱い
IN句相当クエリの制約

それでも残る懐疑心
並列処理は本質的に難しいのでは?
イミュータブルデータは空間効率が悪くないか?
RDBMSでさばけないほど高負荷の業務アプリ?
シーケンスは実現可能?
分散ロックが必要になるケースはない?
Cassandraはreadが遅い?
脱RDBで本当に速くなる?
業務アプリにNoSQLは向いていないのでは?

それでも残る懐疑心(1)
並列処理は本質的に難しいのでは?
=> 難しい。脱RDBは脱トランザクション管理ではない。
=> ACIDとしてRDBMSに丸投げしたほうが圧倒的にラクだが、Relaxing ACIDが不可能とは思っていない

イミュータブルデータは空間効率が悪くないか?
=> 悪いと思う。特に削除処理に極めて弱い。
=> GC相当の処理(VacuumやCompaction)を許容できる物理リソースが必須(クラウドとNoSQLに一定の補完関係があるとすればここ)

RDBMSでさばけないほど高負荷の業務アプリ?
=> 普通に想像する業務アプリとだいぶ違う機能を目指している=> マルチテナントで世界規模のサービスを目指しているそれでも残る懐疑心(2)シーケンスは実現可能?
=> LW-TXと楽観的並行制御で実現可能(waitがないので獲得側はbusy実装)=> 特別に高速ではないCassandraはreadが遅い?
=> 今のところ困っていないが、キャッシュに逃げる日があるかもそれでも残る懐疑心(3)
脱RDBで本当に速くなる?
=> 並列制御を考えずに普通に作ると、一定の複雑さを持つアプリではむしろ遅くなると思う

業務アプリにNoSQLは向いていないのでは?
=> 「業務アプリ」とひとくくりにしないほうが良い=> 会計業務はRDB利用時もINSERT操作中心で、ログを積み上げていく印象(あるいはそのように見立てる)

5. まとめ「脱RDBが技術的に正しいか?」と言われると、技術者として真摯に答えると「わからない」が回答
しかし、RDBを使った普通の業務アプリは普通に作れるので、今さら(個人的に)頑張る意欲がそこまでない
しかし、普通の業務アプリを普通に作って、世界のマーケットで戦えるイメージもない「世界のどこで使っても応答性能100ミリ秒で、かつ落ちない業務アプリ」を作れば世界で戦える
この実現のためには、リレーショナルモデルだけでは困難だとは思う(実装としてのRDBMSの可否は別として)

宣伝

Cassandra Summit 2015

2015
4/21
(Tue)