ScyllaDB虎の巻
ScyllaDBで効果的にデータモデリングや運用を行うための自分用の虎の巻を作成したため、ついでに公開します。
公式ドキュメント及びブログ、質問へのScyllaDB開発者の回答を中心に収集した情報を整理して記載しています。
scylla_setupを実行する
自動で最適な設定をしてくれる。
cf. ScyllaDBのパフォーマンスを最大化するためのヒントとコツ
システム要件を満たす
cf. システム要件, ScyllaDBのパフォーマンスを最大化するためのヒントとコツ, 共有環境でのScyllaDB
クエリファーストのデータモデリング
アクセスパターンに基づきデータモデリングを行う必要がある。
cf. 5つの性能問題を回避するためのNoSQLデータモデリングのガイドライン, ScyllaDBアプリケーションのための最良慣行
クエリ
クエリは、原則として単一のパーティションにアクセスするものでなければならない。
複数のパーティションにアクセスするクエリは効率が悪い。
また、フルスキャンは基本的に避けなければならない。
cf. アプリケーションのワークフローとクエリ分析
ホットアクセスパターンは避ける。
cf. 5つの性能問題を回避するためのNoSQLデータモデリングのガイドライン, 性能のためのデータモデリングの最良慣行
時系列データへのクエリ
時系列データへのクエリは、過去 < time < 現在
のような閉じられた範囲ではなく、過去 < time
のような開かれた範囲を指定すべきである。
これによりキャッシュが効果的に行われ、パフォーマンスが向上する。
cf. 時系列データをキャッシュするための時間ベースのアンチパターン, 内部キャッシュの内側
キャッシュ
ScyllaDB は優れた内部キャッシュを持っている。
必要に応じて、これを利用した設計をする。
cf. チームが外部データベースキャッシュを廃止する理由, ScyllaDBでキャッシュを置き換える, Redis vs Memcached vs Scylla Cache - どれを選ぶべきか?
BYPASS CACHE
しない限り、実行された読み取りの全てがキャッシュされる。
cf. 内部キャッシュの内側
BYPASS CACHE
大量のデータスキャンや再度読み取られる可能性の低いデータのクエリはキャッシュすべきではない。
そのようなデータのキャッシュのために、重要なキャッシュが無効化される可能性がある。
クエリでBYPASS CACHE
を指定すると、データがキャッシュされなくなる。
これは特に範囲クエリで有用である。
cf. 内部キャッシュの内側, ScyllaDBのパフォーマンスを最大化するためのヒントとコツ ScyllaDB CQL 拡張
キースペース
実際のシステムでは、関心事を分離するためにさらに多くのキースペースにテーブルを分割する場合がある。
cf. 物理データモデリング
パーティション
パーティションは小さすぎず大きすぎもしないように設計する。
cf. 高度な主キーの選択
巨大なパーティションやホットパーティションはクラスターの性能を悪化させるため回避しなければならない。
負荷とデータをクラスター内のノードやシャードによく分散するため、パーティションキーの濃度が高いことが望ましい。
cf. 5つの性能問題を回避するためのNoSQLデータモデリングのガイドライン, ScyllaDBアプリケーションのための最良慣行, 性能のためのデータモデリングの最良慣行, 大量のパーティションの影響, テーブルが保持できるレコードの最大数は何ですか?
パーティション当たりの望ましい行数は、実際のクエリや負荷などの要件により異なる。
実際のケースでは、パーティション内に数千行以上あるような場合もある。
ScyllaDBのデフォルト設定では、パーティションのサイズが1000MBを超えると警告の対象となる。
少なくとも2018年までは、100MBが閾値となっていた。
Cassandraでは、パーティション当たり10万行以下、サイズを100MB以下に抑えることが望ましいとされている。
cf.クラスタリングキーの重要性, パーティションあたりの最大行数, ScyllaDBの大規模パーティションテーブル, ScyllaDB 2.3からの大規模パーティションのサポート, パーティションサイズの見積もり
莫大な数の小さなパーティションは、より多くの計算資源を要求する可能性がある。
Bloomフィルターのサイズを大幅に増加させ、メモリを圧迫する可能性がある。
期待するほど多くのパーティションがキャッシュに収まらない場合がある。
二次索引やマテリアライズドビューを持つテーブルでは、修復時のビュー更新生成が非常に遅くなる場合がある。
cf. テーブルに単一行のパーティションを設定するのは悪い習慣か?, ブルーム・フィルターのサイズを改善する方法について, 非常に短いパーティションを持つテーブルの無駄なストレージ, パーティションを設計するために提案が必要, テーブルが保持できるレコードの最大数は何ですか?
SSTableの圧縮、ストリーミング、修復等の内部操作では、パーティション全体をメモリに読み込むことはない。
ページングやリミット、境界の指定を行う場合、パーティションのサイズに関係なく問題を引き起こすことはない。
クエリ中は、関連する結果だけが読み取られる。
cf. 大規模パーティションのサポート
必要ディスクスペースの推定
実際のパーティションのセル数とサイズを見積もる。
cf. データモデルの評価と洗練
ブルームフィルタの必要ビット数
なお、ScyllaDBのデフォルトの許容偽陽性率(bloom_filter_fp_chance
)は、0.01
である。
パーティション数(n) | メモリ使用量(バイト) | メモリ使用量(MB) |
---|---|---|
100,000 | 120,000 bytes | 約0.114 MB |
1,000,000 | 1,200,000 bytes | 約1.144 MB |
10,000,000 | 12,000,000 bytes | 約11.44 MB |
100,000,000 | 120,000,000 bytes | 約114.44 MB |
最適なハッシュ関数の数
cf. ブルームフィルタ計算機, テーブルオプション
デザインパターン
ワイドパーティションパターンは、クラスタリングキーを利用しパーティションに複数の行をグループとして格納する。
これによりクエリが単一のパーティションに限定され、複数のパーティションにクエリする必要がなくなる。
cf. 論理データモデリング
非正規化は、複数のテーブルに同じデータを書き込むことである。
ScyllaDB は N + 1 問題の解決策として、RDBのような表結合ではなく書き込み時の結合として、非正規化を行う。
書き込みは高速であるため大きなコストは発生しない。
cf. 非正規化
バケット化は、パーティションキーに日や月の情報を含めることで、パーティションを小さくする方法である。
値の接頭辞をパーティションキーに含める方法もある。
cf. クエリ設計, ScyllaDBアプリケーションのための最良慣行, テーブルに単一行のパーティションを設定するのは悪い習慣か?
時系列データのバケット化は、ホットスポットを引き起こす可能性がある。
実際の負荷に応じて、バケット内に複数のパーティションを設けることを検討する。
cf. Cassandraによる高度な時系列
アンチパターン
キューとしてテーブルを使用してはならない。
データの削除に依存する設計は性能が悪くなる可能性がある。
cf. 論理データモデリング, Cassandraのアンチパターン:キューとキューのようなデータセット
ユーザー定義型
主キーに含まれない列の重複を減らし、設計の複雑さを抑えるためによく使用される。
定義が有効な範囲は、宣言が行われたキースペースである。
キースペースごとに宣言が必要であるのは、データモデル設計における多くのトレードオフの一つである。
cf. 物理データモデリング
コレクション
コレクションは、読み取り時に全体がメモリ上に読み込まれる。
パーティションよりも遥かに早くパフォーマンスを悪化させるため、非常に小さく保たなければならない。
cf. ScyllaDBアプリケーションのための最良慣行, 性能のためのデータモデリングの最良慣行, NoSQLデータモデリングの間違い上位
ListよりSetを優先し、可能ならば凍結させる。
cf. 特定の問題に対する列設計とユーザー定義型の推奨事項
デフォルトでは1万要素以上のコレクションは警告の対象となる。
cf. ScyllaDBの大きな行と大きなセルのテーブル
COUNT()は遅い
COUNT()
は全ノードでフルスキャンを行うため効率が悪い。
ScyllaDB 5.1 では効率が改善され、すべてのノードとシャードで自動的に並列化される。
整合性レベルLOCAL_ONE
と併用することで、以前よりはるかに高速になるが、それでもかなりのCPUとI/Oリソースを必要とする。
cf. テーブル内のすべての行を数えるのは遅い, テーブルのおおよその行数を取得するには?
行数が十分に少ないことが保証される場合は使用してもよい。
確認できた情報の範囲では、予測される最大の行数が3以下の場合は問題なく、1000以上の場合は問題がある。
cf. ScyllaDB で N 行を取得する最適な方法 - カウント、制限、またはページング
必要に応じてカウンターを使用する。
cf. ソーシャルメディアの「いいね」を支えるデータモデリング
ORDER BY は使用すべきではない
クラスタリング順序の逆順のクエリはパフォーマンスを悪化させる。
テーブル作成時に順序を指定しておく必要がある。
cf. ScyllaDBのパフォーマンスを最大化するためのヒントとコツ, データ定義
INSERT, UPDATE, DELETE
これらは更新セルのみを複製する
cf. パーティションあたりの最大行数
BATCH
異なるパーティションに対するクエリを一つのバッチに含めるべきではない。
cf. ScyllaDBアプリケーションのための最良慣行
削除は書き込み
DELETE
やTTLの期限切れは、データを即座に削除しない。
実際の削除は、データが削除されたことを示す墓石と言われるマーカーの書き込みを行う。
後述の通り、墓石は読み取りパフォーマンスを悪化させるため、削除を減らす設計が必要である。
削除はパーティション単位で行った方が効率が良い。
cf. 性能を低下させるNoSQLデータモデリングの誤り
墓石(tombstone)
データの削除時に書き込まれるマーカーを墓標と言う。
墓石が多ければ多いほど、読み取りパフォーマンスが悪化する。
墓石が立ったデータは、gc_grace_seconds
に設定された時間の経過後に、SSTable圧縮のタイミングで削除される。
cf. 墓石, 性能を低下させるNoSQLデータモデリングの誤り, NoSQLデータモデリングの間違い上位
null と unset
null値は墓石としてスペースを使用する。
unsetは値を設定しないため、ディスクに保存されない(スペースを使用しない)。
cf. スペースを使わないようにスキーマをモデル化するにはどうすればいいか
軽量トランザクション
効率が悪いため、必要な場合にのみ使用する。
cf. 軽量トランザクションの概要
二次索引とマテリアライズド・ビュー
WHERE
句で指定したい列がある場合は、原則としてテーブル作成時に設定すべきである。
二次索引やマテリアライズド・ビューは、処理量とストレージ容量を増やす。
濃度の高い列を指定する必要がある(クエリで取得される行が全体の1%未満であることが目安)。
cf. マテリアライズドビューの内部, まとめとどちらを使うべきか
二次索引
内部ではマテリアライズド・ビューを使用しているが、プライマリキーと索引化された列以外の列の更新の影響を受けないため効率的である。
一方で、クエリ時には索引検索サブクエリと要素検索サブクエリを行うため、読み取りパフォーマンスが悪化する。
cf. グローバル二次索引, マテリアライズド・ビューと索引 実践実習2
同じテーブルにグローバル二次索引とローカル二次索引の両方を作成できる。
この場合、各クエリに対して適切な索引が自動的に使用される。
cf. ローカル二次索引
二次索引は追加の読み取りと、最大2つの追加の書き込みという高いコストがかかる。
cf. グローバル二次索引によるscylladbへの書き込みパフォーマンスへの影響
グローバル二次索引
非プライマリキーによるフィルタリングのために使用する。
cf. 二次索引
読み取りに追加のコストがかかる。
クエリを受け取った調整ノードは、最初にマテリアライズドビューに元のテーブルの主キーをクエリする。
その主キーを使用し、元のテーブルにクエリが行われる。
cf. マテリアライズド・ビューと索引のハンズオン・ラボ2, 索引、フィルター、その他の動物
書き込みにも追加のコストがかかる。
クエリを受け取った調整ノードは、対象のパーティションを保存するノードにデータを送信する。
ノード内のレプリカがデータを保存すると、索引に使用されるマテリアライズドビューを元のテーブルに同期されるように、ビューの更新が生成、送信される。
cf. 索引、フィルター、その他の動物
書き込みはローカル二次索引より遅くなる。
cf. グローバル二次索引
ローカル二次索引
基礎テーブルのパーティションキーを継承しつつも、異なるクラスタリングキーを指定したい場合に使用する。
cf. マテリアライズド・ビューと索引のハンズオン・ラボ2
マテリアライズド・ビューのパーティションは、元のテーブルの対応するパーティションと同じノード上に存在することが保証される。
同じノード上で操作が行われるため、通信のオーバーヘッドが無く効率的である。
cf. 索引、フィルター、その他の動物
マテリアライズド・ビュー
他の列によるフィルタリング、異なるソート順、クエリの事前計算のために使用する。
二次索引と異なり新しいテーブルが作成されるため、読み取りパフォーマンスが悪化しない。
特定の値を持つ行だけで構成されるものも作成できる。
cf. マテリアライズドビュー概要
主キーには、基礎テーブルの主キーの構成要素を全て含む必要がある。
cf. マテリアライズド・ビューと索引のハンズオン・ラボ1
マテリアライズド・ビューの更新は非同期である。
サーバーが過負荷になると、基礎テーブルの変更を反映するのに時間を要する場合がある。
基礎テーブルへの書き込みと読み込みは常に優先されるため、基礎テーブルの可用性に影響は無い。
整合性水準は適用されない。
cf. マテリアライズドビューの内部
必要に応じて非正規化の代わりに使用すると、アプリケーションとデータベースの間の往復回数を減らしパフォーマンスを向上できる。
cf. マテリアライズドビュー
ALLOW FILTERING
アドホックな用途を除き、原則として使用すべきではない。
読み取り頻度が極めて低い上にパーティションキーの濃度が低い場合(クエリで取得される行が全体の1%以上であることが目安)に使用してもよい。
書き込みの追加コストは無く、ストレージ容量を増やさない。
読み取りの追加コストは莫大である。
cf. フィルタリング, データのフィルタリングと非正規化
キャッシュの恩恵を受けられると、二次索引よりも高速に処理できる場合がある。
cf. 索引、フィルター、その他の動物
サイズ階層型圧縮戦略(Size-tiered Compaction Strategy)
スペース増幅を引き起こし最大で2倍のスペースを一時的に占有する可能性がある。書き込み増幅は少ない。SSTableが4つ揃うまで圧縮されないため、不要なデータの除去が遅れる可能性がある。
cf. 圧縮戦略, 間違った圧縮戦略を選択してパフォーマンスを台無しにする方法
レベル化圧縮戦略(Leveled Compaction Strategy)
主に読み取り、一部更新ありの場合に採用する。無視できない量の書き込みがある場合は、書き込み増幅が発生し圧縮が追い付かなくなる可能性がある。
cf. 圧縮戦略, 間違った圧縮戦略を選択してパフォーマンスを台無しにする方法
時間枠圧縮戦略(Time-window Compaction Strategy)
時系列データに推奨される。不均一なTTLを用いると非効率になる可能性がある。
データの上書き及び削除は絶対してはならない。
cf. 圧縮戦略, 間違った圧縮戦略を選択してパフォーマンスを台無しにする方法, 時間枠圧縮戦略
監視
パフォーマンスの問題を特定しデータモデリングを改善するために必ず導入する。
cf. 5つの性能問題を回避するためのNoSQLデータモデリングのガイドライン, ScyllaDBアプリケーションのための最良慣行, 性能を低下させるNoSQLデータモデリングの誤り
スケール
EBSボリュームを持つt2.smallインスタンス(相当の計算資源)を検討し、成長に応じてより大きなインスタンスにスケールする。最終的には、100GB程度を超えたらi3en.large(相当の計算資源)に移行する。
cf. 新規スタートアップのための低コスト手法
情報共有
これらの蓄積は、各プロジェクトで得られた知見がコミュニティで共有された結果である。
公式ブログを追うことは特に有益である。
cf. ScyllaDBアプリケーションのための最良慣行, データの最良慣行
Discussion