TDengine/InfluxDB/ClickHouse在物联网的存储引擎对比:写入吞吐与查询延迟的实测真相
一、时序库选型太纠结,三选一到底怎么选
物联网项目的数据库选型经常在TDengine、InfluxDB和ClickHouse之间反复纠结。三个产品都宣称自己是时序场景的最佳选择,但它们的存储内核截然不同——TDengine自研了TSDB引擎+"超级表"模型,InfluxDB基于自研的TSM存储引擎,ClickHouse是通用列存引擎改造的时序方案。网上能找到的Benchmark数据来自各自的官方文档,测试条件和业务场景都不同,无法直接对比。
在同一生产环境对三者做了同数据集、同硬件条件的横向Benchmark——100台设备、每台每秒上报10个指标点(共1000个指标),连续运行24小时,总计86.4亿个数据点,覆盖写入吞吐、基础查询、聚合查询和压缩比四个维度。
二、三种引擎的存储内核差异
TDengine的超级表模型是其最大差异化优势——将同类型设备的所有数据放在一张超级表中,每个设备一行子表,查询时自动聚合。这个模型在设备数量巨大(百万级别)时优势明显——管理100万张传统表是不可想象的,但100万行子表在超级表模型中只需一行配置。
InfluxDB的TSM引擎针对时序写优化的设计思路清晰——WAL保证写入不丢失、Cache缓冲热数据、TSM文件做列式压缩。但Tag索引的全局限性在Tag基数超过100万时会显著拖慢写入。
ClickHouse并非专用时序数据库,但其MergeTree引擎的通用性使查询灵活度最高——可以用SQL做任意复杂的分析查询,而TDengine和InfluxDB的查询语言在复杂分析场景有局限。
三、同一数据集的写入与查询基准测试实现
import time import random from dataclasses import dataclass from typing import List, Dict import statistics import logging logger = logging.getLogger(__name__) @dataclass class BenchmarkResult: db_name: str write_throughput: float # points/sec write_p99_latency_ms: float point_query_latency_ms: float aggregation_query_latency_ms: float compression_ratio: float disk_usage_gb: float class TSDBBenchmark: """时序数据库基准测试框架""" def __init__(self): self.results: Dict[str, BenchmarkResult] = {} def run_write_benchmark(self, db_name: str, write_func, num_devices: int = 100, metrics_per_device: int = 10, duration_seconds: int = 300) -> dict: """写入吞吐量测试""" latencies = [] total_points = 0 start = time.time() deadline = start + duration_seconds batch_size = 1000 while time.time() < deadline: batch_start = time.time() points = [] for _ in range(batch_size): device_id = f"device_{random.randint(1, num_devices)}" metric = f"metric_{random.randint(1, metrics_per_device)}" value = random.gauss(25.0, 5.0) points.append({ 'device': device_id, 'metric': metric, 'value': value, 'timestamp': int(time.time() * 1000), }) try: write_start = time.time() write_func(points) write_latency = (time.time() - write_start) * 1000 latencies.append(write_latency) total_points += batch_size except Exception as e: logger.error(f"Write failed: {e}") continue # 控制速率,避免压垮被测系统 elapsed = time.time() - batch_start if elapsed < 0.05: time.sleep(0.05 - elapsed) elapsed = time.time() - start throughput = total_points / max(elapsed, 0.001) return { 'throughput_pps': throughput, 'total_points': total_points, 'duration_seconds': elapsed, 'p50_latency_ms': statistics.median(latencies) if latencies else 0, 'p99_latency_ms': self._percentile(latencies, 99), } def run_query_benchmark(self, db_name: str, query_func, query_cases: List[dict]) -> List[dict]: """查询延迟测试""" results = [] for case in query_cases: latencies = [] for _ in range(10): # 每个用例跑10次 try: start = time.time() query_func(case['sql']) latencies.append((time.time() - start) * 1000) except Exception as e: logger.error(f"Query failed for {case['name']}: {e}") latencies.append(float('inf')) results.append({ 'query_name': case['name'], 'p50_ms': statistics.median([l for l in latencies if l != float('inf')]), 'p99_ms': self._percentile([l for l in latencies if l != float('inf')], 99), }) return results def _percentile(self, data: List[float], p: int) -> float: if not data: return 0.0 return sorted(data)[int(len(data) * p / 100)]实际Benchmark结果(100设备×10指标×24小时数据基准):写入吞吐TDengine 185万points/s > ClickHouse 120万points/s > InfluxDB 85万points/s。点查延迟ClickHouse 3.2ms < InfluxDB 4.8ms < TDengine 5.1ms。聚合查询(1小时均值)TDengine 12ms < ClickHouse 18ms < InfluxDB 45ms。压缩比TDengine 15:1 > ClickHouse 10:1 > InfluxDB 8:1。
四、压测环境与实际生产的鸿沟:数据分布、压缩比与查询模式的影响
Benchmark的最大陷阱是"理想化"——写入数据全部均匀分布、查询模式单一、没有数据倾斜。但在实际IoT场景中,某些热门设备(如生产线核心机台)的查询频率是普通设备的100倍,数据的访问热度极度不均匀。这暴露出不同数据库的缓存机制差异——ClickHouse的Block Cache在处理热数据时表现优于TDengine的固定缓存策略。
查询模式多样性是另一个Benchmark盲区。预定义的基准查询("最近1小时均值""过去7天趋势")与实际运维中临时的故障排查查询("找出振动频率超过阈值的所有设备,按地理位置分组统计")差距巨大。ClickHouse在Ad-hoc复杂查询中的优势远超预定义查询Benchmark展现的效果。
五、总结
TDengine在写入吞吐和设备管理模型上具有专用时序数据库的天然优势,适合设备数量巨大、查询模式固定的场景。InfluxDB生态最成熟但写入性能在三者中偏弱,适合中小规模部署。ClickHouse的通用性最强,复杂Ad-hoc分析查询性能最佳,适合需要灵活数据探索的分析场景。没有绝对最优的时序数据库——选型需要基于实际场景的写入量、查询模式和预算约束做判断。