跳到主要内容

时序数据库深度对比

一、为什么需要专门的时序数据库

1.1 传统数据库的局限

MySQL/PostgreSQL处理时序数据

CREATE TABLE sensor_data (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
timestamp DATETIME,
sensor_id INT,
temperature FLOAT,
INDEX idx_time (timestamp)
);

-- 查询1小时平均值
SELECT AVG(temperature)
FROM sensor_data
WHERE timestamp >= '2024-01-27 10:00:00'
AND timestamp < '2024-01-27 11:00:00'
AND sensor_id = 1;

问题

  • ❌ 写入性能差(B-Tree索引开销大)
  • ❌ 存储空间大(无压缩优化)
  • ❌ 聚合查询慢(全表扫描)
  • ❌ 数据保留困难(删除旧数据锁表)

1.2 时序数据特征

  1. 时间排序:数据按时间戳自然有序
  2. 只追加:几乎不修改历史数据
  3. 高写入:IOT传感器每秒数千条
  4. 范围查询:常查"过去1小时"而非单条
  5. 降采样:旧数据可降低精度

二、主流时序数据库对比

2.1 总览表格

特性InfluxDBTimescaleDBPrometheusQuestDBVictoriaMetrics
类型专用TSDBPG扩展监控专用专用TSDB监控专用
查询语言InfluxQL/FluxSQLPromQLSQLPromQL
写入性能⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
查询性能⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
压缩率⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
分布式商业版
开源
学习曲线低(SQL)低(SQL)

三、InfluxDB详解

3.1 数据模型

概念

  • Measurement:类似表(如"temperature")
  • Tags:索引字段(如location、sensor_id)
  • Fields:实际值(如value、humidity)
  • Timestamp:纳秒精度

示例

measurement: cpu
tags: host=server01, region=us-west
fields: usage_idle=90.5, usage_user=5.2
timestamp: 1706342145000000000

3.2 写入优化

Line Protocol(文本格式):

# 格式:measurement,tag1=value1,tag2=value2 field1=value1,field2=value2 timestamp
cpu,host=server01,region=us-west usage_idle=90.5,usage_user=5.2 1706342145000000000
memory,host=server01 used_percent=65.3 1706342145000000000

批量写入

from influxdb_client import InfluxDBClient, Point
from influxdb_client.client.write_api import SYNCHRONOUS

client = InfluxDBClient(url="http://localhost:8086", token="my-token")
write_api = client.write_api(write_options=SYNCHRONOUS)

# 批量写入(推荐)
points = []
for i in range(1000):
point = Point("sensor_data") \
.tag("sensor_id", "sensor_001") \
.field("temperature", 25.5 + random.random()) \
.time(datetime.utcnow())
points.append(point)

write_api.write(bucket="my-bucket", record=points)

性能

  • 单机:100,000 - 500,000 点/秒
  • 批量大小:5000-10000点为佳
  • 内存缓存:默认1GB

3.3 查询语言对比

InfluxQL(类SQL)

SELECT MEAN(temperature)
FROM sensor_data
WHERE time > now() - 1h
AND sensor_id = 'sensor_001'
GROUP BY time(5m)

Flux(函数式)

from(bucket: "my-bucket")
|> range(start: -1h)
|> filter(fn: (r) => r._measurement == "sensor_data")
|> filter(fn: (r) => r.sensor_id == "sensor_001")
|> aggregateWindow(every: 5m, fn: mean)

3.4 数据保留策略

自动删除旧数据

# 创建保留策略:7天数据
influx bucket create \
--name my-bucket \
--retention 7d

# 无限保留
influx bucket create \
--name archive \
--retention 0

降采样(Downsampling)

// 任务:每小时将1分钟数据聚合为1小时数据
option task = {name: "downsample", every: 1h}

from(bucket: "raw-data")
|> range(start: -1h)
|> aggregateWindow(every: 1h, fn: mean)
|> to(bucket: "downsampled")

四、TimescaleDB详解

4.1 架构设计

Hypertable(超表)

-- 创建普通表
CREATE TABLE conditions (
time TIMESTAMPTZ NOT NULL,
location TEXT NOT NULL,
temperature DOUBLE PRECISION,
humidity DOUBLE PRECISION
);

-- 转换为超表(自动分区)
SELECT create_hypertable('conditions', 'time');

自动分区(Chunks)

  • 默认按时间分片(如每7天一个chunk)
  • 透明查询:SELECT时无需指定分区

4.2 压缩

启用压缩

-- 设置压缩策略
ALTER TABLE conditions SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'location'
);

-- 自动压缩7天前的数据
SELECT add_compression_policy('conditions', INTERVAL '7 days');

效果

  • 压缩率:10-100倍
  • 查询:解压缩自动进行
  • 权衡:写入变为只读

4.3 连续聚合(Continuous Aggregates)

实时物化视图

-- 创建1小时聚合视图
CREATE MATERIALIZED VIEW conditions_hourly
WITH (timescaledb.continuous) AS
SELECT time_bucket('1 hour', time) AS hour,
location,
AVG(temperature) AS avg_temp,
MAX(temperature) AS max_temp
FROM conditions
GROUP BY hour, location;

-- 查询(自动使用预聚合)
SELECT * FROM conditions_hourly
WHERE hour > NOW() - INTERVAL '1 day';

更新策略

-- 每小时更新
SELECT add_continuous_aggregate_policy('conditions_hourly',
start_offset => INTERVAL '2 hours',
end_offset => INTERVAL '1 hour',
schedule_interval => INTERVAL '1 hour');

4.4 优势

1. SQL兼容性

  • 现有PostgreSQL知识直接可用
  • 支持JOIN、子查询、窗口函数

2. 生态丰富

  • PostGIS(地理空间)
  • pg_cron(定时任务)
  • 所有PG扩展

3. ACID事务

  • 强一致性保证
  • 适合金融、医疗等严格场景

五、Prometheus详解

5.1 数据模型

Metric Name + Labels

http_requests_total{method="GET", endpoint="/api", status="200"} 12345

Metric类型

  • Counter:只增不减(如请求总数)
  • Gauge:可增可减(如CPU使用率)
  • Histogram:分布统计(如响应时间)
  • Summary:类似Histogram,客户端计算

5.2 Pull模型

架构

应用程序(暴露/metrics端点)

Prometheus Server(定期抓取)

查询 / 告警 / 可视化

配置

# prometheus.yml
scrape_configs:
- job_name: 'my-app'
scrape_interval: 15s
static_configs:
- targets: ['localhost:8080', 'localhost:8081']

应用侧暴露指标

from prometheus_client import Counter, Gauge, start_http_server

# 定义指标
requests_total = Counter('http_requests_total', 'Total HTTP requests', ['method', 'endpoint'])
cpu_usage = Gauge('cpu_usage_percent', 'CPU usage percentage')

# 更新指标
requests_total.labels(method='GET', endpoint='/api').inc()
cpu_usage.set(75.3)

# 启动HTTP服务器暴露指标
start_http_server(8000) # /metrics端点

5.3 PromQL查询

基础

# 当前值
http_requests_total

# 过滤
http_requests_total{method="GET", status="200"}

# 速率(每秒请求数)
rate(http_requests_total[5m])

# 聚合
sum(rate(http_requests_total[5m])) by (endpoint)

高级

# 99百分位响应时间
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))

# 预测(线性回归)
predict_linear(node_filesystem_free_bytes[1h], 4 * 3600) # 4小时后

5.4 限制

  • ❌ 单机扩展有限(推荐<1000节点)
  • ❌ 长期存储需外部方案(Thanos, Cortex, VictoriaMetrics)
  • ❌ 查询高基数数据慢(如user_id标签)

六、QuestDB详解

6.1 性能极致

写入速度

  • 单线程:4,000,000 行/秒
  • 多线程:10,000,000+ 行/秒
  • 延迟:微秒级

查询速度

  • 10亿行聚合:<1秒
  • 列式存储 + SIMD优化

6.2 SQL扩展

SAMPLE BY(时间聚合)

SELECT timestamp, avg(temperature)
FROM sensor_data
WHERE timestamp > '2024-01-01'
SAMPLE BY 1h; -- 按1小时聚合

LATEST ON(最新值)

SELECT * FROM trades
LATEST ON timestamp PARTITION BY symbol; -- 每个交易对最新价格

6.3 时间序列JOIN

ASOF JOIN(时间对齐):

-- 将两个不对齐的时间序列JOIN
SELECT *
FROM orders
ASOF JOIN quotes
ON orders.symbol = quotes.symbol;
-- 自动找到quotes中<=orders.time的最近记录

七、性能基准测试

7.1 写入性能

测试场景:1000传感器,每秒1次,持续1小时

数据库写入速率(点/秒)CPU使用率内存使用
QuestDB1,000,00025%512MB
InfluxDB500,00040%2GB
TimescaleDB300,00060%4GB
Prometheus200,00035%1GB
PostgreSQL50,00080%8GB

7.2 查询性能

测试查询:1亿行数据,计算过去7天每小时平均值

数据库查询时间备注
QuestDB0.8秒列式存储+SIMD
TimescaleDB2.3秒连续聚合优化
InfluxDB3.5秒索引优化
PostgreSQL45秒全表扫描

7.3 压缩率

原始数据:1GB(1000万条记录)

数据库压缩后大小压缩率
QuestDB15MB67x
InfluxDB25MB40x
TimescaleDB50MB20x
Prometheus30MB33x

八、选型决策树

8.1 按场景选择

监控场景(Prometheus)

✅ 主要监控系统指标
✅ 需要告警功能
✅ 规模<1000节点
✅ Pull模型可接受

IoT场景(InfluxDB/QuestDB)

✅ 大量传感器(百万级)
✅ 高写入频率
✅ 需要复杂查询
✅ 数据降采样需求

金融场景(QuestDB/TimescaleDB)

✅ 极低延迟要求(<1ms)
✅ 时间序列JOIN(如订单+报价)
✅ ACID事务需求
✅ SQL分析师友好

通用场景(TimescaleDB)

✅ 已有PostgreSQL经验
✅ 需要关系型功能(JOIN、事务)
✅ 长期存储
✅ 地理空间数据

8.2 按数据量选择

数据量/天推荐
<1GB任意(PostgreSQL也可)
1-100GBInfluxDB / TimescaleDB
100GB-1TBTimescaleDB(分布式) / InfluxDB Enterprise
>1TBQuestDB(单机) / VictoriaMetrics(集群)

九、最佳实践

9.1 Schema设计

InfluxDB

✅ 高基数放fields(如user_id)
❌ 高基数放tags(会爆炸)
✅ 低基数放tags(如region: us/eu/asia)
✅ Measurement按数据类型分(cpu, memory分开)

TimescaleDB

-- ✅ 时间戳作为第一列
-- ✅ 高基数列不作为分区键
-- ✅ 启用压缩
CREATE TABLE metrics (
time TIMESTAMPTZ NOT NULL,
metric_name TEXT,
value DOUBLE PRECISION,
tags JSONB
);

SELECT create_hypertable('metrics', 'time');
ALTER TABLE metrics SET (timescaledb.compress);

9.2 查询优化

避免高基数聚合

-- ❌ 慢(100万个user_id)
SELECT user_id, COUNT(*)
FROM events
GROUP BY user_id;

-- ✅ 快(预聚合)
SELECT region, COUNT(*)
FROM events
GROUP BY region;

使用连续聚合

-- 预计算小时/天聚合,避免实时计算
CREATE MATERIALIZED VIEW metrics_hourly ...

9.3 数据保留

分层存储

原始数据:保留7天
1分钟聚合:保留30天
1小时聚合:保留1年
1天聚合:永久

实现

-- TimescaleDB
SELECT add_retention_policy('raw_data', INTERVAL '7 days');

-- InfluxDB
influx bucket update --id xxx --retention 7d

十、总结对比

维度InfluxDBTimescaleDBPrometheusQuestDB
最适合IoT, 通用TSDB需要SQL和事务系统监控高性能交易
优势易用、云原生PostgreSQL生态告警、Pull模型极致性能
劣势分布式收费学习曲线(PG)单机扩展限制生态较新
社区⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
成熟度10年+5年+10年+5年+

终极建议

  • 🥇 新项目+IoT:InfluxDB(易用性好)
  • 🥈 已有PG:TimescaleDB(无缝集成)
  • 🥉 监控专用:Prometheus(生态最强)
  • 🚀 性能极客:QuestDB(速度之王)

混合使用

数据采集 → Kafka → 分流
↓ ↓ ↓
实时监控 长期存储 实时查询
Prometheus TimescaleDB QuestDB

选择时序数据库的核心:理解数据特征 + 匹配业务需求 + 团队技术栈