実録:Cassandraが大量のトラフィックで「息切れ」した話
AdTechプロジェクトに携わっていた頃、チームは非常に厳しい課題に直面していました。リアルタイムで押し寄せるログやユーザーイベントの量は、秒間20万リクエストを超えていたのです。Cassandraは水平スケーラビリティに優れているため,当時は第一の選択肢でした。しかし、トラフィックがピークに達したとき、現実はそれほど甘くありませんでした。
ダッシュボードは真っ赤に染まり、Cassandraノードが時折数秒間「固まる」ことで、アプリケーション側ではタイムアウトエラーが頻発しました。不思議なことにCPU使用率は50%を超えたことがなかったのですが、P99レイテンシ(遅延)は数秒単位まで跳ね上がっていました。「酸素吸入」が必要なほど瀕死の状態にあるデータベースクラスターを救出するために徹夜したことがある人なら、この無力感がわかるはずです。
根本的な原因:ガベージコレクションという名の邪魔者
詳細にデバッグした結果、ボトルネックはJVM(Java仮想マシン)そのものにあることに気づきました。CassandraはJava上で動作するため、**ガベージコレクション(GC)**との共存が避けられません。書き込み負荷が高い(Write-heavy)システムでは、JVMは頻繁にメモリのクリーンアップを行う必要があります。
GCによる「Stop-the-world」が発生すると、すべての読み書き操作が完全にフリーズしてしまいます。苦い現実は、Javaのメモリ管理タスクを実行するためだけに、サーバー性能の約30%を浪費していることが少なくないということです。ヒープサイズを32GBに設定したり、ZGCに変更したりしても、それは一時しのぎに過ぎませんでした。JVMの影が、ハードウェア性能を最大限に引き出すことを阻んでいたのです。
一般的な3つの解決策(そして、なぜそれらが失敗するのか)
チームは最良の解決策を見つける前に、あらゆる方法を試しました:
- GCのチューニング: パラメータの微調整に1週間費やしましたが、効果はわずかでした。非常に複雑で、副作用も発生しやすいです。
- ハードウェアのアップグレード: 大容量RAMや最高級のNVMeドライブを採用しました。これは「金で解決する」手法ですが、コストがかさむ割にCPUコアあたりの効率は改善されませんでした。
- マネージドサービスの利用: AWS DynamoDBへの移行を検討しました。便利ではありますが、書き込みコストが高すぎて、月末の請求書を見た上司が卒倒しそうになりました。
ScyllaDB:強力なShard-per-coreアーキテクチャ
ScyllaDBは、CassandraのAPIを維持しつつ、C++で完全に書き直された完璧なアップグレード版として登場しました。最大の強みは**Shard-per-core**アーキテクチャです。スレッド間でリソースを競合させる代わりに、ScyllaDBは各CPUコアにデータの一部を個別に割り当てて管理します。競合なし。ロックなし(Lockless)。そして最も重要なのは、GCがもう存在しないことです。
DockerによるScyllaDBの迅速なデプロイ
試してみたい場合は、Dockerを使うのが一番早いです。30秒でScyllaDBクラスターを構築できます。私はよく、移行を決定する前にこのような小規模なラボ環境を構築してベンチマークを測定します。
# ScyllaDBのシングルインスタンスを実行
docker run --name scylla-node -d scylladb/scylla
# ノードの状態を確認(準備ができるまで10秒ほど待ちます)
docker exec -it scylla-node nodetool status
ステータスが UN (Up/Normal) になれば、おなじみの cqlsh コマンドでデータベースにアクセスできます。
docker exec -it scylla-node cqlsh
CQLによる最適なデータテーブル設計
ScyllaDBの構文(CQL)はSQLとほぼ同じですが、1ms以下の速度を実現するにはNoSQL的な思考が必要です。
-- キースペース(Keyspace)を作成
CREATE KEYSPACE itfromzero
WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1};
USE itfromzero;
-- 最新のクエリに最適化されたログ保存テーブル
CREATE TABLE user_logs (
user_id uuid,
action_time timestamp,
action_name text,
PRIMARY KEY (user_id, action_time)
) WITH CLUSTERING ORDER BY (action_time DESC);
ヒント:user_id(パーティションキー)によって、ScyllaDBはどのコアにデータがあるかを判断します。このキーを均等に分散させる設計にすることで、システムは非常に高い負荷に耐えられるようになります。
大規模データ処理における実践的な経験
数百万行のCSVファイルでパフォーマンスをテストする場合、手作業で行うことはありません。ちょっとしたコツとして、toolcraft.app/ja/tools/data/csv-to-json にある変換ツールを使用して、サンプルファイルのフォーマットを素早く標準化します。その後、Pythonスクリプトを使用して驚異的なスピードでデータを流し込みます。
import uuid
from datetime import datetime
from cassandra.cluster import Cluster
# ローカルノードに接続
cluster = Cluster(['127.0.0.1'])
session = cluster.connect('itfromzero')
# サンプルレコードを1件挿入
query = "INSERT INTO user_logs (user_id, action_time, action_name) VALUES (%s, %s, %s)"
session.execute(query, (uuid.uuid4(), datetime.now(), 'USER_LOGIN'))
Lời kết
ScyllaDBは、まさにスピードの「怪物」です。Cassandraの遅さやDynamoDBのコストに頭を悩ませているなら、ぜひScyllaDBへの移行を検討してみてください。1ドルあたりのパフォーマンスの差に驚くはずです。ただし、多くのコアを搭載したCPUと高速なストレージを備えた環境でこそ、その真価を発揮することを忘れないでください。

