如何用Redis Bitmap做海量设备在线离线心跳检测及状态聚合?

夜磊小哥_9051

夜磊小哥_9051

2026-10-20

229人浏览

原创

bitcount统计在线设备数快因o(1)时间复杂度,但仅返回1的个数而无法定位具体设备,故适用于监控大盘而非业务逻辑;需配合bitpos、bitop或映射表等方案满足运营需求。

如何用redis bitmap做海量设备在线离线心跳检测及状态聚合?

BITCOUNT 统计在线设备数为什么快,但不能直接用

因为 BITCOUNT 确实能在 O(1) 时间内返回整个位图中值为 1 的 bit 数量,看起来是统计“当前在线设备总数”的完美解法。但问题在于:它只告诉你“有多少个 1”,不告诉你“哪些 offset 是 1”。你没法知道具体是哪台设备在线,更没法做分页、导出、按区域筛选这类运营需求。

实际生产中,BITCOUNT 更适合做秒级大盘监控(比如看后台 Dashboard 上那个“在线数:2,489,103”),而不是业务逻辑主路径。一旦需要关联设备元数据(如 IP、型号、归属分组),就必须配合其他结构查表,或者改用 BITPOS 扫描 + 分批拉取。

  • 单 key 存一亿设备,BITCOUNT 耗时稳定在微秒级,但 BITPOS key 1 扫第一个在线设备可能要几毫秒——偏移量越靠后越慢
  • Redis 7.0+ 支持 BITPOS key 1 COUNT 100,可一次找前 100 个在线位,比循环调用高效得多
  • 别对大 offset 位图频繁扫 BITPOS,容易阻塞主线程;建议拆 key 或加缓存层

SETBIT 心跳写入必须带 EXPIRE,否则会累积脏数据

很多人把设备 ID 当作 offset,每天用 SETBIT online:20261019 123456 1 更新状态,却忘了过期。结果第二天这个 key 还在,里面全是昨天的“1”,导致 BITCOUNT 统计严重虚高。

正确做法是:每次心跳都重置 key 生存时间。不是靠客户端定时删,而是服务端在写入时强制刷新 TTL:

SETBIT online:20261019 123456 1
EXPIRE online:20261019 86400

注意这两条命令必须原子执行——用 Lua 脚本封装,否则存在竞态(比如 SETBIT 成功但 EXPIRE 失败):

redis-tools
redis-tools

按类别查找Redis命令、检测服务器连接、监控键数量和内存占用,支持离线速查表。

下载
EVAL "redis.call('SETBIT', KEYS[1], ARGV[1], 1); redis.call('EXPIRE', KEYS[1], ARGV[2]); return 1" 1 online:20261019 123456 86400
  • 不要用 SETEX,因为它只支持字符串整体设置,不支持位操作
  • 避免用 SETBIT + PEXPIRE 分开调用,网络延迟或失败会导致 key 永久不过期
  • 如果心跳频率高(如每 10 秒一次),TTL 建议设为心跳间隔的 3 倍(即 30 秒),兼顾容错与及时下线

多天状态聚合必须用 BITOP,但要注意 key 对齐和内存峰值

要算“过去 7 天都在线的设备”,本质是 7 个位图做 AND 运算:BITOP AND result:7day online:20261013 online:20261014 ... online:20261019。这很高效,但有两个硬约束:

  • 所有参与 BITOP 的 key 必须有相同长度(即最大 offset 一致),否则短的会被自动补零——这可能导致误判:某设备第 5 天没心跳,对应位是 0,但第 6 天 key 长度不够,被补成 0,AND 后还是 0,结果“没在线”被当成“不存在”
  • BITOP 是同步阻塞操作,7 个 1GB 的位图做 AND,Redis 主线程会卡住几百毫秒,集群环境下还可能触发 slowlog 报警
  • 推荐预生成固定长度位图:比如统一按 1 亿设备容量初始化,用 SETBIT key 99999999 0 触发分配,后续所有天都复用该长度

更稳妥的做法是把聚合下沉到离线任务:每天凌晨用 Spark 读 Redis 位图 dump,做位运算后写回一个汇总 key,线上只查这个结果。

设备 ID 映射 offset 容易溢出或碰撞,必须做压缩

直接把原始设备 ID(如 UUID 字符串或 64 位整数)当 offset 用,风险极大。Redis offset 是 uint64,最大支持 2^64−1 ≈ 1.8×10¹⁹,看似够用,但实际会遇到两类问题:

  • UUID 转数字可能超范围,比如 uuid.uuid4().int 在 Python 中是 128 位,直接强转会截断或报错
  • 不同设备 ID 映射到同一 offset(哈希冲突),比如用简单取模 id % 100000000,千万级设备下冲突概率不可忽略
  • 稀疏 ID(如数据库自增主键跳着增长)会导致位图内部大量空洞,内存浪费严重——Redis 按最大 offset 分配字节,中间全补零

推荐方案:用布隆过滤器 + 单调递增映射表(如 MySQL 自增 ID)做两级压缩。上线时先查映射表,不存在则插入并获取新 offset;查询时反查表还原设备 ID。这样 offset 紧凑、可控、无冲突。

复杂点在于映射表本身成了单点瓶颈,所以得加本地缓存(Caffeine)+ 异步批量写入,同时容忍短暂映射延迟——毕竟心跳检测本就是“推断”,不是强实时。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

redis

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
golang map内存释放
golang map内存释放

本专题整合了golang map内存相关教程,阅读专题下面的文章了解更多相关内容。

2025.09.05

590

5

golang map相关教程
golang map相关教程

本专题整合了golang map相关教程,阅读专题下面的文章了解更多详细内容。

2025.11.16

343

7

golang map原理
golang map原理

本专题整合了golang map相关内容,阅读专题下面的文章了解更多详细内容。

2025.11.17

513

20

java判断map相关教程
java判断map相关教程

本专题整合了java判断map相关教程,阅读专题下面的文章了解更多详细内容。

2025.11.27

283

6

常用的数据库软件
常用的数据库软件

常用的数据库软件有MySQL、Oracle、SQL Server、PostgreSQL、MongoDB、Redis、Cassandra、Hadoop、Spark和Amazon DynamoDB。更多关于数据库软件的内容详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.02

4549

19

内存数据库有哪些
内存数据库有哪些

内存数据库有Redis、Memcached、Apache Ignite、VoltDB、TimesTen、H2 Database、Aerospike、Oracle TimesTen In-Memory Database、SAP HANA和ache Cassandra。更多关于内存数据库相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.14

4095

11

mongodb和redis哪个读取速度快
mongodb和redis哪个读取速度快

redis 的读取速度比 mongodb 更快。原因包括:1. redis 使用简单的键值存储,而 mongodb 存储 json 格式的数据,需要解析和反序列化。2. redis 使用哈希表快速查找数据,而 mongodb 使用 b-tree 索引。因此,redis 在需要高性能读取操作的应用程序中是一个更好的选择。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.02

7612

6

redis怎么做缓存服务器
redis怎么做缓存服务器

redis 作为缓存服务器的答案:redis 是一款开源、高性能、分布式的键值存储,可作为缓存服务器使用。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

643

6

redis怎么解决数据一致性
redis怎么解决数据一致性

redis 提供了两种一致性模型,以维护副本数据一致性:强一致性 (sync) 确保写操作仅在复制到所有从节点后才完成;最终一致性 (async) 则在主节点上写操作后认为已完成,牺牲一致性换取性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

756

6

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
phpEnv手册
phpEnv手册

共0课时 | 0人学习