午前2時の救世主:PostgreSQLとMySQLがIoTデータに「降参」した時
午前2時に鳴り響く電話のバイブレーションは、すべての運用エンジニアにとって悪夢です。Grafanaのダッシュボードは真っ白になり、サーバーのCPUは100%に達し、IOPSは限界を突破. 監視システムは完全に麻痺してしまいます。
私はかつて、工場のセンサー監視プロジェクトでこの苦い経験をしました。当初、チームは使い慣れているという理由でPostgreSQLを選びました。しかし、規模が5,000センサーに拡大し、毎秒10万件以上のレコードが送られてくるようになると、PostgreSQLは息切れし始めました。インデックスは数百GBに膨れ上がり、INSERT文はボトルネックとなり、期間指定(time-range)のクエリはレスポンスに1分以上かかるようになりました。
実際、MySQLやMongoDBは一般的な業務には素晴らしいツールです。しかし、毎秒数百万行の書き込み速度が求められるタイムシリーズ(時系列)データに関しては、QuestDBの敵ではありません。QuestDBは極限まで最適化されています。カラムナ(列指向)ストレージ、CPUのSIMD命令セットの活用、そして純粋なSQLのサポートがその特徴です。
クイックスタート:5分でQuestDBを起動する
煩雑な設定手順に時間を費やす必要はありません。最も速い導入方法はDockerを使用することです。QuestDBは非常に軽量で、大量の依存ライブラリを引き連れることもありません。
docker run -p 9000:9000 -p 8812:8812 -p 9009:9009 -p 9003:9003 \
questdb/questdb
注意すべきポートは以下の通りです:
- 9000: Web Console。
localhost:9000にアクセスして、直感的なインターフェースでデータベースを管理できます。 - 8812: Postgres Wire Protocol。このポートを使用すると、PostgreSQLのライブラリをそのまま使用して接続できます。
- 9009: InfluxDB Line Protocol。これはデータを最大速度で投入するための「高速道路」です。
なぜ QuestDB は理不尽なほど速いのか?
違いはストレージの仕組みにあります。MySQLのような従来のデータベースは行ベース(row-based)でデータを保存します。10億件のレコードから平均気温を算出する場合、Postgresはsensor_idやlocationといった無関係な列まで全てスキャンしなければなりません。これはリソースの甚大な浪費です。
QuestDBは異なるアプローチを取ります。それはカラムナ(列指向)保存です。気温を計算する必要があるとき、システムはディスク上のその特定の列だけを読み取ります。時間によるパーティショニング(Time-partitioning)メカニズムと組み合わせることで、QuestDBは不要なデータ領域を完全に排除します。
1億行のデータを用いた実地テストでは、QuestDBでのAVG計算はわずか数百ミリ秒で完了しました。これは、複雑なチューニングなしでPostgreSQLよりも15倍高速な数値です。
データの投入:InfluxDB Line Protocol (ILP) を優先する
QuestDBはSQLのINSERTをサポートしていますが、それはIoTにとって最適な方法ではありません。最高のパフォーマンスを得るには、InfluxDB Line Protocol (ILP)を使用すべきです。このプロトコルは、定期的にディスクにフラッシュする前に、データを直接RAMに投入します。
以下のPythonコードは、ソケットを介してセンサーデータを送信するシミュレーションです:
import socket
import time
HOST, PORT = 'localhost', 9009
def send_sensor_data():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
while True:
# 構造: table_name,tags fields timestamp
data = f"sensors,sensor_id=S001 temperature=25.5,humidity=60.2 {time.time_ns()}\n"
s.sendall(data.encode())
time.sleep(0.01) # 毎秒100件のレコードを送信
send_sensor_data()
大きなメリットは、QuestDBがスキーマレス(Schema-less)をサポートしている点です。最初にデータを受信した瞬間に自動的にテーブルを作成し、新しい列を追加するため、DB設計の時間を節約できます。
拡張SQLによるインテリジェントなクエリ
QuestDBはFluxのような新しい言語を学ぶ必要はありません。標準的なSQLを使用しながら、タイムシリーズ(時系列)データに「真価を発揮する」キーワードを追加しています。
1. SAMPLE BY:超高速な時間グルーピング
GROUP BYでの複雑な日時処理関数は忘れてください。QuestDBなら、たった1行のコマンドで済みます。
SELECT timestamp, avg(temperature)
FROM sensors
SAMPLE BY 1h; -- 1時間ごとの平均を自動的にグルーピング
2. LATEST BY:最新の状態を即座に取得
全センサーの現在の状態を知るために、従来のSQLでは非常に重いJOINやMAX(timestamp)が必要でした。QuestDBはこれを一瞬で処理します。
SELECT * FROM sensors LATEST BY sensor_id;
3. ASOF JOIN:ズレのあるデータの結合
これは金融やIoTにおいて非常に強力な機能です。pricesテーブルとtradesテーブルがミリ秒単位で正確に一致しない場合でも、ASOF JOINは自動的に直前の最も近い値を取得してマッチングさせます。
実地運用の教訓
強力なQuestDBですが、システムハングを避けるために守るべき独自のルールがあります:
- パーティショニングの使用は必須: テーブル作成時には必ず
PARTITION BY DAYまたはMONTHを宣言してください。これにより、古いデータの削除(保持期間管理)がテーブルロックを発生させずに即座に行えます。 - 順序外(Out-of-order)データの管理: QuestDBはデータが時系列順に送られてくる時に最高のパフォーマンスを発揮します。データの遅延(ラグ)が大きい場合は、バッファを最適化するために
cairo.max.uncommitted.rowsパラメータを調整してください。 - ハードウェア: SSDまたはNVMeへの投資を惜しまないでください。QuestDBはシーケンシャルな書き込みを多用するため、ディスク速度がスループットを決定する鍵となります。
- SELECT * の制限: 数十億行のテーブルに対して、Web Consoleで
LIMITを付けずにSELECT *を実行すると、表示データの過負荷によりブラウザがフリーズします。
まとめ
QuestDBは、厳格なACID特性が必要な金融取引などの用途でPostgreSQLを完全に置き換えるために作られたわけではありません。しかし、インフラ監視や数百万台のIoTデバイスのトラッキングに苦労しているなら、これが救世主となります。 インデックスの最適化に夜を徹する代わりに、その重荷をQuestDBに任せて、ぐっすり眠りましょう。

