摄取1亿次心跳:在不破产的情况下扩展可穿戴技术

发布: (2025年12月26日 GMT+8 00:00)
6 min read
原文: Dev.to

Source: Dev.to

“Continuous”的数学

让我们实事求是地看数字。如果设备每秒发送一次心跳负载(1 Hz):

  • 1 用户 = 86,400 次写入/天。
  • 1,000 用户 = 86.4 百万次写入/天。

负载大小:即使是一个只有 100 字节的 JSON 包,也意味着每天数 GB 的摄入流量。

标准关系型数据库(如 MySQL 或普通的 Postgres)针对事务完整性(ACID)进行优化,而不是每秒处理数百万条小写入。B‑Tree 索引的开销本身就会严重削减写入吞吐量。

策略 1:停止 “喋喋不休” 协议(批处理)

第一个错误是把可穿戴设备当成聊天应用来使用。不要为每一次心跳都打开一个 WebSocket 或 API 请求。网络开销(TCP 握手、HTTP 头部)往往比实际数据还大。

解决办法: 在设备端进行缓冲。理想情况下,设备应收集 60 秒的数据后一次性发送压缩块。

在后端,避免一次只 INSERT 一行数据,而是使用批量插入。下面是一个虽然写得不够优雅但有效的 Node.js 示例,展示了两者的差别:

// DON'T DO THIS
data.points.forEach(async (point) => {
  await db.query('INSERT INTO heartbeats VALUES (...)', [point]); // RIP Database
});

// DO THIS
const format = require('pg-format');

const values = data.points.map(p => [p.userId, p.value, p.timestamp]);
const query = format('INSERT INTO heartbeats (user_id, bpm, time) VALUES %L', values);

await db.query(query); // One network round trip, one transaction

这个简单的改动可以将你的 IOPS(每秒输入/输出操作次数)降低约 50–100 倍。

Strategy 2: 使用时间序列数据库(TSDB)

我喜欢 Postgres。但对于原始指标来说,你需要一种 仅追加 且按时间顺序组织的数据处理方式。

TimescaleDB(基于 Postgres)或 InfluxDB 之类的工具在这里就是救星。它们使用 “hypertables” 或专门的存储引擎,将数据按时间块进行分区。

这怎么省钱?压缩。 时间序列数据高度重复:

{ "time": "10:00:01", "bpm": 70 }
{ "time": "10:00:02", "bpm": 70 }
{ "time": "10:00:03", "bpm": 71 }

TSDB 使用 delta‑of‑delta 压缩。它们不是每次都存储完整的时间戳和完整的整数,而是只存储它们之间的微小变化。我们已经看到,仅仅将普通的 Postgres 表切换为压缩的 hypertables,就能让存储成本下降 90 %

如果你想阅读更多关于数据库选择的技术深度解析,请查看我的 技术指南和教程

策略 3:下采样的艺术(汇总)

Here is the hard truth: Nobody needs second‑by‑second resolution from 3 months ago. A doctor might need granular data for yesterday’s arrhythmia event, but for a trend report from last year they only need the daily average, min, and max.

事实是: 没有人需要三个月前的秒级分辨率。 医生可能需要昨天心律失常事件的细粒度数据,但对于去年的趋势报告,他们只需要每日的平均值、最小值和最大值。

The Fix: Continuous aggregates. Don’t calculate averages on the fly (that’s slow). Pre‑calculate them as data comes in and drop the raw data later.

解决方案: 连续聚合。不要实时计算平均值(那很慢)。在数据进入时预先计算,并在之后删除原始数据。

In SQL (Timescale syntax), it looks like this:

-- Create a view that updates automatically
CREATE MATERIALIZED VIEW hourly_heartrate
WITH (timescaledb.continuous) AS
SELECT
  time_bucket('1 hour', time) AS bucket,
  user_id,
  avg(bpm) AS avg_bpm,
  max(bpm) AS max_bpm
FROM heartbeats
GROUP BY bucket, user_id;

-- Add a retention policy to delete raw data after 7 days
SELECT add_retention_policy('heartbeats', INTERVAL '7 days');

Now, your storage doesn’t grow infinitely. You keep high‑resolution data for a week (for immediate alerts) and low‑resolution data forever (for long‑term trends).

现在,您的存储不会无限增长。您可以保留一周的高分辨率数据(用于即时警报),并永久保留低分辨率数据(用于长期趋势)。

结论

为“持续监控”构建不仅仅是写代码;它还涉及物理学和经济学。

  • Buffer 在客户端进行缓冲以减少网络请求。
  • Batch 在服务器端批处理以节省 IOPS。
  • Compress 使用时序数据库进行压缩以节省磁盘空间。
  • Downsample 对旧数据进行降采样以保持数据库快速。

虽然过度工程很诱人,但严格的数据生命周期策略往往比华丽的 Kubernetes 集群更有价值。

祝编码愉快。 🚀

Back to Blog

相关文章

阅读更多 »

仓库利用的权威指南

引言 仓库本质上只是一个 3‑D 盒子。利用率只是衡量你实际使用了该盒子多少的指标。虽然物流 c...