プロジェクト

全般

プロフィール

Cassandra DB

はじめに

分散型データベースでNoSQLに分類されるCassandra DBについて調べたことを整理したページです。

Cassandaの概要

Apacheプロダクト(オープンソースソフトウェア)として開発・提供されているスケーラビリティの高い分散型のNoSQLデータベースで、単一故障点を排しビッグデータを扱うのに適したデータベースです。大量のデータを高速に書き込み・読み込みに対応しています。

2007年にFacebook社のエンジニア Avinash Lakshman(Amazon Dynamo の作者)とPrashant Malikにより開発、2008年にオープンソース化し、2009年にApacheインキュベータプロジェクトに寄贈、IBM、Twitter、Rackspace社の貢献を受けて2010年にApacheトップレベルプロジェクトに昇格しました。

米DataStax社が、Cassandra DBの開発を主導するとともに商用版 DataStax Enterpriseを提供しています。
適用事例では、Netflix、Apple、CERN、Hulu、IBM、Instagram、Uber、Yahoo他。

https://cassandra.apache.org/_/case-studies.html

リリース経緯

Cassandra 5.0 2024年
Cassandra 4.1 2022年
Cassandra 4.0 2021年
Cassandra 3.0 2015年
Cassandra 2.0 2013年
Cassandra 1.0 2011年

NoSQLの分類とCassandra

NoSQLデータベースには、キーバリュー型、ドキュメント型、カラムファミリー型、グラフ型がありますが、Cassandraはカラムファミリー型に分類されます。

カラムファミリーまたはワイドカラムストアはキーバリューに類似していますが、キーバリューは1つのキーに1つの値を持つのに対して、カラムファミリーは1つのキーに複数のカラム(カラム名と値)を持ちます。

論理的には次のような構造です。

primary key 列1 列2 ・・・ 列N
AAAA 値1 値2 ・・・ 値N
BBBB 値1 値2 ・・・ 値N

primary keyにより行(raw)が特定されます。primary keyも列の集合で、パーティションキーとクラスタリングカラムから構成されます。
パーティションキーは、複数の行を格納するパーティションを特定します。
クラスタリングカラムは、パーティション内で行がどの順序になるかを規定します。
primary keyを構成する列以外の列は値を省略することができ、スパースなデータとすることができます。
実際には、パーティションキー毎に、列の数だけ複数のキーバリューが存在(列名をキー、列の値をバリュー)します。

パーティションキーと列のセットをtableとし、複数のtableを束ねたkey spaceの構成を取ります。
tableの構造は、スキーマ定義を必要とします。(完全にスキーマレスではない模様)

クエリー言語

CassandraはNoSQLに属しますが、SQLに類似したCQL(Cassandra Query Language)を使用します。

クラスタ

Cassandraは、複数の計算機(ノード)からクラスタを構成します。データは、パーティションキーでどのノードに配置されるかが決まります。
クライアントはどのノードに問い合わせてもよく、クライアントからクエリを受けたノードがコーディネータ(調停)ノードとなってデータをクラスタ内で取得しクライアントへ結果を返します。

Cassandraではデータを複製して複数のノードに配置します。これにより、ノードが故障してもデータの欠落は発生しません。
また、クラスタのコントロールをするマスターは存在しません。これにより単一故障点(Single Point Of Failure)を持たない構成を実現しています。

データ量が増えてきた場合、ノードを追加することでスケールアウト(性能向上)が可能で、またノードの追加にあたりノードの停止を不要としています。

さらに、クラスタ(データセンター)を複数構成しクラスタ間を同期することで、DR(Disaster Recovery)対応も可能としています。

分散システムとCAP定理

分散システムでは、次のCAP(Consistency、Availability、Partition-tolerance)の3つを同時に満たせないCAP定理があり、Cassandraは、3つのうちAとPを満たす設計です。

  • 可用性(Availability):ノードの1つが故障しても他のノードが応答
  • 分断耐性(Partition-tolerance):データセンター間が切断しても各データセンターがそれぞれ稼働継続

ただし、データを複製し複数ノードに配置することは、一貫性の欠如が課題となります。Cassandraではデータの読み書き時に複数ノードに複製したデータ間に不一致があっても、多数決(3ノードのうち2ノードが一致している)で、最新の値を返却するとともに、それを修復する仕組み(Read Repair)を持ちます。

整合性(Consistency)については、次の調整が可能です。

  • ONE:最初にリターンしたノードのデータを返却
  • TWO:2つのレプリカがリターンしたデータを返却
  • THREE:3つのレプリカがリターンしたデータを返却
  • QUORUM:過半数のリターンノードのデータからタイムスタンプの新しいものを返却
  • ALL:すべてのノードがリターンした後、タイムスタンプの新しいものを返却
  • LOCAL_QUORAM:同一データセンター内のレプリカノードの過半数がリターンしたデータを返却

開発試験でのクラスタ

Cassandraは分散型のデータベースでクラスター構成を取りますが、開発・試験用に単一マシンで実行することが可能です。また、ローカルマシンにDockerで複数ノードを実行してクラスタ構成を取ることもできます。

パーティション

Cassandraは、ノードへの分散配置の単位をパーティションとし、パーティションキーにより配置ノード(例えば3つ)が決まります。
そのため、均等に分散配置するには、パーティションキーをどのように定義するかデータモデルの設計が重要となります。

パーティションサイズは2GB程度に収まるようにデータモデルを設計するのがミソ。

レプリケーションファクタでデータのレプリカをいくつのノードに分散するかを制御

ファイル構造

LSM(Log Structured Merge tree)で、高速化書き込みを実現
クライアントからの書き込み要求を Commit Logファイルに書き込み、メモリ上のmemtableに書き込み、非同期にSSTableファイルにフラッシュ
SSTableファイルは追記を基本とし、新しい世代のSSTableファイルが作られる。データの更新をするとCompactionが発生するため性能に影響

苦手なこと

  • パーティションをまたがる検索
  • データの更新

できないこと

  • テーブル(カラムファミリー)のJOIN
  • ACIDトランザクション

実行環境

Cassandra DBは、Javaで記述されています。実行にはJavaランタイムが必要となります。
また、CQLを実行するcqlshはPythonで記述され、実行にはPythonランタイムが必要となります。

CassandraとJava/Pythonのバージョン関係

Cassandraのバージョンと、動作に必要となるJava/Pythonの実行環境(ランタイム)バージョンを次に示します。

Cassandra Java Python
Cassandra 5 Java 11または17 Python 3.8~3.11

使用するポート番号

Cassandraは、次のポート番号を使用します。

ポート番号 内容
9042 CQLクライアント接続用
7000 ノード間の通信用
7001 ノード間の通信用(SSL)
7199 JMX監視用

Cassandraの実行方式

OS上で直接Cassandraプロセスを実行する方法と、Dockerコンテナとして実行する方法があります。

JavaのバージョンとPythonのバージョンに制約があるので、OS上で直接Cassandraを実行する場合構成が難しい場合があります。
例えば、Debian 13でCassandra 5.0を稼働させようとした場合、Debian 13の公式リポジトリから入手できるJavaはOpenJDK 21または25であり、Cassandra 5.0が要求する11または17を別途インストールする必要があります。さらに厄介なのはDebian 13のPythonは3.13なのでcqlshの動作に必要な 3.8~3.11を満たせず、Linux系OSにおいてOS標準のPythonバージョン以外を入れるのに苦労します。

Dockerコンテナとして実行する場合、OSのカーネル以外はDockerコンテナ側で用意するのでDebianのいずれかのバージョンでも他のLinuxディストリビューションでも動作が容易です。
データベースをDockerコンテナで実行する場合、データを永続化する必要があるので、バインドマウントまたはVolumeのどちらかでホスト上のストレージにコンテナ側の/var/lib/cassandraをマウントします。

最初のデータベース構築

Cassandra DBにテーブルを作成しデータの格納、取り出しをします。
例題として、衛星軌道情報(TLE: Two Line Element)を扱います。TLEについては、衛星軌道を参照。

キースペースの作成

キースペースはアプリケーションごとに1つ設けるのが一般的です1 。
命名は、英数字とアンダースコアで48文字以内とします。Cassandraはデフォルトでは大文字小文字の区別をしません。

$ docker exec -it cassandra1 cqlsh
cqlsh> CREATE KEYSPACE satellite WITH replication={'class': 'SimpleStrategy', 'replication_factor': 3};
cqlsh> DESCRIBE keyspaces;
satellite  system_auth         system_schema  system_views
system     system_distributed  system_traces  system_virtual_schema

1 https://cassandra.apache.org/doc/stable/cassandra/developing/cql/ddl.html

テーブルの作成

TLEを格納するパーティションを収容するテーブルを定義します。
テーブルは、アプリケーションのクエリモデル(データの利用方法)を設け、それを基に設計します。
アプリケーションから1回の検索対象データが同一パーティションに格納されるようにします。
今回のサンプルでは、次の検索を想定して設計します。

  • 衛星ID(カタログ番号)を指定し、その衛星の最新のTLEを取得する
  • 衛星ID(カタログ番号)を指定し、エポック日時(逆順)で並んだTLEを取得する
  • 衛星ID(カタログ番号)とTLEのデータソースを指定し、指定したデータソースからのTLEを取得する

1つのクエリで同時に異なる衛星IDのTLEを取得することはないので、衛星IDをパーティションキーとします。
衛星IDだけではパーティション内でTLEを特定することができないので、一意に特定するため衛星ID、エポック、データソースを合わせてプライマリーキーとします。

次に、エポックの日時で降順に並べるため、エポックをクラスタリングキーとします。
また、データソースによって取捨選択をするため、データソースをクラスタリングキーとします。

TLEは、入手したTLE情報(衛星名と2行要素の1行目、2行目)でデータベースに格納します。テーブル構造は次となります。

tle_history
satellite_id text K
epoch timestamp C ↓
source text C ↑
name text
line1 text
line2 text
cqlsh> CREATE TABLE satellite.tle_history (
   ... satellite_id text,
   ... epoch timestamp,
   ... source text,
   ... name text,
   ... line1 text,
   ... line2 text,
   ... PRIMARY KEY((satellite_id), epoch, source)
   ... )
   ... WITH CLUSTERING ORDER BY (epoch DESC, source ASC);
sqlsh> 
TLEのデータ例

国際宇宙ステーション(ISS)のTLEの例です。

ISS (ZARYA)             
1 25544U 98067A   26284.82161096  .00007651  00000+0  14771-3 0  9996
2 25544  51.6314  82.9019 0006707 250.7442 109.2821 15.48831270589830

TLEは2行軌道要素で、0行目、1行目、2行目の3行で構成しています。0行目は衛星名が記載されていますが必須ではありません。データとして意味を持つのは1行目、2行目です。

その他の検索

今回は衛星に着目してその衛星のTLEを時間軸・データソースに応じて取得するアプリケーションの利用方法を基に設計しています。
他のアプリケーションとして、あるエポック近傍の日時におけるすべての衛星のTLEを取得するということがあります。ある時点で地球周回しているすべての衛星の位置を扱うアプリケーションなどです。このアプリケーションでは、パーティションを衛星IDではなく、ある時間範囲とするのが適切です。

クラスタ

Cassandra DBは分散データベースなので、複数計算機(ノード)をクラスタに束ねて1つの論理的なデータベースとして稼働します。

トポロジ

クラスタの構成では、仮想的なネットワークトポロジとしてデータセンターとラックの2階層のグルーピングがあります。

データセンター

ネットワーク上の1つの拠点をデータセンターとし、データセンター内は高速なローカルエリアネットワーク(LAN)で結ばれ、データセンター間はやや低速・遅延の大きな広域ネットワーク(WAN)で結ばれているモデルをデータセンターと定義しています。

データセンター災害時復旧(Disaster Recovery)を想定し、1つのデータセンターで完結するデータが別データセンターでも保持されます。

Cassandra DBでは、クラスタ構成において複数のデータセンターを定義することができます。
データセンター毎に、シードノードを最低1つ定義します。

ラック

同一データセンター内において、同一ラック内に配置されるノードか別ラックに配置されるノードかによってラックのグループを定義します。ラックのグループはレプリカノードの選出に影響を及ぼし、同一データのノードは極力別ラックのノードに割り当てられます。これは、同一ラックのノードの場合、ラックの電源故障・空調故障・ネットワーク機器故障などで同時に障害になる可能性があるためです。

クラスタ構成の確認

nodetool

使い方
nodetool <サブコマンド>

サブコマンド
  • status
    クラスタを構成する各ノードの状態を確認
  • ring
    トークンが度のノードに割り当てられているかを一覧
  • describecluster
    クラスタ情報(クラスタ名、Snitch、Partitioner、ノード、データセンター、Kyespace)を確認

稼働環境

計算機構成

Cassandra DBは、マスターのない分散データベースとなっており、Cassandra DBが稼働する計算機を複数でクラスタ構成し、水平方向へのスケールを実現しています。

公式ドキュメントからハードウェア要件に関する資料
https://cassandra.apache.org/doc/stable/cassandra/managing/operating/hardware.html

ストレージ

HA(High Availability)などの冗長構成や複数の仮想サーバーを構成する場合は、SAN(Storage Area Network)やNAS(Network Attached Storage)を使用してHAや仮想サーバーのデータ引継ぎ、マイグレーションを可能とすることが一般的です。しかしCassandra DBは計算機群にデータを分散して配置しデータの冗長性を持たせ、性能を上げるアーキテクチャですので、ストレージを集約してしまうことはアーキテクチャにそぐわない結果となります。
Cassandra DBに向いたストレージは、クラスタを構成する計算機のローカルストレージを使用し、計算機間を高速なネットワークで結ぶ方式となります。

また、データの冗長性は、RF(Replication Factor)で指定した数のノードに複製することで確保しているので、それぞれのノードのストレージをRAID構成することは性能向上のRAID0(ストライピング)を除き不要となります。

ストレージデバイスとしては、読み書きの高速化が可能なSSD(Solid State Drive)を採用し、インタフェースも高速なNVMe(Non-Volatile Memory Express)が望ましいです。

CPU

Cassandra DBはスレッドを複数用いた並列化による性能向上を行っているので、ある程度の数のCPU(コア)を搭載することが望ましく、8コア以上が推奨です。

メモリ

Cassandra DBはJavaで書かれておりJavaVM上で実行されます。Javaは割とメモリを喰いますので、各ノードは16GB、できれば32GBのメモリを搭載することが望ましいです。

ネットワーク

クラスタ間でのデータ転送を向上させるため、なるべく高速なLAN(10Gpbs)が望ましく、少なくとも2.5Gbpsが欲しいところです。

監視

ノードの監視

クラスタ構成ノード一覧の監視

  • nodetool status
    全ノードの稼働状況が把握できます。各ノードのLoad列の値を見てクラスタのバランス状況が判断できます。

ノードの稼働状況の監視

  • nodetool tpstats
    そのノードのメッセージ喪失量が分かります。blocked flushが増大するとデータのメモリ書き込み量がディスクへのフラッシュを超えていることが分かります。この問題は長続きするので抜本的対策が必要です。
  • ログファイルのERROR/WARNを確認

参考文献

公式ドキュメント

Cassandra Documentation

書籍

Cassandra: The Definitive Guide, (Revised) Third Edition
Jeff Carpenter, Eben Hewitt著、O'Reilly刊、2022、430ページ
Cassandra 4.0対応、概要、CQL、データモデリング、アーキテクチャ、アプリケーション(データアクセス)設計、ドライバー、データのread/writeパス、構成・展開、監視、保守、性能、セキュリティとほぼ全容を網羅しています。


約23時間前に更新