先说一个判断:几千台设备把 MySQL 拖垮,大多数时候不是 MySQL 配置不够、SQL 写得太差,而是存储模型和数据访问模式不匹配。时序数据是“写多读少、追加为主、按时间扫描、需要聚合”的数据,天生适合列式存储、时间分区和专用压缩,而不是以 B+ 树为核心的行式存储。这篇文章不打算去讨论怎么抢救 MySQL,而是把时序库选型重新捋一遍:为什么 MySQL 扛不住、时序数据的存储诉求是什么、主流时序库各自擅长什么、针对几千台设备的规模到底怎么选、以及落地时有哪些坑。读完你会得到一份可以直接照做的选型清单和最小可运行的接入链路。
1. 故障现场:MySQL 是怎么被几千台设备拖垮的
先还原一个很有代表性的场景。设备管理平台早期只有几百台设备,每台设备每 30 秒上报一次温湿度、电量、状态等指标,MySQL 单库单表也能跑,慢查询偶尔有,但不影响业务。随着设备数量涨到几千台,上报频率从 30 秒缩短到 5 秒,问题开始接二连三地出现。
1.1 第一阶段:写入先撑不住
设备上报本质上是一条高频 INSERT。几十台上百台设备时,MySQL 可以轻松处理;但几千台设备、每台设备每次带多个指标,写入 TPS 会迅速涨到每秒几千甚至上万。此时最容易出现的现象是:
- InnoDB 的写事务堆积,
INSERT执行时间从几毫秒涨到上百毫秒; - 应用侧连接池被打满,报“连接数不够”或“获取连接超时”;
- 主从同步延迟越来越大,从库读到的数据永远落后。
这个阶段多数团队会尝试加索引、加缓存、升级实例规格,确实能缓解几天,但很快又会打回原形。
1.2 第二阶段:查询也全面劣化
写入慢只是表象,查询慢才是让业务真正不可用的原因。监控页面要画某个设备最近 24 小时的曲线,后端通常执行类似SELECT ... WHERE device_id = ? AND ts >= ?的查询。设备多、表数据量大之后,这条语句的问题就暴露了:
- 复合索引
(device_id, ts)在千万级行规模下依然要回表; - 聚合类查询,比如按小时统计所有设备的平均温度,往往要扫描大量行,甚至触发全表扫描;
- 多个在线用户同时看大盘,慢查询互相争抢 CPU 和 IO。
从故障现象看,第二阶段的特点就是:数据库 CPU 使用率居高不下,慢查询日志刷屏,应用接口超时,用户在页面上看到的曲线经常出现断点。
1.3 第三阶段:运维动作全部失效
走到这一步,DBA 能做的常规手段基本都试过了:
- 把
ts字段和device_id字段拆成单独索引,没用; - 按月分表,查询代码里要手动拼表名,排查问题变得很痛苦;
- 定期清理历史数据,但
DELETE在亿级表上会造成大事务、锁竞争、binlog 膨胀,运维窗口根本切不出来; - 把监控类数据挪到 Redis,热数据能顶住,冷数据和聚合查询依然绕回 MySQL。
最后的结论很直接:单靠优化 MySQL 已经无法解决这个量级下的时序数据存储问题,必须换一种存储底座。
2. 为什么时序数据存进 MySQL 会越跑越慢
要理解选型,得先理解两类存储的底层差异。
2.1 时序数据到底有哪些特征
时序数据是一组随时间变化的数据点,比如设备温度在某个时间戳下的值。它有四个很明显的特征:
- 写入模式固定:数据只追加,很少修改;
- 时间维度强相关:几乎所有查询都带时间范围;
- 数据量增长快:点位多、频率高,数据天然持续膨胀;
- 热点集中在近期:越新的数据被读得越多,旧数据访问频率急剧下降。
这样的特征决定了它有较强的“分段管理”需求:数据可以按时间切分成 chunk,过期数据可以直接丢弃,不需要一条条 DELETE。这就是时序数据库的核心思路。
2.2 MySQL 的存储模型错在了哪里
MySQL 默认的行式存储面向的是“事务型负载”,核心设计目标是把一行数据完整地读出来、安全地写进去。它的根基是 B+ 树主键索引和二级索引。如果我们把温度、湿度、电压这些指标都存进一张宽表,问题非常明显:
- 每次插入都伴随二级索引更新。
(device_id, ts)复合索引每次写入都需要重新平衡 B+ 树节点,数据量越大,树越高,写入放大越严重; - 行式存储下,一个 chunk 里可能包含不同设备、不同时间的数据,做聚合时要逐行扫描,缓存命中率很低;
- 时间数据没有天然的按时间分区机制,过期数据清理只能靠 DELETE,成本极高;
- 同一条记录里的温度、湿度等字段,批量分析时往往只用到其中一两个列,但行式存储必须把整行读出来。
简单说,MySQL 把大量精力花在了“保证一行数据的完整性和随机读写能力”上,而时序业务根本不关心随机更新,它只需要“快速追加 + 按时间扫描”。这种错配决定了 MySQL 在这种场景下不是慢一点的问题,而是会随着数据量线性甚至加速恶化。
2.3 为什么加索引和分表也不是出路
加索引只能缓解特定查询,救不了写入吞吐的瓶颈;分表解决了单个表过大的问题,却没有解决存储格式和压缩问题,而且让查询代码复杂度直线上升。分库分表方案本质上是在 MySQL 的存储模型外面打补丁,而不是从存储引擎层面适应时序数据。几千台设备这个规模,很多团队第一反应都是继续压榨 MySQL,结果就是反复救火,直到某一天某个报表查询把整个实例拖宕机。
3. 时序库选型的底层逻辑:先看数据访问模式
选型不能只看“哪个时序库功能最全”,而要先弄清你的数据访问模式,再反向匹配存储引擎。
3.1 时序库的基本数据模型
大部分时序库都采用类似的结构:
measurement/table:一类设备或一类指标;tags:可索引的维度字段,如device_id、region、group;fields:实际指标值,如温度、湿度、电压;timestamp:该条数据的时间戳。
这个模型本质上就是为“按设备维度 + 按时间范围”的查询设计的。写入时不需要频繁更新,查询时先按 tag 过滤,再按时间范围扫描连续存储块,效率远高于 MySQL 的二级索引随机读。
3.2 时序库要解决的核心问题
评估一个时序库是否适合你的场景,可以从五个维度看:
- 写入吞吐:能否稳定承接每秒几万到几十万点的写入,批量写入是否高效;
- 压缩率:同一条时间序列相邻数据通常变化很小,专用压缩算法能大幅降低磁盘占用;
- 查询性能:时间范围聚合、降采样、多设备对比是否做了预聚合或向量化优化;
- 生命周期管理:是否支持按时间自动分区、自动删除过期数据、自动降采样;
- 运维成本:是单机安装、分布式部署,还是托管服务,扩容和备份是否方便。
3.3 容易忽略的高基数陷阱
选型时还有一个关键指标经常被忽略:序列基数。时序库里每条独立序列由measurement + tags + 时间戳决定。如果每台设备上报 10 个指标,5000 台设备就有 5 万条序列;如果还带上多个 tag 组合,基数会成倍增长。高基数会严重影响索引内存和查询性能。选型前务必统计清楚:未来一年会有多少设备、多少点位、多少维度标签。
4. 主流时序数据库横评
这一部分不追求把所有产品列一遍,而是重点对比几类在工程中真正常用的方案。
4.1 InfluxDB:老牌时序库,功能完整但集群版门槛高
InfluxDB 是时序数据库的经典选择,采用 TSM 存储引擎,支持类 SQL 的 Flux/InfluxQL,生态成熟,和 Grafana、Telegraf 配合很好,适合快速搭建监控系统。单机版安装简单,功能丰富。需要注意的地方是:开源版单机部署的横向扩展能力有限,企业级集群版的授权和运维成本都不低。如果你的预期规模在几千台设备、单机写入能力足够覆盖,InfluxDB 是一个稳妥选项;如果预期未来要扩展到数十万设备,就要提前评估集群方案。
4.2 TimescaleDB:SQL 兼容的 PostgreSQL 扩展,迁移平滑
TimescaleDB 不是独立数据库,而是 PostgreSQL 扩展,底层仍然是 PostgreSQL。它通过“超表”概念按时间自动分 chunk,让时序数据的插入和查询能够利用分区块 + 压缩。最大的优点是你不需要学习新的查询语言,直接用标准 SQL 就能完成窗口函数、JOIN、时间桶聚合。如果团队已经熟 PostgreSQL,迁移成本极低,业务代码改动量小。缺点是需要自己维护 PostgreSQL 实例,数据压缩和高可用能力依赖 PG 的运维水平;在超大规模写压下,性能表现取决于 PG 调优水平。
4.3 TDengine:面向物联网场景的时序数据库,SQL 风格融合
TDengine 是国产时序数据库里比较有代表性的产品,主打“一个设备一张表 + 超级表”,将设备静态标签和数据分开存储。它的核心工作方式很贴合设备场景:先建超级表定义数据和标签结构,再为每个设备创建一张子表;查询时按标签聚合,底层按时间块扫描。相对 MySQL 和通用时序库,它提供了较高压缩率、内置 TTL 和数据保留策略,安装部署、分区管理也更接近一个独立数据库产品。需要注意它的 SQL 与 MySQL 有一定差异,尤其是建表和超级表的用法,团队需要一点点适应成本。从实际经验看,TDengine 比较适合 IoT 设备点位多、查询以“按设备聚合 + 按时间范围”为主的业务场景。
4.4 VictoriaMetrics:轻量高性能,适合 Prometheus 生态的长期存储
VictoriaMetrics 面向监控和指标场景,兼容 Prometheus 的 remote write 协议和查询 API。它最大的优势是资源占用低、写入吞吐高、单机也能扛较大规模,同时支持按时间的自动分区、数据压缩、租户隔离等能力。它的查询语言以 PromQL/MetricsQL 为主,而不是 SQL。如果你的数据已经通过 Prometheus 采集,或者你主要需要的是监控指标、告警数据,VictoriaMetrics 是一个非常值得优先考虑的对象。但它并不适合作为业务系统的通用时序存储,比如应用需要频繁按业务标签做复杂聚合时,它的通用性不如 InfluxDB 和 TimescaleDB。
4.5 Prometheus + Thanos:监控生态标准,扩展了长期存储和全局查询
Prometheus 本身是监控系统的标准用法,它存储的是拉取模型采集到的时序数据,查询用 PromQL。Thanos 在 Prometheus 之上加了长期存储和全局查询能力,把多个 Prometheus 实例的数据统一到对象存储,解决单机 Prometheus 的容量和 HA 问题。这套方案非常稳健,适合以监控为主、数据量从几十万点到上百万点的场景。但它的数据模型相对固定,不适合业务日志、明细数据、复杂 SQL 分析。严格说它不是“时序数据库选型”里的唯一答案,而是“监控数据存储架构”里的一种成熟路径。
4.6 横向对比与初步结论
从工程选型的角度看,没有绝对最好的时序库,只有最匹配需求的。下表是一个快速判断参考:
| 方案 | 数据模型 | 查询接口 | 部署复杂度 | 高可用扩展 | 适合场景 |
|---|---|---|---|---|---|
| InfluxDB | measurement+tags+fields | InfluxQL / Flux | 中 | 企业版集群 | 通用监控、中小规模时序分析 |
| TimescaleDB | 超表,时间分 chunk | 标准 SQL | 中 | 依赖 PostgreSQL | 已有 PG 团队、SQL 兼容、业务数据 |
| TDengine | 超级表+子表,标签分离 | 类 SQL | 低 | 集群商业版 | IoT 设备数据、点位多、聚合查询 |
| VictoriaMetrics | 与 Prometheus 兼容 | PromQL / MetricsQL | 低 | 单机或集群 | Prometheus 长期存储、大规模监控 |
| Prometheus+Thanos | 指标模型 | PromQL | 较高 | 多 Prometheus + 对象存储 | 监控告警生态,长期存储 |
这个表省略了很多参数,但能说明一个核心差异:InfluxDB 偏向通用时序分析,TimescaleDB 偏向 SQL 生态,TDengine 偏向 IoT 设备模型,VictoriaMetrics 偏向监控指标。
5. 针对几千台设备规模的选型建议
你不需要一上来就建设一个跨多机房的时序集群。几千台设备,假设每台设备上报 10 个指标,每秒写入点数也就是几万点这个量级,很多时序数据库的单机实例都可以覆盖。更关键的是判断你的业务形态属于哪一类。
5.1 按场景选择
如果你的核心诉求是“设备状态监控、指标巡检、告警曲线”,现有技术栈里已经有 Prometheus,那么优先考虑 VictoriaMetrics,它可以用最小代价补上长期存储能力。
如果你的核心诉求是“设备上报的业务数据要长期保留、按设备查询、生成报表”,那么 TDengine 或 TimescaleDB 更合适。TDengine 的设备模型贴合度高,TimescaleDB 则在 SQL 兼容性上占优。
如果你希望快速验证、团队不想引入太多新概念,InfluxDB 单机版是成本最低的入门选择,但要想清楚未来三年设备的增长曲线。
从这次选型复盘的实际感受看,我更倾向于把设备业务数据放入一个独立时序库,同时保留 MySQL 存储订单、用户、配置等事务型数据。也就是让 MySQL 回归它擅长的事务场景,让时序库承担持续写入和数据分析压力。
5.2 迁移路径设计
从 MySQL 切换到时序库,不建议一次性全量搬迁历史数据,而是做两件事:一是双写,在切换窗口内同时写入 MySQL 和时序库,比对两端最新数据的时间戳和点数;二是把历史数据归档,保留在 MySQL 中供低频查询,或直接按天导出到对象存储。等新链路稳定运行一个保留周期后,再彻底下线 MySQL 中的时序表。这个迁移路径能保证出问题时随时回滚,不会把业务压到不可逆状态。
6. 最小演练:用一个时序库跑通写入与查询链路
下面用一个最小可运行的例子演示,先写 MySQL 时代的表结构,再对比一个时序库的写法,最后给一个采集端批量写入示例。读者可以在自己的测试环境中直接复现。
6.1 先看 MySQL 时代的代码问题
我们通常会建这样一张表:
-- 文件路径:schema/mysql_device_metric.sql CREATE TABLE device_metric ( id BIGINT AUTO_INCREMENT PRIMARY KEY, device_id VARCHAR(32) NOT NULL, ts DATETIME NOT NULL, temperature FLOAT, humidity FLOAT, KEY idx_device_ts (device_id, ts) );每条上报对应一条 INSERT:
INSERT INTO device_metric (device_id, ts, temperature, humidity) VALUES ('dev001', NOW(), 36.5, 60.1);单看这条 SQL 本身没有太大问题,但当它变成每秒几千次的 INSERT,同时还有大量按device_id + ts的聚合查询时,InnoDB 的 B+ 树索引写放大和随机 IO 就会被放大。这就是前面说的存储模型与负载不匹配。
6.2 用 InfluxDB line protocol 写入
InfluxDB 使用 line protocol 格式,一行代表一个数据点:
# 写入一个数据点,注意逗号、空格和时间的精确格式 curl -i -XPOST 'http://localhost:8086/write?db=iot_monitor' \ --data-binary 'device_metric,device_id=dev001,region=bj temperature=36.5,humidity=60.1 1671888000000000000'这里的含义是:measurement为device_metric,tags为device_id=dev001,region=bj,fields为两个指标,时间戳是纳秒级 Unix 时间戳。InfluxDB 2.x 的 API 路径和鉴权方式不同,但 line protocol 本身是通用的。采用这种格式写入后,查询可以这样写:
SELECT MEAN("temperature") FROM "device_metric" WHERE time > now() - 1h AND "device_id" = 'dev001' GROUP BY time(5m)它会按 5 分钟一个桶返回该设备的平均温度,这正是监控图表需要的聚合结果。
6.3 用 TDengine 建库建表和批量写入
TDengine 的模型更接近“设备一张表”,先建数据库、超级表,再为设备建子表:
-- 建库,KEEP 表示保留时长,DURATION 表示分区时长,参数以实际版本为准 CREATE DATABASE iot_monitor KEEP 365 DURATION 30 BUFFER 256 WAL_LEVEL 2; -- 建超级表 CREATE STABLE device_metric ( ts TIMESTAMP, temperature FLOAT, humidity FLOAT ) TAGS ( device_id NCHAR(20), region NCHAR(20) ); -- 为具体设备创建子表并写入 INSERT INTO d001 USING device_metric TAGS ('dev001', 'bj') VALUES (now, 36.5, 60.1);TDengine 还支持一次批量写入多条:
INSERT INTO d001 USING device_metric TAGS ('dev001', 'bj') VALUES (now, 36.5, 60.1), d002 USING device_metric TAGS ('dev002', 'sh') VALUES (now, 37.2, 58.3);查询时按标签过滤和聚合:
SELECT AVG(temperature) FROM device_metric WHERE region = 'bj' AND ts >= now - 1d INTERVAL(10m);这种模型对几千台设备、每台设备多条指标的 IoT 场景非常友好。因为标签和时序数据分开存储,避免了大宽表带来的重复存储和扫描浪费。
6.4 用 TimescaleDB 保存原有 SQL 习惯
TimescaleDB 对 SQL 开发者最友好,安装扩展后建表就是标准 SQL:
CREATE EXTENSION IF NOT EXISTS timescaledb; CREATE TABLE conditions ( time TIMESTAMPTZ NOT NULL, device_id TEXT NOT NULL, temperature DOUBLE PRECISION, humidity DOUBLE PRECISION ); SELECT create_hypertable('conditions', 'time', chunk_time_interval => INTERVAL '1 day');写入可以直接沿用 MySQL 的 INSERT 语句:
INSERT INTO conditions (time, device_id, temperature, humidity) VALUES (NOW(), 'dev001', 36.5, 60.1);查询聚合时使用time_bucket函数:
SELECT time_bucket('5 minutes', time) AS bucket, AVG(temperature) AS avg_temp FROM conditions WHERE device_id = 'dev001' AND time > NOW() - INTERVAL '1 hour' GROUP BY bucket ORDER BY bucket;这个方案保留了团队的 SQL 经验,改造业务代码时改动范围最小。前提是你愿意维护一个 PostgreSQL 实例,并且接受它的时序能力是建立在 PG 生态之上,而不是独立的专用引擎。
6.5 采集端批量写入示例
无论选哪个时序库,采集端都应该避免逐条发送。下面是一个使用 Python + HTTP 批量写入 InfluxDB 兼容 API 的简化示例:
# 文件路径:collector/batch_write.py import time import requests devices = ["dev001", "dev002", "dev003"] def build_lines(): lines = [] timestamp = int(time.time() * 1_000_000_000) # 纳秒 for device_id in devices: temp = round(30 + device_id.__hash__() % 10, 2) humi = round(50 + device_id.__hash__() % 20, 2) lines.append( f"device_metric,device_id={device_id},region=bj " f"temperature={temp},humidity={humi} {timestamp}" ) return lines def write_batch(): data = "\n".join(build_lines()) resp = requests.post( "http://127.0.0.1:8086/write?db=iot_monitor", data=data, headers={"Content-Type": "text/plain"}, timeout=5, ) print(resp.status_code, resp.text) if __name__ == "__main__": write_batch()核心思想是把多条数据拼成一个字符串,一次 HTTP 请求写入,而不是一条一条地发送。这样可以显著减少网络往返和服务端解析开销。真实项目中通常还会用消息队列缓冲采集数据,再由一个消费程序批量写入时序库,避免采集抖动直接把存储打挂。
6.6 如何验证链路是否正常
启动一个时序库并完成建表后,验证链路至少要做三件事:
- 写入验证:向库里写入一批测试数据,再查询时间戳最新的几条数据,确认字段值和设备标签都正确;
- 聚合验证:执行一个按时间桶聚合的查询,确认返回的时间桶数量和平均值符合预期;
- 可视化验证:接入 Grafana,选择对应数据源,拉出一个设备的 5 分钟粒度曲线,确认没有断点。
如果验证过程中发现数据缺失,优先检查采集端时间戳是否一致、批量写入是否成功、时序库的写入接口返回内容是否包含错误码。
7. 常见问题与排查思路
无论选择哪个时序库,上线后大概率会碰到下面几类问题。下面表格整理了现象、原因、排查方向和解决建议:
| 问题现象 | 可能原因 | 排查思路 | 解决方案 |
|---|---|---|---|
| 设备数据出现断点或缺失 | 客户端时间戳格式不统一,或批量写入部分失败 | 检查写入端日志和库内时间序列表 | 统一时间精度(建议纳秒)、分批重试失败数据 |
| 磁盘增长过快 | 未配置保留策略,或压缩率偏低 | 查看每张表/测量的磁盘占用和分区大小 | 配置 TTL、降低副本数、启用降采样 |
| 聚合查询延迟高 | 查询缺少时间范围,或维度标签过多导致高基数 | 用查询计划分析扫描范围,统计序列基数 | 查询强制限制时间窗口,收敛 tag 值和 tag 数量 |
| 写入速率上不去 | 单条写入请求太多、批次太小 | 观察写入 API 的 QPS 和平均耗时 | 改为批量写入,积攒几百条再提交 |
| 时序库自身 CPU 飙升 | 出现乱序写入或大量高基数查询 | 查看最新时间戳写入分布,观察查询并发数 | 校准采集端时钟,对高频报表查询做预聚合 |
| 从 MySQL 迁移后数据对不上 | 时区、时间精度或字段命名不一致 | 对比同一设备同一时刻两边的数据点和值 | 统一用 UTC 时间存储,展示层再转本地时区 |
真正容易踩坑的地方是时间精度:MySQL 的DATETIME一般精确到秒,而多数时序库支持毫秒、微秒、纳秒。如果迁移时没补齐精度,原始数据的排序和聚合结果会和预期不一致。
8. 时序库落地的工程规范与最佳实践
选好库只是开始,真正决定系统是否稳定的往往是落地细节。
8.1 标签设计要克制
标签是时序库的索引维度,但不是越多越好。一个常见误区是把每条请求 ID、随机数放进 tag,导致序列基数爆炸性地增长。建议把高基数、高变化的信息放在 field 里,把低基数的设备维度、区域等稳定信息放在 tag 里。标签值尽量规范化,比如统一小写、统一长度。
8.2 批量写入和乱序处理
所有时序库都更欢迎连续时间戳的批量写入。采集端的本地时钟如果和服务器不一致,很容易产生乱序数据。乱序数据会让存储引擎做额外的重排和合并,显著降低写入性能。工程上建议在生产环境开启 NTP 时间同步,并在写入端做 100~500 条的小批量攒批,既保证实时性,又不至于把压力全打到存储层。
8.3 保留策略和降采样分开设计
长期数据不意味着“全部原始数据都保留”。可以按业务需求把保留周期拆成两层:原始数据保留 30 天,30 天到 1 年的数据通过降采样保存分钟级或小时级均值,更早的数据归档到对象存储。这样磁盘成本和查询性能都能得到控制。时序库的 TTL 能力是实现这个策略的基础,也是它比 MySQL DELETE 高效的核心原因之一。
8.4 权限和最小安全边界
时序库虽然不像业务数据库那样直接暴露给外部应用,但写入和查询权限也要分开。采集端使用只写账号,报表端使用只读账号;生产环境的删除操作要经过审批,并且先在测试环境用少量数据验证。分布式时序库的节点间通信、备份恢复策略也要提前确认,不要出现“数据都堆在单机目录里,没有任何备份”的情况。
8.5 监控时序库本身
时序库是新的数据底座,必须把自身的状态纳入监控:写入速率、磁盘使用率、分区数量、查询延迟、慢查询记录。建议在 Grafana 里单独建一个 Dashboard,把时序库的写入吞吐和磁盘增长曲线放到最显眼的位置。这样设备量增长或保留策略失效时,能够提前发现并处理。
8.6 保留回滚通道
切换时序库时,不建议立刻把 MySQL 的表删掉。最稳妥的做法是保留至少一个保留周期的数据,打开业务写入开关时先双写一段时间的实时数据,确认接口返回正常后再逐步关闭 MySQL 写入。回滚通道存在的意义,不是让你真的回滚,而是让团队在切换过程中保持冷静,不要在上线当天被迫做高风险决策。
9. 这次选型复盘给我的几条经验
如果只让我总结三条最值得记住的经验,我会说:
第一,别把 MySQL 当成“万能存储”。它擅长事务型负载,保留业务订单、用户关系、系统配置这些数据没有问题;但让它扛持续追加的时序数据,等于用错误模型做正确的事,最终一定会在某个规模点爆发。
第二,选型先算序列基数和写入吞吐,再看功能和生态。很多团队纠结于查询语法、压缩率、可视化集成,却忘了先回答两个最基本的问题:未来一年会有多少条独立序列?峰值每秒多少点写入?这两个数字直接决定单机够不够用、该选哪种数据模型。
第三,落地比选型更重要。无论选 InfluxDB、TDengine、TimescaleDB 还是 VictoriaMetrics,都要把批量写入、标签规范、保留策略、监控告警和回滚方案一起设计进去。这套工程规范会随着设备规模增长不断被验证,也是真正拉开“能稳定跑”和“反复重构”差距的地方。
几千台设备把 MySQL 拖垮,不是一件坏事。它只是用一次生产事故告诉你,数据有不同类型,存储引擎也应该有不同的分工。下一步建议你拿一台测试机,把本文中任意一个时序库的最小示例跑通,然后接入自己的一小部分设备数据,和 MySQL 的查询速度做一个对比。这个对比结果,会比任何选型文章都更有说服力。