TiDB HTAPリアルタイム分析実践:TiFlashからリアルタイムダッシュボードまで6つのプロダクションパターン
T+1分析は終わった、リアルタイムインサイトこそ未来
午前3時、オンライン注文が10倍に急増、CEOが聞く:「今、各カテゴリのリアルタイムGMVは?」——あなたは答えるしかない:「データはまだETLパイプラインの中、明日にならないと見られません。」
これが従来のT+1分析のジレンマ:
- ETLパイプラインの脆弱性:Kafka → Flink → ClickHouse、どの环节が故障してもデータが途絶
- データ遅延の制御不能:OLTPからOLAPまで、分〜時間レベルの遅延が常態
- デュアルシステムの運用悪夢:MySQLがトランザクション、ClickHouseが分析、データ整合性は手動対応
- コストの暴走:2つのストレージ、2つのコンピューティング、2つの運用、リソース利用率は低い
- リアルタイム性の不足:ビジネス判断には秒単位の応答が必要、T+1レポートは過去の遺物
TiDB HTAPの約束:1つのシステムでOLTPとOLAPを同時に処理、行ストレージから列ストレージへのデータ同期遅延ゼロ。これはPPT上のアーキテクチャではなく、数千の企業でプロダクション検証済みのソリューション。
HTAPコア概念クイックリファレンス
| 概念 | フルネーム | コアの役割 | 例え |
|---|---|---|---|
| HTAP | Hybrid Transactional/Analytical Processing | 1つのシステムでトランザクションと分析を同時処理 | 1人がトレーダーとアナリストを兼務 |
| TiFlash | TiDB Flash | 列ストレージエンジン、OLAPクエリを処理 | OLAP専用アクセラレータ |
| TiKV | TiDB Key-Value | 行ストレージエンジン、OLTPトランザクションを処理 | OLTPの礎 |
| MPP | Massively Parallel Processing | マルチノード並列計算フレームワーク | 複数人が異なるシャードを同時計算 |
| 列ストレージ | Columnar Storage | 列単位でデータを格納、分析クエリは必要な列のみ読み取り | 必要なページだけめくる |
| Raft Learner | Raft合意プロトコル学習者ロール | TiFlashへ非同期レプリケーション、メインフローに影響なし | 影の書記 |
| データマート | Data Mart | 特定ビジネスドメイン向けの事前集計データセット | 各部署向けのカスタムレポートショーケース |
💡 JSONフォーマッターを使ってTiDB設定JSONファイルを整形し、トラブルシューティングを容易に。
従来のリアルタイム分析アーキテクチャの5つのペインポイント
ペインポイント1:ETLパイプラインが最大の単一障害点
MySQL → Canal → Kafka → Flink → ClickHouse
│ │ │ │ │
│ │ │ │ └─ 書き込み失敗、データロス
│ │ │ └─ ジョブクラッシュ、データ蓄積
│ │ └─ パーティション不均、消費遅延
│ └─ Binlog解析エラー
└─ レプリケーションラグ
一つでも問題が起きるとパイプライン全体が断絶。深夜のアラートは止まず、運用チームは疲弊。
ペインポイント2:データ整合性の保証不可
OLTPとOLAPは独立した2つのシステム、データ同期には遅延ウィンドウが存在:
-- MySQL: 注文が返金された
UPDATE orders SET status = 'refunded' WHERE id = 10086;
-- ClickHouse: 5分後に返金ステータスが反映
-- この5分間、レポートデータは誤り
ペインポイント3:デュアルシステムの運用コスト倍増
| 運用項目 | MySQL | ClickHouse | 合計 |
|---|---|---|---|
| 監視・アラート | Grafana + Prometheus | Grafana + Prometheus | 2セット |
| バックアップ・リカバリ | mysqldump / XtraBackup | clickhouse-backup | 2セット |
| キャパシティプランニング | プライマリ-レプリカ + シャーディング | 分散テーブル + レプリカ | 2セット |
| バージョンアップ | ローリングアップグレード | ローリングアップグレード | 2セット |
| 人員要件 | DBA | ClickHouseエキスパート | 2チーム |
ペインポイント4:クエリパフォーマンスとデータ鮮度のジレンマ
- ClickHouseはクエリパフォーマンスを追求、MergeTreeマージが必要、データに遅延あり
- リアルタイム性を求めるとReplacingMergeTreeを使うが、クエリにFINALが必要、パフォーマンス急落
- リアルタイム性とパフォーマンス、二者択一
ペインポイント5:開発効率の低下
// 開発者は2つのデータモデルを保守する必要がある
public class OrderService {
// OLTP: MySQL
@Transactional
public void createOrder(Order order) {
orderMapper.insert(order);
}
// OLAP: ClickHouse - もう一度書く必要がある
public void syncToClickHouse(Order order) {
clickHouseMapper.insert(order);
// フィールドマッピング、型変換、エラーハンドリング...
}
}
6つのプロダクション級HTAP実践パターン
パターン1:TiFlash列ストレージレプリカ構築
TiFlashはRaft Learnerロールを通じてTiKVのデータ変更を非同期受信し、行ストレージから列ストレージへのリアルタイム同期を実現。
1.1 TiFlashノードのデプロイ
# tiup topology.yaml スニペット
tiflash_servers:
- host: 10.0.1.21
tcp_port: 9000
http_port: 8123
flash_service_port: 3930
flash_proxy_port: 20170
flash_proxy_status_port: 20292
metrics_interval: 15
data_dir: /data/tiflash/data
deploy_dir: /data/tiflash/deploy
config:
logger:
level: information
storage:
main:
capacity: "2TB"
latest:
capacity: "200GB"
- host: 10.0.1.22
tcp_port: 9000
http_port: 8123
flash_service_port: 3930
flash_proxy_port: 20170
flash_proxy_status_port: 20292
data_dir: /data/tiflash/data
deploy_dir: /data/tiflash/deploy
# TiFlashノードのスケールアウト
tiup cluster scale-out tidb-prod topology-tiflash.yaml
# TiFlashノードステータスの確認
tiup cluster display tidb-prod | grep tiflash
1.2 テーブルにTiFlashレプリカを追加
-- テーブル全体にTiFlashレプリカを追加(デフォルト1レプリカ)
ALTER TABLE orders SET TIFLASH REPLICA 1;
-- テーブルに2つのTiFlashレプリカを追加(高可用性)
ALTER TABLE orders SET TIFLASH REPLICA 2;
-- パーティションテーブルにTiFlashレプリカを追加
ALTER TABLE orders_partition SET TIFLASH REPLICA 1;
-- レプリカ同期進捗の確認
SELECT * FROM information_schema.tiflash_replica
WHERE table_name = 'orders';
同期進捗出力例:
+----------+------------+----------+---------------+------------------+-----------+
| TABLE_ID | TABLE_NAME | REPLICA | LOCATION_LABELS| AVAILABLE | PROGRESS |
+----------+------------+----------+---------------+------------------+-----------+
| 85 | orders | 1 | | True | 1.0 |
+----------+------------+----------+---------------+------------------+-----------+
1.3 列単位でTiFlashレプリカを追加(ストレージ節約)
-- 分析に必要な列のみTiFlashレプリカを作成
ALTER TABLE orders SET TIFLASH REPLICA 1
COLUMNS (order_id, user_id, product_id, amount, status, created_at);
-- 列レベルレプリカの確認
SHOW CREATE TABLE orders;
パターン2:MPP分散クエリアクセラレーション
TiDBのMPPエンジンは分析クエリを複数のTiFlashノードにプッシュダウンして並列実行し、ClickHouseに近いクエリパフォーマンスを実現。
2.1 MPPモードの有効化
-- 現在のMPPモードの確認
SHOW VARIABLES LIKE 'tidb_allow_mpp';
-- MPPの有効化(デフォルトで有効)
SET SESSION tidb_allow_mpp = 1;
-- MPPの強制使用(テスト/検証用)
SET SESSION tidb_enforce_mpp = 1;
-- MPPクエリのExchange圧縮設定
SET SESSION tidb_exchange_compression = 'ON';
2.2 MPPクエリ実践:リアルタイム売上分析
-- シナリオ:本日のカテゴリ別売上Top10をリアルタイム集計
-- MPPは自動的に計算をTiFlashノードにプッシュダウンして並列実行
SELECT
c.category_name,
SUM(oi.quantity * oi.unit_price) AS total_sales,
COUNT(DISTINCT o.user_id) AS unique_buyers,
AVG(oi.quantity * oi.unit_price) AS avg_order_value
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
JOIN categories c ON p.category_id = c.category_id
WHERE o.created_at >= CURDATE()
AND o.status IN ('paid', 'shipped', 'completed')
GROUP BY c.category_name
ORDER BY total_sales DESC
LIMIT 10;
2.3 MPP実行計画の確認
-- 実行計画を確認、MPPオペレータを確認
EXPLAIN ANALYZE
SELECT
c.category_name,
SUM(oi.quantity * oi.unit_price) AS total_sales
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
JOIN categories c ON p.category_id = c.category_id
WHERE o.created_at >= CURDATE()
GROUP BY c.category_name;
主要な実行計画の特徴:
+------------------------------------------+----------+---------+--------------+
| id | task | rows | operator info|
+------------------------------------------+----------+---------+--------------+
| TableReader_45 | root | 10 | ... |
| └─ExchangeSender_44 | mpp[tif] | 10 | ExchangeType |
| └─Projection_42 | mpp[tif] | 10 | ... |
| └─HashAgg_43 | mpp[tif] | 10 | group by:... |
| └─ExchangeReceiver_38 | mpp[tif] | 100 | ... |
| └─ExchangeSender_37 | mpp[tif] | 100 | ExchangeType |
| └─HashJoin_36 | mpp[tif] | 100 | inner join |
| ├─Selection_25(Build) | mpp[tif] | 500 | ... |
| │ └─TableFullScan_24 | mpp[tif] | 10000 | tikv:tiflash |
| └─Selection_30(Probe) | mpp[tif] | 800 | ... |
| └─TableFullScan_29 | mpp[tif] | 20000 | tikv:tiflash |
+------------------------------------------+----------+---------+--------------+
mpp[tif]とExchangeSender/Receiverが見えればMPPが有効。
2.4 MPPパフォーマンス比較
-- MPPを無効化、従来のTiKVスキャンを使用
SET SESSION tidb_allow_mpp = 0;
EXPLAIN ANALYZE
SELECT c.category_name, SUM(oi.quantity * oi.unit_price) AS total_sales
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
JOIN categories c ON p.category_id = c.category_id
WHERE o.created_at >= CURDATE()
GROUP BY c.category_name;
-- MPPを再有効化
SET SESSION tidb_allow_mpp = 1;
| クエリモード | データ量 | 実行時間 | スキャン行数 |
|---|---|---|---|
| TiKVスキャン | 1億行 | 45.2s | 100,000,000 |
| TiFlash非MPP | 1億行 | 12.8s | 100,000,000 |
| TiFlash MPP | 1億行 | 3.2s | 100,000,000 |
パターン3:TiCDCリアルタイムETL代替
TiCDC(Change Data Capture)は従来のETLパイプラインを代替し、TiDBのデータ変更をリアルタイムに下流システムへ同期。
3.1 TiCDCのデプロイ
# tiup topology.yaml スニペット
cdc_servers:
- host: 10.0.1.31
port: 8300
deploy_dir: /data/cdc/deploy
data_dir: /data/cdc/data
config:
per-table-memory-quota: 10485760
sink-memory-quota: 536870912
- host: 10.0.1.32
port: 8300
deploy_dir: /data/cdc/deploy
data_dir: /data/cdc/data
# TiCDCのスケールアウト
tiup cluster scale-out tidb-prod topology-cdc.yaml
# TiCDCステータスの確認
tiup cluster display tidb-prod | grep cdc
3.2 同期タスクの作成:MySQL → Kafka → Flinkパイプラインの代替
# TiCDC同期タスクの作成:TiDB → Kafka(Canal + Kafkaの代替)
cdc cli changefeed create \
--server=http://10.0.1.31:8300 \
--sink-uri="kafka://kafka-cluster:9092/tidb-orders?partition-num=6&max-message-bytes=10485760&replication-factor=3" \
--changefeed-id="orders-to-kafka" \
--config=cdc-orders.yaml
# cdc-orders.yaml
filter:
rules:
- 'ecommerce.orders'
- 'ecommerce.order_items'
- 'ecommerce.payments'
ignore_txn_start_ts: []
mounter:
worker-num: 16
sink:
protocol: "canal-json"
dispatchers:
- matcher: ['ecommerce.*']
partition: "index-value"
selector:
rules:
- 'ecommerce.orders'
- 'ecommerce.order_items'
- 'ecommerce.payments'
3.3 ClickHouseへの直接同期(引き続き必要な場合)
# TiCDC → ClickHouseダイレクト接続
cdc cli changefeed create \
--server=http://10.0.1.31:8300 \
--sink-uri="clickhouse://clickhouse-server:8123?database=analytics&username=default&password=***" \
--changefeed-id="orders-to-clickhouse" \
--config=cdc-ch.yaml
3.4 TiCDCからS3への同期(データレイクソリューション)
# TiCDC → S3データレイク
cdc cli changefeed create \
--server=http://10.0.1.31:8300 \
--sink-uri="s3://data-lake/tidb-cdc/?endpoint=https://s3.amazonaws.com&access-key=***&secret-access-key=***" \
--changefeed-id="orders-to-s3" \
--config=cdc-s3.yaml
# cdc-s3.yaml
sink:
protocol: "csv"
column-selectors:
- matcher: ['ecommerce.orders']
columns: ['order_id', 'user_id', 'amount', 'status', 'created_at']
file:
size: 67108864
interval: 5m
filter:
rules:
- 'ecommerce.orders'
- 'ecommerce.order_items'
3.5 TiCDC同期遅延の監視
-- 同期タスクステータスの確認
cdc cli changefeed list \
--server=http://10.0.1.31:8300;
-- 同期遅延の確認
cdc cli changefeed query \
--changefeed-id=orders-to-kafka \
--server=http://10.0.1.31:8300;
主要メトリクス:
{
"state": "normal",
"tso": 446685327530688513,
"checkpoint": "2026-06-15 10:30:00.123",
"lag": "1.2s"
}
パターン4:ダッシュボードクエリ最適化
リアルタイムダッシュボードはHTAPの最も直接的な応用シナリオだが、クエリパフォーマンスがユーザー体験に直結。
4.1 ダッシュボードクエリパターンの識別
-- 典型的なダッシュボードクエリ1:リアルタイムGMV
SELECT
DATE_FORMAT(created_at, '%Y-%m-%d %H:00') AS hour_slot,
SUM(amount) AS gmv,
COUNT(*) AS order_count
FROM orders
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
AND status != 'cancelled'
GROUP BY hour_slot
ORDER BY hour_slot;
-- 典型的なダッシュボードクエリ2:リアルタイムユーザーアクティビティ
SELECT
DATE_FORMAT(login_time, '%Y-%m-%d %H:00') AS hour_slot,
COUNT(DISTINCT user_id) AS dau,
COUNT(*) AS total_actions
FROM user_actions
WHERE login_time >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
GROUP BY hour_slot
ORDER BY hour_slot;
-- 典型的なダッシュボードクエリ3:リアルタイムコンバージョンファネル
SELECT
COUNT(DISTINCT CASE WHEN event = 'view' THEN user_id END) AS views,
COUNT(DISTINCT CASE WHEN event = 'cart' THEN user_id END) AS carts,
COUNT(DISTINCT CASE WHEN event = 'pay' THEN user_id END) AS pays,
ROUND(COUNT(DISTINCT CASE WHEN event = 'pay' THEN user_id END) * 100.0
/ NULLIF(COUNT(DISTINCT CASE WHEN event = 'view' THEN user_id END), 0), 2) AS conversion_rate
FROM user_events
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 HOUR);
4.2 クエリのTiFlash強制使用
-- 方法1:HintでTiFlashを強制
SELECT /*+ read_from_storage(tiflash[orders]) */
DATE_FORMAT(created_at, '%Y-%m-%d %H:00') AS hour_slot,
SUM(amount) AS gmv
FROM orders
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
GROUP BY hour_slot;
-- 方法2:セッション変数の設定
SET SESSION tidb_isolation_read_engines = 'tiflash';
SELECT
DATE_FORMAT(created_at, '%Y-%m-%d %H:00') AS hour_slot,
SUM(amount) AS gmv
FROM orders
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
GROUP BY hour_slot;
SET SESSION tidb_isolation_read_engines = 'tikv,tiflash';
-- 方法3:Resource GroupでOLAPトラフィックを分離
CREATE RESOURCE GROUP IF NOT EXISTS olap_group
RU_PER_SEC = 5000
PRIORITY = LOW;
ALTER USER dashboard_user RESOURCE GROUP olap_group;
4.3 ダッシュボードクエリキャッシュの最適化
-- Bindingで実行計画を固定
CREATE SESSION BINDING FOR
SELECT SUM(amount) FROM orders WHERE created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
USING
SELECT /*+ read_from_storage(tiflash[orders]) */ SUM(amount)
FROM orders WHERE created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR);
-- Bindingの確認
SHOW SESSION BINDINGS;
4.4 ダッシュボードリフレッシュ戦略
-- ダッシュボードクエリはREAD ONLYモードの使用を推奨、OLTPとのリソース競合を回避
SET SESSION tidb_isolation_read_engines = 'tiflash';
-- ダッシュボードクエリのリソース消費を制限
SET SESSION max_execution_time = 30000;
-- Prepared Statementでパースオーバーヘッドを削減
PREPARE dashboard_gmv FROM
SELECT
DATE_FORMAT(created_at, '%Y-%m-%d %H:00') AS hour_slot,
SUM(amount) AS gmv
FROM orders
WHERE created_at >= ?
AND status != 'cancelled'
GROUP BY hour_slot
ORDER BY hour_slot;
EXECUTE dashboard_gmv USING DATE_SUB(NOW(), INTERVAL 24 HOUR);
パターン5:マテリアライズドビューによるデータマート構築
TiDB 8.xはマテリアライズドビュー(Materialized View)をサポート、集計データを事前計算し、ビジネス向けのデータマートを構築可能。
5.1 マテリアライズドビューの作成
-- 日次売上サマリーマテリアライズドビューの作成
CREATE MATERIALIZED VIEW mv_daily_sales
REFRESH EVERY 1 HOUR
AS
SELECT
DATE(created_at) AS sale_date,
c.category_name,
p.product_name,
SUM(oi.quantity) AS total_quantity,
SUM(oi.quantity * oi.unit_price) AS total_revenue,
COUNT(DISTINCT o.user_id) AS unique_buyers,
AVG(oi.quantity * oi.unit_price) AS avg_price
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
JOIN categories c ON p.category_id = c.category_id
WHERE o.status IN ('paid', 'shipped', 'completed')
GROUP BY DATE(created_at), c.category_name, p.product_name;
5.2 ユーザー行動マテリアライズドビューの作成
-- ユーザー行動ファネルマテリアライズドビュー
CREATE MATERIALIZED VIEW mv_user_funnel
REFRESH EVERY 5 MINUTE
AS
SELECT
DATE(created_at) AS event_date,
COUNT(DISTINCT CASE WHEN event_type = 'page_view' THEN user_id END) AS page_views,
COUNT(DISTINCT CASE WHEN event_type = 'add_to_cart' THEN user_id END) AS add_to_carts,
COUNT(DISTINCT CASE WHEN event_type = 'checkout' THEN user_id END) AS checkouts,
COUNT(DISTINCT CASE WHEN event_type = 'payment' THEN user_id END) AS payments,
ROUND(
COUNT(DISTINCT CASE WHEN event_type = 'payment' THEN user_id END) * 100.0
/ NULLIF(COUNT(DISTINCT CASE WHEN event_type = 'page_view' THEN user_id END), 0),
2
) AS view_to_pay_rate
FROM user_events
GROUP BY DATE(created_at);
5.3 マテリアライズドビューの管理
-- マテリアライズドビューのステータス確認
SHOW MATERIALIZED VIEWS;
-- 手動リフレッシュ
ALTER MATERIALIZED VIEW mv_daily_sales REFRESH;
-- 自動リフレッシュの一時停止
ALTER MATERIALIZED VIEW mv_daily_sales DISABLE;
-- 自動リフレッシュの再開
ALTER MATERIALIZED VIEW mv_daily_sales ENABLE;
-- マテリアライズドビューの削除
DROP MATERIALIZED VIEW IF EXISTS mv_daily_sales;
5.4 マテリアライズドビューベースのダッシュボードクエリ
-- マテリアライズドビューのクエリ(ミリ秒レベルのレスポンス)
SELECT
sale_date,
category_name,
total_revenue,
unique_buyers
FROM mv_daily_sales
WHERE sale_date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
ORDER BY total_revenue DESC
LIMIT 20;
パターン6:プロダクションHTAPデプロイとモニタリング
プロダクション環境のHTAPデプロイでは、リソース分離、モニタリング/アラート、障害復旧を考慮する必要がある。
6.1 リソース制御設定
-- OLTPリソースグループの作成(高優先度)
CREATE RESOURCE GROUP IF NOT EXISTS oltp_group
RU_PER_SEC = 20000
PRIORITY = HIGH;
-- OLAPリソースグループの作成(低優先度、リソース制限あり)
CREATE RESOURCE GROUP IF NOT EXISTS olap_group
RU_PER_SEC = 8000
PRIORITY = LOW;
-- ユーザーをリソースグループにバインド
ALTER USER app_user RESOURCE GROUP oltp_group;
ALTER USER dashboard_user RESOURCE GROUP olap_group;
ALTER USER etl_user RESOURCE GROUP olap_group;
-- リソースグループ設定の確認
SELECT * FROM information_schema.resource_groups;
6.2 TiFlash主要モニタリングメトリクス
# Grafana Dashboard 主要メトリクス
# TiFlash Summary Dashboard
tiflash_system_profile:
- metric: tiflash_storage_write_duration_seconds
alert_threshold: "> 5s"
description: "TiFlash書き込みレイテンシ"
- metric: tiflash_mpp_task_duration_seconds
alert_threshold: "> 60s"
description: "MPPタスク実行時間"
- metric: tiflash_storage_segment_count
alert_threshold: "> 10000"
description: "セグメント数(多すぎる場合はcompactionが必要)"
- metric: tiflash_cpu_usage
alert_threshold: "> 80%"
description: "TiFlash CPU使用率"
- metric: tiflash_storage_disk_usage_ratio
alert_threshold: "> 0.8"
description: "TiFlashディスク使用率"
6.3 Prometheusアラートルール
# tidb-htap-alerts.yaml
groups:
- name: tidb_htap
rules:
- alert: TiFlashReplicaSyncLag
expr: |
tiflash_replica_available == 0
or (tiflash_replica_progress < 0.95 and tiflash_replica_progress > 0)
for: 10m
labels:
severity: warning
annotations:
summary: "TiFlashレプリカ同期遅延"
description: "テーブル {{ $labels.table_name }} のTiFlashレプリカ同期進捗は {{ $value }} のみ"
- alert: TiFlashHighWriteDuration
expr: |
histogram_quantile(0.95,
rate(tiflash_storage_write_duration_seconds_bucket[5m])
) > 5
for: 5m
labels:
severity: warning
annotations:
summary: "TiFlash書き込みレイテンシが高すぎる"
description: "P95書き込みレイテンシ {{ $value }}s"
- alert: MPPQueryTimeout
expr: |
rate(tiflash_mpp_task_error_count{reason="timeout"}[5m]) > 0.1
for: 3m
labels:
severity: critical
annotations:
summary: "MPPクエリタイムアウト率が高すぎる"
description: "MPPクエリタイムアウト率 {{ $value }}/s"
- alert: TiFlashDiskSpaceLow
expr: |
tiflash_storage_disk_usage_ratio > 0.85
for: 5m
labels:
severity: critical
annotations:
summary: "TiFlashディスク容量不足"
description: "ディスク使用率 {{ $value }}"
6.4 日常運用スクリプト
#!/bin/bash
# tidb-htap-healthcheck.sh
# TiDB HTAPヘルスチェックスクリプト
CLUSTER_NAME="tidb-prod"
LOG_FILE="/var/log/tidb-htap-healthcheck.log"
echo "=== TiDB HTAP Health Check $(date) ===" >> $LOG_FILE
# TiFlashノードステータスの確認
echo "--- TiFlash Node Status ---" >> $LOG_FILE
tiup cluster display $CLUSTER_NAME 2>/dev/null | grep tiflash >> $LOG_FILE
# TiFlashレプリカ同期ステータスの確認
echo "--- TiFlash Replica Sync Status ---" >> $LOG_FILE
mysql -h10.0.1.10 -P4000 -uroot -e "
SELECT table_schema, table_name, replica_count, available, progress
FROM information_schema.tiflash_replica
WHERE available = 0 OR progress < 1.0;
" >> $LOG_FILE 2>&1
# TiCDC同期遅延の確認
echo "--- TiCDC Sync Lag ---" >> $LOG_FILE
cdc cli changefeed list --server=http://10.0.1.31:8300 >> $LOG_FILE 2>&1
# MPPクエリパフォーマンスの確認
echo "--- MPP Query Performance ---" >> $LOG_FILE
mysql -h10.0.1.10 -P4000 -uroot -e "
SELECT DIGEST_TEXT, AVG_LATENCY, EXEC_COUNT
FROM information_schema.statements_summary
WHERE DIGEST_TEXT LIKE '%GROUP BY%'
AND AVG_LATENCY > 1000000000
ORDER BY AVG_LATENCY DESC
LIMIT 10;
" >> $LOG_FILE 2>&1
echo "=== Health Check Complete ===" >> $LOG_FILE
5つのよくある落とし穴ガイド
❌ 落とし穴1:TiFlashレプリカ未同期でクエリ実行
-- ❌ 誤り:TiFlashレプリカ追加直後にクエリ、データが不完全の可能性
ALTER TABLE orders SET TIFLASH REPLICA 1;
-- 即座にクエリ、不完全なデータを読み取る可能性
SELECT COUNT(*) FROM orders;
-- ✅ 正しい:レプリカ同期完了を待つ
ALTER TABLE orders SET TIFLASH REPLICA 1;
-- 同期進捗の確認
SELECT table_name, progress, available
FROM information_schema.tiflash_replica
WHERE table_name = 'orders';
-- available = 1 になってからクエリ
SELECT COUNT(*) FROM orders;
❌ 落とし穴2:小さなテーブルにもTiFlashレプリカを追加
-- ❌ 誤り:小さなテーブルにTiFlashレプリカを追加すると逆に遅くなる(ネットワークオーバーヘッド > スキャンオーバーヘッド)
ALTER TABLE config_dict SET TIFLASH REPLICA 1;
-- ✅ 正しい:小さなテーブルはTiKVに維持、大きなテーブルのみTiFlashレプリカを追加
-- 目安:データ量 > 100万行の場合のみTiFlashレプリカを検討
-- 小さなテーブルはHintでTiKVを強制
SELECT /*+ read_from_storage(tikv[config_dict]) */
* FROM config_dict WHERE type = 'payment';
❌ 落とし穴3:OLAPクエリがOLTPパフォーマンスに影響
-- ❌ 誤り:OLAPとOLTPが同じユーザーを使用、リソース分離なし
SELECT user, resource_group FROM mysql.user WHERE user = 'app_user';
-- app_user | default
-- ✅ 正しい:Resource Groupで分離
CREATE RESOURCE GROUP IF NOT EXISTS oltp_group
RU_PER_SEC = 20000 PRIORITY = HIGH;
CREATE RESOURCE GROUP IF NOT EXISTS olap_group
RU_PER_SEC = 8000 PRIORITY = LOW;
ALTER USER app_user RESOURCE GROUP oltp_group;
ALTER USER dashboard_user RESOURCE GROUP olap_group;
-- OLAPクエリにタイムアウトを設定
SET SESSION max_execution_time = 60000;
❌ 落とし穴4:TiFlashノードのディスク容量不足
# ❌ 誤り:TiFlashデータディレクトリがシステムディスクと共用
tiflash_servers:
- host: 10.0.1.21
data_dir: /var/lib/tiflash/data # システムディスクは容量が限られている
# ✅ 正しい:TiFlashは専用データディスクを使用、30%の空き容量を確保
tiflash_servers:
- host: 10.0.1.21
data_dir: /data/tiflash/data # 専用データディスク
config:
storage:
main:
capacity: "2TB" # 容量制限を明示
latest:
capacity: "200GB"
❌ 落とし穴5:TiCDC同期遅延アラートの無視
# ❌ 誤り:TiCDC遅延を監視せず、データ同期が停止しても気づかない
# アラート設定が一切ない
# ✅ 正しい:TiCDC遅延アラートを設定
# Prometheusアラートルール
- alert: TiCDCReplicationLag
expr: |
tidb_cdc_checkpoint_ts_lag_seconds > 300
for: 5m
labels:
severity: critical
annotations:
summary: "TiCDC同期遅延が5分を超過"
description: "changefeed {{ $labels.changefeed }} 遅延 {{ $value }}s"
エラートラブルシューティングクイックリファレンス
| エラー現象 | 考えられる原因 | 診断コマンド/方法 | 解決策 |
|---|---|---|---|
| TiFlashレプリカ同期がスタック | TiFlashノードのディスク満杯またはダウン | SELECT * FROM information_schema.tiflash_replica WHERE progress < 1.0; |
ディスク拡張またはTiFlashノードの再起動 |
MPPクエリが Too many MPP tasks を報告 |
同時MPPクエリが多すぎる | SHOW CONFIG WHERE name LIKE '%mpp%'; |
tidb_mpp_task_limit の調整または同時実行数の削減 |
| クエリがTiFlashを使用していない | オプティマイザがTiKVをより適切と判断 | EXPLAIN SELECT ...; でオペレータを確認 |
/*+ read_from_storage(tiflash[t]) */ Hintを使用 |
| TiCDC同期遅延が増大 | 下流の書き込みが遅い、またはネットワークジッター | cdc cli changefeed query --changefeed-id=xxx |
下流のスケールアップまたはTiCDCワーカー数の増加 |
| TiFlash OOM | クエリデータ量が大きすぎる | grep OOM /data/tiflash/log/tiflash.log |
max_memory_usage の増加またはクエリ範囲の絞り込み |
| ダッシュボードクエリのタイムアウト | クエリリソース制限なし | SHOW PROCESSLIST; |
max_execution_time とResource Groupの設定 |
| マテリアライズドビューのリフレッシュ失敗 | ソーステーブルのデータ量が大きすぎる、またはロック競合 | SHOW MATERIALIZED VIEWS; |
リフレッシュ頻度の調整またはソーステーブルインデックスの最適化 |
| TiFlash書き込みレイテンシが高い | Raft Learner同期圧力が高い | Grafana TiFlash Dashboard | TiFlashノードのスケールアップまたは raft-store.sync-log の調整 |
| MPP Joinのデータスキュー | Joinキーの分布が不均一 | EXPLAIN ANALYZE で各ノードのデータ量を確認 |
/*+ shuffle_join() */ の使用またはバケット数の増加 |
| TiFlashディスクIOが高い | Compactionまたは大量スキャン | iostat -x 1 でIOを確認 |
bg_task_io_limit の調整またはディスク拡張 |
高度な最適化テクニック
3.1 TiFlashストレージエンジンのチューニング
-- TiFlashセグメント分割閾値の調整(小さなセグメントの削減)
-- TiFlash設定で変更
# tiflash-config.yaml
storage:
main:
segment_delta_rows: 8192
segment_delta_size: 536870912
segment_stable_rows: 10485760
latest:
segment_delta_rows: 4096
segment_delta_size: 134217728
raft:
apply_batch: 1024
sync_log: false
engine:
dt:
enable_read_thread: true
read_thread_count: 8
enable_leaf_dt_segment_optimize: true
3.2 MPPクエリの高度な最適化
-- 1. Runtime FilterでJoinデータ転送を削減
-- TiDB 8.xではデフォルトで有効、ステータスを確認
SET SESSION tidb_runtime_filter_mode = "LOCAL";
-- 2. MPP Exchangeデータ圧縮の調整
SET SESSION tidb_exchange_compression = 'ON';
SET SESSION tidb_exchange_compression_level = 3;
-- 3. 大きなテーブルのJoin最適化:Hash Join Hintを使用
SELECT /*+ hash_join_build(o) */
c.category_name,
SUM(oi.quantity * oi.unit_price) AS total_sales
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN categories c ON oi.product_id = c.category_id
GROUP BY c.category_name;
-- 4. Index Lookup JoinでHash Joinを代替(小さなテーブルが大きなテーブルを駆動する場合に適している)
SELECT /*+ inl_join(p) */
o.order_id, p.product_name
FROM orders o
JOIN products p ON o.product_id = p.product_id
WHERE o.created_at >= CURDATE();
3.3 マルチテナントHTAPリソース分離
-- 異なるビジネスライン向けに独立したリソースグループを作成
CREATE RESOURCE GROUP IF NOT EXISTS rg_ecommerce
RU_PER_SEC = 15000 PRIORITY = HIGH
QUERY_LIMIT = (EXECUTION_TIME = 30s);
CREATE RESOURCE GROUP IF NOT EXISTS rg_analytics
RU_PER_SEC = 10000 PRIORITY = LOW
QUERY_LIMIT = (EXECUTION_TIME = 120s);
CREATE RESOURCE GROUP IF NOT EXISTS rg_data_science
RU_PER_SEC = 5000 PRIORITY = LOW
QUERY_LIMIT = (EXECUTION_TIME = 600s);
-- ユーザーをリソースグループにバインド
ALTER USER ecommerce_app RESOURCE GROUP rg_ecommerce;
ALTER USER analytics_user RESOURCE GROUP rg_analytics;
ALTER USER ds_user RESOURCE GROUP rg_data_science;
-- リソースグループの使用状況をモニタリング
SELECT * FROM information_schema.resource_group_usage;
HTAPソリューション比較
| 次元 | TiDB HTAP | ClickHouse + MySQL | Snowflake | Apache Doris | SingleStore |
|---|---|---|---|---|---|
| アーキテクチャ | 統合HTAP | デュアルシステムの結合 | クラウドネイティブOLAP | 統合HTAP | 統合HTAP |
| OLTP能力 | 強(MySQL互換) | MySQLネイティブ | なし | 弱い | 中程度 |
| OLAPパフォーマンス | 強(MPP) | 非常に強い | 非常に強い | 強い | 強い |
| データ同期 | Raft Learner(自動) | 自前ETLが必要 | インポートが必要 | 自動 | 自動 |
| リアルタイム性 | 秒レベル | 分レベル | 分レベル | 秒レベル | 秒レベル |
| MySQL互換性 | 95%+ | ネイティブ | 非互換 | 中程度 | 中程度 |
| 水平スケーリング | 透過的 | 各自でスケーリング | 自動スケーリング | 透過的 | 透過的 |
| 運用複雑度 | 中程度 | 高い | 低い | 中程度 | 中程度 |
| コスト | 中程度 | 高い(デュアルシステム) | 高い(従量課金) | 中程度 | 高い |
| オープンソース | 完全オープンソース | 一部オープンソース | クローズドソース | 完全オープンソース | 一部オープンソース |
| 適用シナリオ | 汎用HTAP | 既存MySQL + 強力なOLAPが必要 | 純粋な分析 | リアルタイムデータウェアハウス | リアルタイム分析 |
まとめ
TiDB HTAPは「OLTPもOLAPもできる」というマーケティングスローガンではなく、Raft Learnerによる行ストレージから列ストレージへのリアルタイム同期、MPPエンジンによる分散並列計算、Resource ControlによるOLTP/OLAPリソース分離を実現するエンジニアリングソリューションです。2026年の今、まだMySQL + ClickHouseのデュアルシステムアーキテクチャを維持しているなら、TiDB HTAPを真剣に評価する時です——1つのシステム、1つの運用、ゼロデータ遅延、それこそがリアルタイム分析のあるべき姿です。
おすすめツール
- JSONフォーマッター - TiDB設定JSONファイルの整形
- Base64エンコード/デコード - TiCDC証明書エンコーディングの処理
- ハッシュ計算 - データ同期整合性の検証
ブラウザローカルツールを無料で試す →