時序資料庫選型:IoT場景下QuestDB vs InfluxDB vs TDengine 2026

数据库

時序資料庫選型:IoT場景下QuestDB vs InfluxDB vs TDengine

2026年的IoT世界裡,你的感測器每秒吐出上萬條資料點——溫度、濕度、振動頻率、GPS座標……傳統MySQL?寫入瓶頸5分鐘後就崩了。MongoDB?時序聚合查詢慢到讓人懷疑人生。

選錯時序資料庫,輕則查詢超時、儲存爆炸,重則整個資料管線癱瘓。QuestDB、InfluxDB、TDengine三足鼎立,各有絕活,但到底誰才是你的IoT場景最優解?本文從5個核心實戰模式出發,帶你徹底搞清楚。

核心概念速覽

概念 說明 典型場景
時序資料(Time Series) 按時間順序排列的資料點序列 感測器採集、監控指標
標籤(Tag) 資料的維度/索引欄位 裝置ID、區域、型別
欄位(Field) 資料的度量值 溫度值、電壓值
降取樣(Downsampling) 將高頻資料聚合為低頻 秒級→分鐘級→小時級
保留策略(Retention Policy) 資料自動過期清理 熱資料7天,冷資料1年
超級表(Super Table) TDengine特有,模板化多裝置表 1000台裝置共用一張模板

IoT時序資料的5大痛點

  1. 寫入吞吐瓶頸:百萬級裝置併發寫入,傳統資料庫IOPS扛不住
  2. 查詢聚合低效:時間視窗聚合、降取樣查詢效能差,P99延遲飆升
  3. 儲存成本失控:時序資料量大且持續增長,冷熱資料分離難
  4. 多裝置管理複雜:千台裝置各有標籤,Schema管理混亂
  5. 生態整合割裂:與Grafana、Kafka、Telegraf等工具鏈對接成本高

模式一:IoT時序資料特徵與建模

IoT時序資料有鮮明的特徵:高基數標籤、持續追加寫入、時間局部性強、讀多寫多。理解這些特徵是選型的前提。

# Python: 模擬IoT時序資料生成
# 執行環境: Python 3.12+ / 無額外依賴
import time
import json
import random
from datetime import datetime, timezone

def generate_iot_sensor_data(device_count: int = 100, interval_ms: int = 1000) -> dict:
    """生成IoT感測器時序資料點
    
    Args:
        device_count: 裝置數量
        interval_ms: 採集間隔(毫秒)
    
    Returns:
        單條時序資料點
    """
    device_id = f"sensor-{random.randint(1, device_count):04d}"
    region = random.choice(["east-tw", "west-tw", "south-tw", "north-tw"])
    sensor_type = random.choice(["temperature", "humidity", "vibration", "pressure"])
    
    base_values = {
        "temperature": 25.0,
        "humidity": 60.0,
        "vibration": 0.5,
        "pressure": 101.3,
    }
    value = round(base_values[sensor_type] + random.gauss(0, 2), 3)
    
    return {
        "timestamp": int(time.time() * 1_000_000),
        "tags": {
            "device_id": device_id,
            "region": region,
            "sensor_type": sensor_type,
            "factory": f"plant-{region.split('-')[0]}",
        },
        "fields": {
            "value": value,
            "quality": random.choice(["good", "good", "good", "warning", "error"]),
            "battery": round(random.uniform(10, 100), 1),
        },
    }


def simulate_iot_stream(duration_seconds: int = 10, devices: int = 100) -> list[dict]:
    """模擬IoT資料流
    
    Args:
        duration_seconds: 模擬時長(秒)
        devices: 裝置數量
    
    Returns:
        時序資料點列表
    """
    data_points = []
    start = time.time()
    
    while time.time() - start < duration_seconds:
        point = generate_iot_sensor_data(device_count=devices)
        data_points.append(point)
        time.sleep(0.01)
    
    print(f"生成 {len(data_points)} 條資料點, "
          f"時間跨度 {duration_seconds}s, "
          f"裝置數 {devices}")
    return data_points


if __name__ == "__main__":
    points = simulate_iot_stream(duration_seconds=5, devices=50)
    print(json.dumps(points[0], indent=2, ensure_ascii=False))

模式二:QuestDB SQL時序查詢

QuestDB以「SQL優先」著稱,對標準SQL的時序擴展讓查詢極其直觀。零依賴安裝、高效能寫入是它的殺手鐧。

-- QuestDB: IoT時序資料SQL查詢模式
-- 執行環境: QuestDB 8.x / ILP(InfluxDB Line Protocol)寫入

-- 1. 建立表
CREATE TABLE IF NOT EXISTS sensor_data (
    timestamp TIMESTAMP,
    device_id SYMBOL,
    region SYMBOL,
    sensor_type SYMBOL,
    factory SYMBOL,
    value DOUBLE,
    quality SYMBOL,
    battery DOUBLE
) TIMESTAMP(timestamp) PARTITION BY DAY WAL;

-- 2. 時間視窗聚合 - 每5分鐘平均溫度
SELECT
    timestamp,
    device_id,
    avg(value) AS avg_temp,
    min(value) AS min_temp,
    max(value) AS max_temp,
    count() AS sample_count
FROM sensor_data
WHERE sensor_type = 'temperature'
  AND timestamp >= dateadd('h', -1, now())
SAMPLE BY 5m ALIGN TO CALENDAR;

-- 3. 最新值查詢 - LATEST ON語法
SELECT * FROM sensor_data LATEST ON timestamp PARTITION BY device_id;

-- 4. 降取樣 + 多維度分組
SELECT
    timestamp,
    region,
    sensor_type,
    avg(value) AS avg_value,
    stddev(value) AS std_value,
    count() AS data_points
FROM sensor_data
WHERE timestamp >= dateadd('d', -7, now())
SAMPLE BY 1h ALIGN TO CALENDAR
GROUP BY region, sensor_type;

-- 5. 異常偵測 - 連續3個點超閾值告警
WITH spike_detected AS (
    SELECT
        timestamp,
        device_id,
        value,
        case when value > 35 OR value < 15 THEN 1 ELSE 0 END AS is_spike
    FROM sensor_data
    WHERE sensor_type = 'temperature'
)
SELECT
    timestamp,
    device_id,
    value,
    sum(is_spike) OVER (
        ORDER BY timestamp
        ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
    ) AS consecutive_spikes
FROM spike_detected
WHERE consecutive_spikes >= 3;
# Python: QuestDB ILP寫入客戶端
# 執行環境: Python 3.12+ / pip install questdb
from questdb.ingress import IngressError, Sender, Buffer, Protocol

def write_iot_data_questdb(host: str = "localhost", port: int = 9009):
    """透過ILP協議寫入QuestDB"""
    conf = f"http::addr={host}:{port};"
    
    try:
        with Sender.from_conf(conf) as sender:
            for i in range(1000):
                device_id = f"sensor-{i % 50:04d}"
                region = ["east-tw", "west-tw", "south-tw"][i % 3]
                
                sender.row(
                    "sensor_data",
                    symbols={
                        "device_id": device_id,
                        "region": region,
                        "sensor_type": "temperature",
                        "factory": f"plant-{region.split('-')[0]}",
                    },
                    columns={
                        "value": 25.0 + (i % 10) * 0.5,
                        "battery": 80.0 + (i % 20),
                    },
                    at=Sender.current_timestamp_with_nanos(),
                )
            
            sender.flush()
            print(f"成功寫入1000條資料到QuestDB")
            
    except IngressError as e:
        print(f"寫入失敗: {e}")


if __name__ == "__main__":
    write_iot_data_questdb()

模式三:InfluxDB Flux查詢

InfluxDB 3.x已轉向SQL查詢,但Flux在2.x生態中仍廣泛使用。掌握Flux是維運存量系統的必備技能。

# Python: InfluxDB 2.x Flux查詢模式
# 執行環境: Python 3.12+ / pip install influxdb-client[async]
from influxdb_client import InfluxDBClient, Point, WriteOptions
from influxdb_client.client.write_api import SYNCHRONOUS
import datetime

INFLUX_URL = "http://localhost:8086"
INFLUX_TOKEN = "your-api-token"
INFLUX_ORG = "iot-org"
INFLUX_BUCKET = "sensor-bucket"


def write_iot_data_influxdb():
    """寫入IoT資料到InfluxDB 2.x"""
    client = InfluxDBClient(url=INFLUX_URL, token=INFLUX_TOKEN, org=INFLUX_ORG)
    write_api = client.write_api(write_options=SYNCHRONOUS)
    
    points = []
    for i in range(500):
        point = (
            Point("sensor_data")
            .tag("device_id", f"sensor-{i % 50:04d}")
            .tag("region", ["east-tw", "west-tw", "south-tw"][i % 3])
            .tag("sensor_type", "temperature")
            .field("value", 25.0 + (i % 10) * 0.5)
            .field("battery", 80.0 + (i % 20))
            .time(datetime.datetime.utcnow(), write_precision="ms")
        )
        points.append(point)
    
    write_api.write(bucket=INFLUX_BUCKET, org=INFLUX_ORG, record=points)
    print(f"成功寫入{len(points)}條資料到InfluxDB")
    client.close()


FLUX_QUERIES = """
// 1. 時間視窗聚合 - 每5分鐘平均溫度
from(bucket: "sensor-bucket")
  |> range(start: -1h)
  |> filter(fn: (r) => r._measurement == "sensor_data")
  |> filter(fn: (r) => r.sensor_type == "temperature")
  |> filter(fn: (r) => r._field == "value")
  |> aggregateWindow(every: 5m, fn: mean, createEmpty: false)
  |> yield(name: "avg_temp_5m")

// 2. 多維度分組統計
from(bucket: "sensor-bucket")
  |> range(start: -7d)
  |> filter(fn: (r) => r._measurement == "sensor_data")
  |> filter(fn: (r) => r._field == "value")
  |> aggregateWindow(every: 1h, fn: mean, createEmpty: false)
  |> group(columns: ["region", "sensor_type"])
  |> yield(name: "hourly_by_region_type")

// 3. 異常偵測 - 連續超閾值
from(bucket: "sensor-bucket")
  |> range(start: -1h)
  |> filter(fn: (r) => r._measurement == "sensor_data")
  |> filter(fn: (r) => r._field == "value")
  |> map(fn: (r) => ({r with is_spike: if r._value > 35.0 or r._value < 15.0 then 1 else 0}))
  |> window(every: 5m)
  |> sum(column: "is_spike")
  |> filter(fn: (r) => r.is_spike >= 3)
  |> yield(name: "spike_alert")

// 4. 降取樣寫入連續查詢
from(bucket: "sensor-bucket")
  |> range(start: -30d)
  |> filter(fn: (r) => r._measurement == "sensor_data")
  |> aggregateWindow(every: 1h, fn: mean, createEmpty: false)
  |> to(bucket: "sensor-downsampled", org: "iot-org")
"""

if __name__ == "__main__":
    write_iot_data_influxdb()
    print("\nFlux查詢範例:")
    print(FLUX_QUERIES)

模式四:TDengine超級表

TDengine的超級表是IoT場景的殺手級特性:一張超級表模板管理千台裝置,子表自動繼承標籤,查詢效能極高。

-- TDengine 3.x: IoT超級表模式
-- 執行環境: TDengine 3.3+ / taos CLI

-- 1. 建立超級表(模板)
CREATE STABLE IF NOT EXISTS sensor_data (
    ts TIMESTAMP,
    value FLOAT,
    battery FLOAT,
    quality NCHAR(10)
) TAGS (
    device_id NCHAR(20),
    region NCHAR(20),
    sensor_type NCHAR(20),
    factory NCHAR(20)
);

-- 2. 自動建子表並寫入
INSERT INTO d_sensor_0001 USING sensor_data TAGS ('sensor-0001', 'east-tw', 'temperature', 'plant-east')
VALUES (NOW + 0a, 25.3, 85.2, 'good');

INSERT INTO d_sensor_0002 USING sensor_data TAGS ('sensor-0002', 'west-tw', 'humidity', 'plant-west')
VALUES (NOW + 0a, 62.1, 91.5, 'good');

-- 3. 批量寫入多裝置
INSERT INTO 
    d_sensor_0001 VALUES (NOW + 1a, 25.5, 85.0, 'good') (NOW + 2a, 25.7, 84.8, 'good')
    d_sensor_0002 VALUES (NOW + 1a, 62.3, 91.2, 'good') (NOW + 2a, 62.0, 91.0, 'warning');

-- 4. 超級表聚合查詢 - 按區域統計
SELECT
    region,
    sensor_type,
    AVG(value) AS avg_value,
    STDDEV(value) AS std_value,
    COUNT(*) AS data_points
FROM sensor_data
WHERE ts >= NOW - 1h
INTERVAL(5m)
GROUP BY region, sensor_type;

-- 5. 最新值查詢 - LAST_ROW
SELECT
    device_id,
    LAST_ROW(value) AS latest_value,
    LAST_ROW(ts) AS last_update
FROM sensor_data
GROUP BY device_id;

-- 6. 降取樣 + 視窗函數
SELECT
    _wstart AS window_start,
    _wend AS window_end,
    device_id,
    AVG(value) AS avg_value,
    MIN(value) AS min_value,
    MAX(value) AS max_value
FROM sensor_data
WHERE ts >= NOW - 7d AND sensor_type = 'temperature'
INTERVAL(1h) SLIDING(30m)
GROUP BY device_id;
# Python: TDengine寫入客戶端
# 執行環境: Python 3.12+ / pip install taos-ws-py
import taosws

def write_iot_data_tdengine(host: str = "localhost", port: int = 6041):
    """寫入IoT資料到TDengine 3.x"""
    conn = taosws.connect(f"ws://{host}:{port}/rest/sql", user="root", password="taosdata")
    
    conn.execute("CREATE DATABASE IF NOT EXISTS iot_db KEEP 3650")
    conn.execute("USE iot_db")
    
    conn.execute("""
        CREATE STABLE IF NOT EXISTS sensor_data (
            ts TIMESTAMP,
            value FLOAT,
            battery FLOAT,
            quality NCHAR(10)
        ) TAGS (
            device_id NCHAR(20),
            region NCHAR(20),
            sensor_type NCHAR(20),
            factory NCHAR(20)
        )
    """)
    
    for i in range(100):
        device_id = f"sensor-{i:04d}"
        region = ["east-tw", "west-tw", "south-tw"][i % 3]
        table_name = f"d_sensor_{i:04d}"
        
        sql = f"""
        INSERT INTO {table_name} USING sensor_data 
        TAGS ('{device_id}', '{region}', 'temperature', 'plant-{region.split('-')[0]}')
        VALUES (NOW + {i}a, {25.0 + i * 0.1}, {80.0 + i * 0.2}, 'good')
        """
        conn.execute(sql)
    
    print("成功寫入100條資料到TDengine")
    
    result = conn.execute("SELECT COUNT(*) FROM sensor_data")
    for row in result:
        print(f"總資料量: {row[0]}")
    
    conn.close()


if __name__ == "__main__":
    write_iot_data_tdengine()

模式五:生產級選型對比

# Python: 時序資料庫選型評估框架
# 執行環境: Python 3.12+ / 無額外依賴
from dataclasses import dataclass

@dataclass
class TSDBEvaluation:
    """時序資料庫選型評估模型"""
    name: str
    write_throughput_per_sec: int
    query_latency_p99_ms: float
    compression_ratio: float
    sql_compatibility: int
    ecosystem_maturity: int
    cluster_scalability: int
    learning_curve: int
    license_type: str
    best_for: str
    caution: str


def evaluate_tsdb_choices() -> list[TSDBEvaluation]:
    """生成三大時序資料庫評估結果"""
    return [
        TSDBEvaluation(
            name="QuestDB",
            write_throughput_per_sec=1_500_000,
            query_latency_p99_ms=15.0,
            compression_ratio=8.5,
            sql_compatibility=9,
            ecosystem_maturity=6,
            cluster_scalability=5,
            learning_curve=9,
            license_type="Apache 2.0",
            best_for="SQL團隊、高頻寫入、金融/IoT即時分析",
            caution="叢集版商業授權、生態外掛較少",
        ),
        TSDBEvaluation(
            name="InfluxDB",
            write_throughput_per_sec=500_000,
            query_latency_p99_ms=45.0,
            compression_ratio=5.2,
            sql_compatibility=5,
            ecosystem_maturity=9,
            cluster_scalability=7,
            learning_curve=6,
            license_type="MIT / 商業",
            best_for="DevOps監控、Telegraf生態、中小規模IoT",
            caution="Flux學習曲線陡峭、3.x遷移成本高、叢集版收費",
        ),
        TSDBEvaluation(
            name="TDengine",
            write_throughput_per_sec=2_000_000,
            query_latency_p99_ms=8.0,
            compression_ratio=12.0,
            sql_compatibility=7,
            ecosystem_maturity=7,
            cluster_scalability=8,
            learning_curve=7,
            license_type="AGPL-3.0 / 商業",
            best_for="超大規模IoT、車聯網、工業網際網路、國產化",
            caution="AGPL協議限制、社群版功能受限、SQL方言差異",
        ),
    ]


def print_comparison_table(evaluations: list[TSDBEvaluation]):
    """列印選型對比表"""
    print(f"{'指標':<25} {'QuestDB':<20} {'InfluxDB':<20} {'TDengine':<20}")
    print("-" * 85)
    
    fields = [
        ("寫入吞吐(點/秒)", "write_throughput_per_sec", True),
        ("查詢P99延遲(ms)", "query_latency_p99_ms", False),
        ("壓縮率", "compression_ratio", True),
        ("SQL相容性(1-10)", "sql_compatibility", True),
        ("生態成熟度(1-10)", "ecosystem_maturity", True),
        ("叢集擴展性(1-10)", "cluster_scalability", True),
        ("學習曲線(1-10)", "learning_curve", True),
        ("開源協議", "license_type", None),
    ]
    
    for label, attr, _ in fields:
        values = [str(getattr(e, attr)) for e in evaluations]
        print(f"{label:<25} {values[0]:<20} {values[1]:<20} {values[2]:<20}")
    
    print("\n最佳場景:")
    for e in evaluations:
        print(f"  {e.name}: {e.best_for}")
    print("\n注意事項:")
    for e in evaluations:
        print(f"  {e.name}: {e.caution}")


if __name__ == "__main__":
    evaluations = evaluate_tsdb_choices()
    print_comparison_table(evaluations)

避坑指南:5個常見陷阱

坑1:高基數標籤導致OOM

❌ 錯誤做法:將device_id作為InfluxDB的Tag
✅ 正確做法:高基數欄位放Field,低基數維度放Tag

InfluxDB的Tag會建立倒排索引,10萬個device_id = 10萬個Series,記憶體直接爆炸。QuestDB的SYMBOL型別和TDengine的超級表天然解決了這個問題。

坑2:忽略時區導致聚合錯位

-- ❌ 錯誤:未指定時區,跨時區聚合錯位
SAMPLE BY 1h

-- ✅ 正確:QuestDB指定時區對齊
SAMPLE BY 1h ALIGN TO CALENDAR WITH TIME ZONE 'Asia/Taipei'

坑3:保留策略未配置導致磁碟爆滿

-- InfluxDB: 必須設定RP
CREATE RETENTION POLICY "7days" ON "sensor-bucket" DURATION 7d REPLICATION 1 DEFAULT

-- TDengine: 建庫時指定KEEP
CREATE DATABASE iot_db KEEP 3650

坑4:批量寫入大小不當

# ❌ 錯誤:逐條寫入,網路開銷巨大
for point in data_points:
    write_api.write(bucket=BUCKET, record=point)

# ✅ 正確:批量寫入,推薦5000-10000條/批
BATCH_SIZE = 5000
for i in range(0, len(data_points), BATCH_SIZE):
    batch = data_points[i:i + BATCH_SIZE]
    write_api.write(bucket=BUCKET, record=batch)

坑5:TDengine超級表與子表混淆

-- ❌ 錯誤:對超級表直接INSERT資料(無子表)
INSERT INTO sensor_data VALUES (NOW, 25.0, 80.0, 'good');

-- ✅ 正確:透過子表寫入,自動繼承超級表標籤
INSERT INTO d_sensor_0001 USING sensor_data TAGS (...)
VALUES (NOW, 25.0, 80.0, 'good');

報錯排查表

報錯資訊 資料庫 原因 解決方案
too many series InfluxDB 高基數Tag導致Series超限 將高基數欄位移至Field,或升級InfluxDB 3.x
memory limit exceeded QuestDB 查詢結果集過大 新增LIMIT、使用SAMPLE BY降取樣
Invalid column type QuestDB SYMBOL列寫入超長字串 控制SYMBOL長度<64位元組,或改用STRING
table does not exist TDengine 未USE資料庫 先執行USE iot_db
out of memory TDengine 單查詢記憶體超限 調整queryMemory參數,縮小時間範圍
connection refused :9009 QuestDB ILP埠未啟用 設定tcp.enabled=true
authorization failed InfluxDB Token權限不足 檢查Token的read/write權限
database not found InfluxDB Bucket不存在 先建立Bucket或檢查拼寫
invalid timestamp QuestDB 時間戳精度不匹配 ILP預設奈秒,確認時間戳單位
duplicate table name TDengine 子表名衝突 確保子表名全域唯一

進階優化:5個生產級技巧

技巧1:冷熱資料分層儲存

-- QuestDB: 分區 + O3引擎冷熱分離
ALTER TABLE sensor_data ALTER PARTITION LIST 
    SET ATTRIBUTE 'cold' WHERE timestamp < dateadd('d', -30, now());

-- TDengine: 多級儲存
ALTER DATABASE iot_db KEEP 30,365,3650;

技巧2:預聚合物化檢視

-- QuestDB: 物化檢視自動降取樣
CREATE MATERIALIZED VIEW sensor_hourly AS (
    SELECT
        timestamp,
        device_id,
        avg(value) AS avg_value,
        min(value) AS min_value,
        max(value) AS max_value,
        count() AS sample_count
    FROM sensor_data
    SAMPLE BY 1h ALIGN TO CALENDAR
) PARTITION BY DAY;

技巧3:寫入批處理與背壓控制

# Python: 帶背壓控制的QuestDB寫入
# 執行環境: Python 3.12+ / pip install questdb
import time
import threading
from queue import Queue, Full
from questdb.ingress import Sender, IngressError

class BatchingWriter:
    """帶背壓控制的批量寫入器"""
    
    def __init__(self, host: str = "localhost", port: int = 9009,
                 batch_size: int = 5000, max_queue: int = 100_000):
        self.batch_size = batch_size
        self.queue = Queue(maxsize=max_queue)
        self.sender = Sender.from_conf(f"http::addr={host}:{port};")
        self._running = True
        self._worker = threading.Thread(target=self._flush_loop, daemon=True)
        self._worker.start()
    
    def write(self, table: str, symbols: dict, columns: dict):
        """非阻塞寫入,佇列滿時丟棄"""
        try:
            self.queue.put_nowait((table, symbols, columns))
        except Full:
            print("警告: 寫入佇列已滿,丟棄資料點")
    
    def _flush_loop(self):
        """背景批量刷盤"""
        batch = []
        while self._running or not self.queue.empty():
            try:
                item = self.queue.get(timeout=1.0)
                batch.append(item)
                
                if len(batch) >= self.batch_size:
                    self._flush(batch)
                    batch = []
            except Exception:
                if batch:
                    self._flush(batch)
                    batch = []
    
    def _flush(self, batch: list):
        """執行批量寫入"""
        try:
            for table, symbols, columns in batch:
                self.sender.row(table, symbols=symbols, columns=columns,
                               at=Sender.current_timestamp_with_nanos())
            self.sender.flush()
            print(f"刷盤 {len(batch)} 條資料")
        except IngressError as e:
            print(f"寫入失敗: {e}")
    
    def close(self):
        self._running = False
        self._worker.join(timeout=10)
        self.sender.close()

技巧4:Grafana視覺化整合

# docker-compose.yml: QuestDB + Grafana一體化部署
# 執行環境: Docker Compose v2.30+
version: "3.8"
services:
  questdb:
    image: questdb/questdb:8.2
    ports:
      - "9000:9000"
      - "9009:9009"
      - "9003:9003"
    volumes:
      - questdb_data:/root/.questdb
    environment:
      QDB_MALLOC_SIZE: 4G

  grafana:
    image: grafana/grafana:11.4
    ports:
      - "3000:3000"
    environment:
      GF_INSTALL_PLUGINS: grafana-postgresql-datasource
    volumes:
      - grafana_data:/var/lib/grafana
    depends_on:
      - questdb

volumes:
  questdb_data:
  grafana_data:

技巧5:多活寫入與高可用

# TDengine 3.x 叢集部署
# 執行環境: TDengine 3.3+ / 3節點叢集
cluster: 1
numOfMnodes: 3
mnodeEqualVnodeNum: 4
statusInterval: 1
maxTablesPerVnode: 1000
minRowsPerBlock: 100
maxRowsPerBlock: 4096

三大時序資料庫對比分析

維度 QuestDB InfluxDB TDengine
查詢語言 SQL(擴展時序函數) Flux / SQL(3.x) SQL方言
寫入協議 ILP / REST / PostgreSQL wire ILP / REST REST / WebSocket / JNI
單節點寫入 150萬點/秒 50萬點/秒 200萬點/秒
壓縮率 8-10x 4-6x 10-15x
叢集方案 企業版 開源叢集(3.x) 開源叢集
Grafana整合 ✅ PostgreSQL資料來源 ✅ 原生外掛 ✅ 原生外掛
Kafka整合 ✅ 社群 ✅ 原生 ✅ 原生
學習成本 低(SQL即學即用) 中高(Flux語法獨特) 中(超級表概念)
國產化適配 ✅ 信創相容
適用規模 中小→大型 中小型 中大→超大規模

總結

IoT時序資料庫選型沒有銀彈,關鍵看你的場景匹配度:

  • QuestDB:SQL團隊首選,高頻寫入+即時分析場景,學習成本最低,但叢集版需商業授權
  • InfluxDB:DevOps監控生態最成熟,Telegraf+Grafana一條龍,但Flux學習曲線陡、3.x遷移成本高
  • TDengine:超大規模IoT殺手,超級表+壓縮率碾壓,國產化友善,但AGPL協議需注意

選型決策樹:SQL團隊→QuestDB / DevOps生態→InfluxDB / 超大規模IoT+國產化→TDengine。

線上工具推薦

本站提供瀏覽器本地工具,免註冊即可試用 →

#时序数据库#IoT数据#QuestDB#InfluxDB#TDengine#2026#数据库