インデックスと性能
インデックスの仕組み、自動管理、クエリー最適化、性能調整を説明します。
概要
時系列の負荷に合わせ、テーブル型ごとに方式を使い分けます。
| 型 | インデックス | 管理 | 用途 |
|---|---|---|---|
| Tag | 3 階層パーティション | 自動 | sensor_id と時刻 |
| Log | LSM(任意) | 手動 | 列の高速検索 |
| Volatile | RED-BLACK ツリー | 主キーに自動 | 主キー検索 |
| Lookup | RED-BLACK(追加は任意) | 主キーに自動、追加は手動 | 列の高速検索 |
多くの場合、自動作成される構造を利用でき、手動追加は不要です。
Tag のインデックス
自動の 3 階層構造
次の構造を自動作成します。
第 1 層:タグ名
- 特定センサーを高速特定
- sensor_id を O(log n) で検索
第 2 層:時刻による分割
- 時刻範囲で分割
- 無関係な期間を読み飛ばす
第 3 層:値(SUMMARIZED 列)
- パーティション単位の値インデックス
- 高速な範囲検索
動作
CREATE TAG TABLE sensors (
sensor_id VARCHAR(20) PRIMARY KEY,
time DATETIME BASETIME,
temperature DOUBLE SUMMARIZED
) WITH ROLLUP;
-- 内部で作成される構造:
-- 1.sensor_id(タグ名)のインデックス
-- 2.時刻によるパーティション
-- 3.SUMMARIZED 列の集計とインデックスクエリー最適化
最適な検索(3 層を使用):
-- 高速:sensor_id、時刻パーティション、値インデックスを使用
SELECT * FROM sensors
WHERE sensor_id = 'sensor01'
AND time BETWEEN '2025-10-10 00:00:00' AND '2025-10-10 23:59:59'
AND temperature > 25.0;良い検索(2 層を使用):
-- sensor_id と時刻パーティションを使用
SELECT * FROM sensors
WHERE sensor_id = 'sensor01'
AND time BETWEEN TO_DATE('2025-10-10 14:00:00', 'YYYY-MM-DD HH24:MI:SS')
AND TO_DATE('2025-10-10 15:00:00', 'YYYY-MM-DD HH24:MI:SS');遅い検索(全表スキャン):
-- 全センサーを検索(sensor_id 条件なし)
SELECT * FROM sensors
WHERE temperature > 30.0;ロールアップのインデックス
ロールアップを作成すると、インデックスも利用できます。
-- ロールアップを検索(事前集計済みで高速)
SELECT rollup('hour', 1, time) AS hour_time, AVG(temperature), COUNT(temperature)
FROM sensors
WHERE sensor_id = 'sensor01'
GROUP BY hour_time;
-- 主な集計単位:秒、分、時間推奨事項
推奨:
- WHERE に sensor_id を含める
- Log には
DURATION、Tag には BASETIME 範囲を指定 - 統計にはロールアップを使用
- 基本インデックスの管理はサーバーに任せる
避ける操作:
- 自動管理される基本インデックスを手動で作成しようとする
- sensor_id なしで全体をスキャン
- ロールアップで十分なのに元データを集計
Log のインデックス
任意の LSM
必要に応じて LSM(Log-Structured Merge)を作成できます。
-- Log テーブルを作成
CREATE TABLE app_logs (
level VARCHAR(10),
component VARCHAR(50),
message VARCHAR(2000)
);
-- 頻用する検索列に LSM を作成
CREATE INDEX idx_level ON app_logs(level);
CREATE INDEX idx_component ON app_logs(component);作成する場合
追加を検討する条件:
- WHERE に頻繁に使う列
- 検索が遅い
- カーディナリティーが中程度
省略を検討する条件:
- 時刻だけの検索
- カーディナリティーが非常に高い
- 書き込み性能を最優先
LSM の特性
利点:
- 書き込み中心の負荷に最適化
- 書き込みをブロックしない
- 自動的に保守
動作:
- メモリバッファーに入力
- ディスクセグメントへ定期フラッシュ
- バックグラウンドでマージ
- 複数セグメントを横断して検索
インデックスの構築
-- インデックスの状態を確認
SHOW INDEXES;
-- インデックス構築の進捗を確認
SHOW INDEXGAP;
-- バックグラウンドで構築(ブロックしない)検索の最適化
インデックスを使用:
-- 高速:idx_level を使用
SELECT * FROM app_logs
WHERE level = 'ERROR'
DURATION 1 HOUR;キーワードインデックスが必要:
-- SEARCH には KEYWORD インデックスが必要
CREATE INDEX idx_message ON app_logs(message) INDEX_TYPE KEYWORD;
SELECT * FROM app_logs
WHERE message SEARCH 'timeout'
DURATION 1 HOUR;Volatile のインデックス
自動の RED-BLACK ツリー
PRIMARY KEY にメモリ内インデックスを自動作成します。
CREATE VOLATILE TABLE device_status (
device_id INTEGER PRIMARY KEY, -- 自動的にインデックスを作成
status VARCHAR(20),
last_updated DATETIME
);性能特性
- 検索:主キーで O(log n)
- 挿入:O(log n)
- 更新:O(log n)
- 削除:O(log n)
すべてメモリ内で処理します。
検索の最適化
高速:
-- PRIMARY KEY インデックスを使用
SELECT * FROM device_status WHERE device_id = 101;
UPDATE device_status SET status = 'RUNNING' WHERE device_id = 101;
DELETE FROM device_status WHERE device_id = 101;比較的低速:
-- 全表スキャン(status にインデックスなし)
SELECT * FROM device_status WHERE status = 'ERROR';Lookup のインデックス
RED-BLACK インデックス
CREATE LOOKUP TABLE devices (
device_id INTEGER PRIMARY KEY,
device_name VARCHAR(100),
location VARCHAR(200)
);
-- 頻用する検索列にインデックスを作成
CREATE INDEX idx_location ON devices(location);主キーには自動で作成されます。検索列への追加インデックスは、Log と同様に検索頻度を基に検討します。
時刻による分割
自動分割
Tag と Log は時刻を考慮した保存方式で範囲検索を効率化します。Lookup は 永続的な参照テーブルで、主キーと任意の追加インデックスで最適化します。
パーティション構成の模式図:
┌──────────────────────────────────────┐
│ 分割 1:第 1 週(10 月 1~7 日) │
│ - この週のデータ │
│ - 個別のインデックス │
│ - 最適化した圧縮 │
├──────────────────────────────────────┤
│ 分割 2:第 2 週(10 月 8~14 日) │
│ - この週のデータ │
│ - 個別のインデックス │
│ - 最適化した圧縮 │
├──────────────────────────────────────┤
│ 分割 3:第 3 週(10 月 15~21 日) │
│ - 入力中の分割 │
│ - 入力用に圧縮を抑制 │
└──────────────────────────────────────┘利点
検索性能:
- 関連するパーティションだけをスキャン
- 範囲外の過去や未来の分割を除外
- 並列スキャン
データ管理:
- 古いパーティションの削除で保持期間を管理
- 分割ごとの圧縮
- 効率的なバックアップと復元
検索の最適化
良い例(1 パーティション):
SELECT * FROM logs DURATION 1 DAY;比較的遅い例(複数パーティション):
SELECT * FROM logs DURATION 30 DAY;非常に遅い例(全パーティション):
SELECT * FROM logs; -- 時刻条件なし最適化の方針
1.時刻条件を使用
避ける例:
SELECT * FROM sensors WHERE sensor_id = 'sensor01';推奨:
SELECT * FROM sensors
WHERE sensor_id = 'sensor01'
AND time BETWEEN TO_DATE('2025-10-10 14:00:00', 'YYYY-MM-DD HH24:MI:SS')
AND TO_DATE('2025-10-10 15:00:00', 'YYYY-MM-DD HH24:MI:SS');2.DURATION を使用
推奨(専用構文):
SELECT * FROM logs DURATION 1 HOUR;比較的効率が低い例(手動条件):
SELECT * FROM logs
WHERE _arrival_time BETWEEN TO_DATE('2025-10-10 14:00:00', 'YYYY-MM-DD HH24:MI:SS')
AND TO_DATE('2025-10-10 15:00:00', 'YYYY-MM-DD HH24:MI:SS');3.分析にはロールアップを使用
以下の例は時間条件と集計単位が異なります。性能を比較する場合は、同じ条件・同じ集計単位にそろえてください。
推奨(一致するロールアップがある場合):
SELECT * FROM sensors
WHERE sensor_id = 'sensor01'
AND time BETWEEN TO_DATE('2025-10-01', 'YYYY-MM-DD')
AND TO_DATE('2025-10-08', 'YYYY-MM-DD');
SELECT rollup('hour', 1, time) AS hour_time, AVG(temperature)
FROM sensors
WHERE sensor_id = 'sensor01'
GROUP BY hour_time;遅い例(数百万行):
SELECT sensor_id, AVG(temperature)
FROM sensors
WHERE sensor_id = 'sensor01'
AND time BETWEEN TO_DATE('2025-10-01', 'YYYY-MM-DD')
AND TO_DATE('2025-10-08', 'YYYY-MM-DD')
GROUP BY sensor_id;4.結果セットを制限
推奨:
SELECT * FROM logs DURATION 1 HOUR LIMIT 1000;避ける例:
SELECT * FROM logs; -- 数百万行を返してしまう5.絞り込みに有効な列にインデックス
良い例(中程度のカーディナリティー):
-- level:ERROR、WARN、INFO(低カーディナリティーで適する)
CREATE INDEX idx_level ON logs(level);避ける例(非常に高いカーディナリティー):
-- message:数百万の異なる値(インデックスを避ける)
CREATE INDEX idx_message ON logs(message); -- この使い方は避ける圧縮
自動圧縮
次の圧縮を自動適用します。
論理圧縮(列指向):
- 列ごとに圧縮
- パターンに基づく圧縮
- 10~100 倍の圧縮率
物理圧縮(ブロック):
- ディスクブロックを圧縮
- ユーザーからは透過的
- 追加で 2~5 倍の圧縮
圧縮特性
| 型 | 方法 | 一般的な圧縮率 |
|---|---|---|
| Tag | 列とブロック | 50~100 倍 |
| Log | 列とブロック | 10~50 倍 |
| Volatile | なし(メモリ内) | 1 倍 |
| Lookup | ブロック | 2~5 倍 |
性能への影響
読み取り:
- I/O が減り、高速化
- 展開の負荷は小さい
- 大規模スキャンで効果
書き込み:
- 先にメモリに蓄積
- フラッシュ時に圧縮
- 入力時の圧縮待ちを回避
性能の監視
テーブル統計の確認
-- テーブル情報を表示
SHOW TABLE sensors;
-- ストレージ使用量を表示
SHOW STORAGE;
-- テーブルスペース情報を表示
SHOW TABLESPACES;クエリーの監視
-- 実行中のクエリーを表示
SHOW STATEMENTS;
-- 遅いクエリーを確認
-- 長時間実行中のクエリーを確認できるインデックスの状態
-- インデックスを確認
SHOW INDEXES;
-- インデックス構築の進捗を確認
SHOW INDEXGAP;性能調整
サーバー設定
machbase.conf の主なパラメーター:
# メモリ設定
PROCESS_MAX_SIZE = 8G # プロセスのメモリ上限
VOLATILE_TABLESPACE_MEMORY_MAX_SIZE = 1G # Volatile のメモリ
DISK_COLUMNAR_TABLESPACE_MEMORY_MAX_SIZE = 2G # ディスクテーブルのメモリ
# 書き込み性能
DISK_COLUMNAR_TABLE_CHECKPOINT_INTERVAL_SEC = 600
DISK_COLUMNAR_INDEX_CHECKPOINT_INTERVAL_SEC = 600
# 検索性能
MAX_QPX_MEM = 512M # クエリーごとのメモリ上限
SESSION_QUERY_TIMEOUT_SEC = 60 # クエリータイムアウト。0 は無効アプリケーションの最適化
一括書き込みの概略(引数を省略した疑似コード):
// 一括入力には APPEND を使用
SQLAppendOpen(stmt, "sensors");
for (int i = 0; i < 10000; i++) {
SQLAppendDataV(stmt, sensor_id, time, value);
}
SQLAppendClose(stmt); // バッチを反映
コネクションプール:
- 接続を再利用
- 接続のオーバーヘッドを削減
- 一般的には 10~20 接続
結果件数の制限:
-- UI の検索では結果件数を制限
SELECT * FROM logs DURATION 1 HOUR LIMIT 100;よくある性能問題
1.時刻条件がなく遅い
問題:
SELECT * FROM sensors WHERE sensor_id = 'sensor01';
-- 低速:全パーティションを検索対処:
SELECT * FROM sensors
WHERE sensor_id = 'sensor01'
AND time BETWEEN TO_DATE('2025-10-10 14:00:00', 'YYYY-MM-DD HH24:MI:SS')
AND TO_DATE('2025-10-10 15:00:00', 'YYYY-MM-DD HH24:MI:SS');2.分析で元データを集計
元データの例は期間全体の平均、ロールアップの例は時間単位の平均です。必要な集計単位に合わせて使用してください。
問題:
SELECT AVG(temperature)
FROM sensors
WHERE time BETWEEN TO_DATE('2025-10-01', 'YYYY-MM-DD')
AND TO_DATE('2025-10-08', 'YYYY-MM-DD');
-- 低速:数百万行を集計対処:
SELECT rollup('hour', 1, time) AS hour_time, AVG(temperature)
FROM sensors
WHERE time BETWEEN TO_DATE('2025-10-01', 'YYYY-MM-DD')
AND TO_DATE('2025-10-08', 'YYYY-MM-DD')
GROUP BY hour_time; -- 一致するロールアップがあれば高速3.Log のインデックス不足
問題:
-- インデックスなしでは低速
SELECT * FROM logs WHERE level = 'ERROR' DURATION 1 DAY;対処:
CREATE INDEX idx_level ON logs(level);
-- インデックスにより高速化4.大きな結果セット
問題:
SELECT * FROM logs DURATION 30 DAY;
-- 数百万行を返してしまう対処:
-- 代わりに集計する
SELECT level, COUNT(*) FROM logs
DURATION 30 DAY
GROUP BY level;
-- または結果件数を制限
SELECT * FROM logs DURATION 30 DAY LIMIT 1000;推奨事項の一覧
DURATIONまたは時刻範囲を指定- Tag の分析にはロールアップを使用
- Log/Lookup の頻用する検索列にインデックスを作成
- LIMIT で結果を制限
- APPEND で一括入力
- Tag/Volatile の基本インデックスはサーバー管理に任せる
- SHOW STATEMENTS で検索性能を監視
- 古いデータを削除し、保持期間を管理
次のステップ
- SELECT:最適化の適用
- テーブルの種類:詳細
- トラブルシューティング:性能問題
要点
- Tag は 3 階層の自動パーティションインデックス
- Log には LSM、Lookup には RED-BLACK の追加インデックス
- Volatile は自動のメモリ内インデックス
- 時刻による分割は自動で行われる
- 時刻条件を指定して性能を改善
- 分析には事前集計を使用
- 多くの基本インデックスは自動管理
仕組みを理解し、実際の検索性能を確認しながら調整してください。