データ指向アプリケーションデザイン 勉強会メモ
データ指向アプリケーションデザイン 勉強会メモ
勉強会の内容+ α(メモ)
ref:
データ指向
- オブジェクト指向:オブジェクトを中心にプログラムを設計
- データ指向:「データ量」「データの複雑さ」「データの変化速度」を中心に考える設計
データ表現
データ量少ない場合:データの表現、生成しやすさを重視
- テキストデータフォーマット:CSV, JSON, XML など
- バイナリデータフォーマット:MessagePack, Thrift, Protocol Buffers, Apache Avro
- (データとして完結している)自己記述型(self-describinig)データフォーマット
- 例外:ProtocolBuffers はデータ自体にスキーマを含まない(self-describinig ではない)
- (データとして完結している)自己記述型(self-describinig)データフォーマット
データ量多い場合(データ基盤):省メモリ、圧縮しやすさ、ストレージの格納しやすさを重視 データを節約してコンパクトにする方法
- テーブルフォーマット
- 行指向(row-oriented)フォーマット
- RDBMS でよく使われる
- 1行ずつ表現
- レコード単位で更新しやすい
- 列指向(column-oriented)フォーマット
データアクセス頻度
読込が頻繁
- 変化しない = 不変データ(イミュータブル)
- 蓄積されるログ(列指向フォーマットと相性〇)
- 読み込み特化型 (read-intensive)
- データへの高速なアクセス、分析の速さが問われる
- 高速化手法(データを分散)
- パーティショニング(シャーディング)
- データを時間範囲(1h, 1d 等)、キー範囲などで分割
- 必要なデータのみにアクセスが済む、別領域に分かれるため並列アクセス可(高速化)
- レプリケーション
- 同じデータを複数個所(マシン、ディスク)に配備し、並列アクセス&耐障害性を確保
- 実体化ビュー(Materialized View)
- 一度計算したクエリ結果を保存、再利用可能にする
- パーティショニング(シャーディング)
更新が頻繁(データが 1 か所の場合)
書き込み特化型(write-intensive)
更新対象を素早く見つけられる必要がある = 高速検索
- → 解決策:インデックス
- ディスク上のデータを高速に見つけるためのデータ構造
- B-Tree、Hash index、SSTable(Sorted String Table)
- → 解決策:インデックス
B-Tree
- ディスク上のページに、次ページへのルックアップテーブルを格納
- RDBMS では 基本 CREATE INDEX 命令で作られる
- ※ 1 回の CREATE INDEX で 1 つの B-Tree が作られる。B-Tree を複数作ると更新時にそれらすべて更新する必要があるのでその分更新速度が落ちる
- 高い更新性能を発揮。空き領域にデータ挿入しやすいため
- ページ数が増えても木の深さはあまり増えないため、ディスクのランダムアクセスを減らせる=アクセスの回数を減らすのに役立つ
発展:Log-Structured Merge (LSM) Tree
さらに更新が頻繁な場合にも対応できる。(最近よく使われている)
- レコードの追加、コンパクション操作(恐らくメモリコンパクション=空き領域の断片化を解消して連続した広い空間に再編。メモリ上を整理して合成する操作)を分離
- ランダムアクセスに強いメモリ上にどんどん追加。更新される旅にメモリ上をソート
- バックグラウンドで、定期的にソート済みのデータと合成(marge sort)して次レイヤーに保存(L0,L1,… = ディスク、外部ストレージなど)
- 性能特性
- 更新時のランダムアクセスを減らせる(シーケンシャルなアクセスなため)
- 各種オーバヘッドがある
- 読み込み時に各レイヤーにアクセスする必要がある
- 書き込みの増幅(Write Amplification)が生じる (次レイヤーに書き出すため同じデータの書き込みが複数回生じる)
例:LevelDB、RocksDB(C++)、AWS S3(Rust) など
分散データ(データが複数個所)
データを複数個所(複数ノード)に分散する場合、考えるべき問題の複雑さが数段上がる
- ノード間でデータを送受信するために、分散システムが必須
- 分散ステートマシン=各ノードが同じ状態になる必要がある(membership)
- (サーバ間のデータを整える目的で) サーバ・クライアント間でのデータ通信も必要(HTTP 上で実装されることが多い)
- REST プロトコル、RPC を使う必要がある
- 複数のノードで頻繁に更新がある場合
- 障害対応
以上を踏まえて、システムが正しく動作するように設計する必要がある
24 時間 365 日稼働するサービス設計
- 稼働率:許容されるダウンタイム
- 99.9 % availability = 8.7 時間/年
- 99.99 % availability = 53 分/年
- 99.999 % availability = 5 分/年
- アプリケーションのデプロイ (その際には止める必要があるが)
- 冪等性
Compute - Storage の分離 (常にデプロイし続けるための設計)
- 近年の標準
- 常にデプロイし続けられるよう、クエリ実行(Compute)とストレージ(Storage)を分離
- クエリエンジンとストレージを別々にスケールしたり、ローリングアップグレード(同じ機能を持った複数サーバ構成のシステムをアップデートする手法の一つで、システムの稼働状態を維持しながら1台ずつ順番にアップデートしていく方法)
例:Snowflake、Amazon Redshift(データウェアハウスサービス)など
分散トランザクション
そもそもトランザクション処理
- ACID :Atomicity(不可分性)、Consistency(一貫性)、Isolation(独立性/分離性)、Durability(永続性)
- 分離性(Isolation)が最も重要
- 直列化可能性(Serializability)が基本
- トランザクションが1つずつ実行されたのと変わらない状態を維持
- オーバヘッドが大きいため現実的には使われにくい(使う場合は考える必要がある)
- 解消する手段:弱い分離性
- Read-Committed、Snapshot Isolation、MVCC(Multi-version concurrency contorol)
- 妥協しないアプローチ:Serializable Snapshot Isolation (SSI)
- トランザクション異常(anomaly)の種類
- ファントム(まだ存在しないレコードに、どうロックをかけるか)※SSI だと防げない
- Write Skew(同じスナップショットを読んで、違う場所に書き込む)
- Repeatable Read (ダーティリード:なし、ノンリピータブル:なし、ファントム:あり)とは違う
- トランザクション異常(anomaly)の種類
- 直列化可能性(Serializability)が基本
分散トランザクションの世界
- 複数ノード間、または複数システム間でトランザクションを実装
- 一貫性、合意、耐障害性
- 線形化可能性(linearizability)が重要
- レプリカが複数あっても、ユーザにはコピーが 1 つしかないように見せる
- 更新操作が終了した途端、全ノードが同じデータを見られるように保証する
- 実装するために必要な技術要素 (本 9 章)
- リーダー選出(leader election)
- サービスディスカバリ
- メンバーシップ管理
- 使用例:Zookeeper、etcd(Kubernetes 内部で使用)
- 異なった方向性のアプローチ(2022 ~)
- アプリケーション側のクライアントで分散トランザクションを吸収(ScalarDB)
極み:Amazon Aurora (SIGMOD 2018)
AWS Aurora: AWS Aurora とは?その特徴とは?
- クラウド向けリレーショナルデータベース
- 分散版 MySQL:
- 6つのレプリカを作成
- 2 copy × 3 AZs 。AZ が一つ落ちても quorum (多数決) が取れる = 4/6
- MySQL のデータベース操作を「ログ書き込み」のみに特化して簡略化
データの複雑さ
テーブルの意味の変化
- 従来:RDBMS にある最新データ(Snapshot)
- 現代:
- 時間とともに変化するデータ、そこから表す派生するデータ(derived data)を表す
- 列指向クエリエンジンの発展により、分析クエリが容易になり導出データ(derived data)が大量に
- バッチ処理とストリーム処理の区別がなくってくる位いろんなデータが作られる
導出データ (derived data)
- 1つデータから数千の導出データが作られることも (実例:Treasure Data 社)
- クエリで生成されるデータの依存関係や、データ履歴を管理する必要がある
分散バッチ処理
バッチ処理:Unix コマンドでのデータ処理に近い(コマンドのパイプ繋ぎ)
分散(複数ノード)で実行するアイディア
ストリーム処理
- バッチ処理:保存されているデータに対してクエリ実行
- ストリーム処理:
- クエリをデータの入力側に送り、常時クエリを実行し続ける
- あるいはデータの変更をとらえ処理する。 マイクロバッチ、CDC(Charge Data Capture)
- (上記の概念を上手にまとめたのが )Dataflow Model の概念が必要
- 時系列データ処理方法の分類・パターンの定義
- 遅れてやってくる(late arraival)データの処理を埋め合わせる必要性を提示
- event time(データを捉えた時刻), processing time(データがシステムで見えるようになった時刻)を 2 次元の watermark で管理
- 2 次元の軸を分割してもれなくデータを処理する
SLO:システムパフォーマンスのはかり方
事業者が定義・合意した SLA を履行するために、サーバーやネットワーク、ストレージなどの各領域の稼働率、性能、可用性、セキュリティ、サポートといった項目ごとに、パフォーマンスの目標値
- 平均値を見るだけでは、データ規模が大きく性能が遅くなりやすい重要顧客の様子が見えてこない
- p99.99 の極端な領域での改善は、コストの割りに利益を生まないこともある
まとめ
- データ指向アプリケーションデザイン
- 分散データシステム入門の決定版
- より深い世界に踏み込むための手引き
- データ分野全般をカバーしたガイドブック: https://github.com/ept/ddia-references
- 最新は自分で情報収集する必要あり。英語論文など