时序数据库与日志存储
问题
什么是时序数据库?适合存储什么类型的数据?
答案
时序数据特点
时序数据是按时间顺序记录的数据点序列,具有以下特点:
- 写多读少(持续写入大量数据)
- 很少更新或删除
- 查询通常按时间范围和聚合
- 数据有时效性(旧数据可归档/删除)
适用场景
| 场景 | 数据类型 |
|---|---|
| 监控指标 | CPU、内存、请求延迟 |
| IoT 传感器 | 温度、湿度、位置 |
| 前端埋点 | PV/UV、性能指标 |
| 金融行情 | 股价、交易量 |
| 日志分析 | 应用日志、访问日志 |
常见方案对比
| 方案 | 类型 | 特点 | 适用 |
|---|---|---|---|
| InfluxDB | 时序数据库 | 简单易用、SQL-like | 监控指标 |
| ClickHouse | 列式分析库 | 超快聚合查询 | 日志分析、BI |
| TimescaleDB | PG 扩展 | 兼容 SQL | 已有 PG 生态 |
| Prometheus | 监控系统 | Pull 模型 + AlertManager | 基础设施监控 |
| Elasticsearch | 搜索引擎 | 全文搜索 + 聚合 | 日志检索 |
前端监控数据存储
metrics-storage.ts
// 前端性能指标入库(ClickHouse)
interface PerformanceMetric {
timestamp: Date;
page_url: string;
fcp: number; // First Contentful Paint
lcp: number; // Largest Contentful Paint
cls: number; // Cumulative Layout Shift
ttfb: number; // Time to First Byte
user_agent: string;
country: string;
}
// ClickHouse 建表(列式存储 + 按日期分区)
const createTableSQL = `
CREATE TABLE web_vitals (
timestamp DateTime,
page_url String,
fcp Float32,
lcp Float32,
cls Float32,
ttfb Float32,
user_agent String,
country LowCardinality(String)
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(timestamp)
ORDER BY (page_url, timestamp)
TTL timestamp + INTERVAL 90 DAY -- 90 天自动清理
`;
// 聚合查询:每小时平均 LCP
const query = `
SELECT
toStartOfHour(timestamp) AS hour,
page_url,
avg(lcp) AS avg_lcp,
quantile(0.75)(lcp) AS p75_lcp,
quantile(0.95)(lcp) AS p95_lcp,
count() AS sample_count
FROM web_vitals
WHERE timestamp >= now() - INTERVAL 24 HOUR
GROUP BY hour, page_url
ORDER BY hour DESC
`;
常见面试问题
Q1: 为什么不用 MySQL 存时序数据?
答案:
- MySQL 按行存储,聚合查询需要扫描大量无关列
- 数据量大时写入性能下降(索引维护)
- 缺少自动数据过期(TTL)
- 缺少时序专用聚合函数
Q2: ClickHouse 为什么查询这么快?
答案:
- 列式存储:只读需要的列,减少 IO
- 向量化执行:批量处理数据
- 数据压缩:同列数据压缩率极高
- 稀疏索引:分区 + 排序键快速定位
Q3: ELK Stack 是什么?
答案:
- Elasticsearch:分布式搜索和分析引擎
- Logstash:数据收集和处理管道
- Kibana:可视化仪表板
常用于日志收集和分析。现代替代方案:Loki(Grafana 生态)。