跳到内容

CSP + JS 完整性监控做进 InfluxDB 后,线上直接炸了

千万月活 SaaS 的安全监控系统,选 InfluxDB 存储上报数据。测试环境 3 天上线秒过,上线后 Node 内存暴涨、查询卡死。根本原因:时序数据库不是这么用的。

作者hopes76 分钟InfluxDB, 时序数据库, CSP, 架构设计, cardinality, SaaS

起因

前段时间我们想做一套前端安全监控:

  • CSP(Content Security Policy)违规监控
  • 自有 JS 完整性校验
  • 第三方 JS hash 校验
  • 风险脚本告警

业务是 SaaS,多国家站点,所有站点加起来月活接近千万。

需求听上去不复杂:用户每次打开页面,检查 CSP 合规、校验 JS hash,异常就直接上报。然后做趋势分析、风险统计、实时监控、异常检索。

我们选了 InfluxDB。原因非常朴素:

"时序数据库快。"

老实说,当时我对时序数据库并不熟。但现在 AI 开发速度太快了——架构还没想清楚,系统已经跑起来了。

整个系统几乎都是 Codex 写的。数据结构、InfluxDB 写入、dashboard、聚合查询、前端 hash 校验、查询接口,全部 AI 出品。

第一版,3 天上线。测试环境,一切正常。

然后上线。

直接炸了。

第一版:内存聚合的灾难

最开始我以为是查询写得太烂。

因为第一版的逻辑其实非常简单粗暴:把 raw data 全部拉出来,Node.js 内存聚合。

测试环境完全没问题。数据量小,用户少,cardinality 很低。

但线上根本不是一个世界。

上线大概 20 分钟,告警就开始响了:

  • Node 内存暴涨
  • CPU 飙升
  • dashboard 卡死
  • 查询越来越慢

后来才发现,真正慢的不是 InfluxDB,而是我自己把 raw data 全拉出来在内存里算。

于是马上改第二版。

第二版:还是挂了

这次做了几件事:

  • 尽量使用 InfluxDB 原生聚合函数
  • 减少 Node 内存计算
  • 增加原始查询优化
  • 重新设计查询逻辑

结果还是挂了。

而且这次更诡异——有些查询几分钟都查不出来。

这时候我开始怀疑:InfluxDB 是不是不行?

后来我才意识到,不是 InfluxDB 不行,而是:

我在拿时序数据库做高维检索。

时序数据库到底解决什么问题

很多人第一次接触 InfluxDB、Prometheus、OpenTSDB,都会有一个误区:觉得既然是数据库,应该也能像 MySQL 一样查数据。

但时序数据库真正解决的问题是:

某个指标,随着时间变化是什么样子。

它擅长的是:

  • CPU 使用率变化趋势
  • 错误率趋势
  • QPS 波动
  • 时间桶聚合
  • 监控指标告警

也就是 Time Series Analysis。

而不是:

  • 任意条件组合检索
  • 多维度筛选
  • 分页查询
  • 明细记录查某一条

但我重新审视自己的需求后发现,我真正要做的是:

  • 查某个 userId
  • 查某个 scriptUrl
  • 查某个 hash
  • 查某个 pageUrl
  • 多条件组合筛选
  • 风险排查
  • 分页
  • 明细检索

这已经不是"时序分析"了,更像 风控系统、日志检索、OLTP 查询系统。

真正的深渊:Series Cardinality

真正的问题,是在我开始理解 InfluxDB 里 tag 和 field 到底意味着什么的时候出现的。

最开始我以为:

  • tag = 有索引
  • field = 普通字段

后来发现完全不是这么回事。

InfluxDB 的 tag 不只是"有索引的字段"——它实际上是 series identity 的一部分。数据写入的时候,InfluxDB 就已经决定了 series structure。

这和 MySQL 完全不是一个世界。在 MySQL 里,今天没索引,明天 ALTER TABLE ADD INDEX 就行。但 InfluxDB 不是——tag 一旦设计错,后面通常意味着 backfill、rewrite、rebuild index,基本等于重建整个时序结构。

75 亿 Series

然后我开始真正研究什么叫 series cardinality。

InfluxDB 会把 measurement + tag set 组合成一个唯一 series。

比如:

measurement=csp_report
siteCode=us
env=prod
userId=123
scriptUrl=a.js
pageUrl=/checkout
hash=xxxx

这一整套就是一个 series。

换一个 userId,新的 series。换一个 hash,又是新的 series。

如果你熟悉 InfluxDB,看到 userId 和 hash 放进 tag,可能已经开始笑了。

然后我算了一下:

字段 数量
siteCode 30
userId 100,000
hash 500
scriptUrl 50
pageUrl 100

理论 series:

30 × 100,000 × 500 × 50 × 100 = 7,500,000,000

75 亿。

我第一次算出来的时候,甚至怀疑自己算错了。重新按了几遍计算器,发现没错。就是 75 亿 series。

那一刻我突然意识到,InfluxDB 最恐怖的地方不是某个字段特别大,而是 多个"看起来不大"的维度,组合后直接指数爆炸。

InfluxDB 最贵的资源不是数据量

更离谱的是,我们一天的数据其实只有 170 万 rows。

说实话,这个量级并不算夸张。MySQL 分表、ClickHouse、Elasticsearch,其实都还能打。

但问题在于,InfluxDB 最贵的资源从来不是数据量,而是 series cardinality。

很多人第一次接触 InfluxDB 时会误以为 series ≈ 数据量。其实完全不是。

series 更像 索引空间。InfluxDB 会为每一个 series:

  • 分配 series id
  • 建立 inverted index
  • 建立 metadata
  • 建立 tag index

所以真正吃资源的不是数据本身,而是 series metadata。

后来我才明白,为什么 InfluxDB 官方一直强调:不要把 userId、hash、uuid、requestId、random string 这种高离散字段放进 tag。因为这些字段会直接把 series cardinality 打爆。

为什么查询越来越慢

高 cardinality 下,InfluxDB 查询不是简单的"扫数据",而是 扫海量 series metadata。

尤其:

  • tag filter
  • group by
  • series expansion

这些操作会导致:

  • index scan 成本急剧增加
  • shard traversal 跨分片扫描
  • mmap page fault 频繁触发

这不是"优化查询"能解决的问题。这是底层架构选错了。

最终方案:InfluxDB + MySQL 双存储

最后整个系统重新拆了。

InfluxDB 继续保留,但只负责它擅长的事:

  • 趋势分析
  • count / sum 聚合
  • TOP N 排行
  • 时间桶聚合
  • 告警趋势

MySQL 负责检索:

  • 明细查询
  • userId 检索
  • hash 检索
  • 多条件筛选
  • 风险排查
  • 分页

也就是:

InfluxDB 做趋势,MySQL 做检索。

最终的 Tag / Field 设计

改造后的 InfluxDB measurement 只保留低基数的 tag:

# Tags(低基数,参与 series 定义)
siteCode    → 约 30 个值
env         → prod / staging / dev
type        → csp_violation / js_integrity / third_party_hash / risk_script

# Fields(高基数,不参与 series 定义)
reportCount → integer(聚合用)
riskLevel   → string(高/中/低)

所有高基数字段(userId、hash、scriptUrl、pageUrl)全部只写进 MySQL。InfluxDB 只关心"某种类型的异常,今天发生了多少次,趋势如何"。

写入层面做了双写:一条数据同时写 InfluxDB(聚合用)和 MySQL(明细用)。写入一致性靠应用层重试 + MySQL 事务保证,InfluxDB 少写几条对趋势影响不大。

后来我才真正理解,判断一个系统是不是适合时序数据库,不要看"数据里有没有时间字段",而要看:

你到底是在看趋势,还是在查记录。

这是两个完全不同的问题。

回头看

整个项目其实特别像 AI 时代的软件开发缩影。

Codex 帮我在 3 天内完成了整个系统。测试环境秒过。开发速度快得离谱。

但它不会提醒我:

30 × 100,000 × 500 × 50 × 100 = 7,500,000,000

AI 可以极大加速开发。但系统最终还是要遵守底层架构规律。

速度再快,方向错了也是白搭。

#InfluxDB#时序数据库#CSP#架构设计#cardinality#SaaS