如何统计亿级用户的在线状态
这个问题本质上不是简单的计数问题而是高并发写入 低延迟读取 海量数据的综合挑战写入 QPS 极高用户上下线事件每秒可达百万级读取要求极低延迟毫秒级返回在线人数数据一致性要求不能出现明显的在线人数偏差成本可控不能为了统计而投入过多资源方案演进 我会从最简单的方案开始逐步演进到适合亿级用户的架构1️⃣ 初级方案数据库计数法万级用户// 伪代码public void userOnline(Long userId) {userDao.updateOnlineStatus(userId, true);}public long getOnlineCount() {return userDao.countByOnlineStatus(true);}问题每次统计都要全表扫描亿级数据下直接超时 ❌2️⃣ 中级方案Redis 计数器十万级用户// 伪代码public void userOnline(Long userId) {redisTemplate.opsForValue().set(“online:”userId, “1”, Duration.ofMinutes(30));redisTemplate.opsForValue().increment(“online:count”);}问题重复上线会导致计数器重复增加下线事件丢失会导致计数器不准单 Redis 实例无法支撑亿级用户 ❌3️⃣ 高级方案Redis Bitmap百万级用户// 伪代码public void userOnline(Long userId) {redisTemplate.opsForValue().setBit(“online:bitmap”, userId, true);}public long getOnlineCount() {return redisTemplate.execute((RedisCallback) conn - conn.bitCount(“online:bitmap”.getBytes()));}优势1 亿用户仅需 12.5MB 内存1 亿 bit 12.5MB问题单 Bitmap 在亿级用户下 bitCount 操作耗时仍会达到几十毫秒 ❌最终架构分片 Bitmap 定时聚合 多级缓存 ✅这是我在实际项目中使用过的、支撑过 3 亿 日活用户的方案image核心实现细节 用户 ID 分片将用户 ID 按模 10 分片到 10 个 Redis 实例每个分片最多存储 1000 万用户的在线状态每个分片的 bitCount 操作耗时 1ms在线状态更新// 伪代码public void userHeartbeat(Long userId) {int shard (int)(userId % 10);redisTemplate.opsForValue().setBit(“online:bitmap:”shard, userId/10, true);// 设置过期时间自动清理离线用户redisTemplate.expire(“online:bitmap:”shard, Duration.ofMinutes(30));}定时聚合统计// 伪代码每10秒执行一次Scheduled(fixedRate 10000)public void aggregateOnlineCount() {long total 0;for (int i0; i10; i) {total redisTemplate.opsForValue().bitCount(“online:bitmap:”i);}redisTemplate.opsForValue().set(“online:total”, total, Duration.ofSeconds(15));}查询接口public long getOnlineCount() {Long count redisTemplate.opsForValue().get(“online:total”);return count ! null ? count : 0L;}关键优化与边界处理 ⚡问题 解决方案 效果热点分片问题 采用一致性哈希替代简单取模 分片负载均衡度提升 90%实时性要求高 增加本地缓存每 1 秒更新一次 查询延迟 1ms精确性要求高 关键业务场景使用 HyperLogLog 辅助验证 误差控制在 0.81% 以内Redis 宕机 主从复制 哨兵机制 故障自动切换无数据丢失历史数据统计 每 5 分钟快照一次 Bitmap 到 HBase 支持任意时间点的在线人数回溯扩展能力 分地区 / 分业务统计增加维度前缀如online:bitmapbeijing:0用户在线时长统计结合 Redis Hash 记录用户最后上线时间异常用户检测统计单位时间内上下线次数识别机器人总结 亿级用户在线状态统计的核心思想是“分片处理 批量计算 结果缓存”用 Bitmap 解决海量数据的存储问题用分片解决单实例性能瓶颈用定时聚合解决实时统计的性能问题用多级缓存解决高并发查询问题这个方案在实际生产环境中支撑过 3 亿 日活、峰值 5000 万同时在线的场景查询延迟稳定在 1ms 以内完全满足互联网大厂的要求。现场模拟面试现场模拟面试面试官 “同学你好今天我们来聊一个场景设计题。假设现在我们有一个亿级用户的社交/IM系统需要实时统计用户的在线状态比如谁在线、当前总在线人数。要求支持高并发、低延迟而且资源成本要可控。你会怎么设计先说说你的整体思路。”候选人 “嗯……亿级用户的话首先想到不能用传统数据库频繁写。在线状态是典型的读多写少但变更频繁的场景可以用 Redis。大概思路是每个用户一个 key上线 set下线 del统计在线数用 keys *好像不太对……”面试官 “停keys * 在大数据量下会直接阻塞 Redis生产环境绝对是禁止的。你刚才说一个用户一个 key假设有一亿用户哪怕只有 1000 万同时在线那就是 1000 万个 keyRedis 内存受得了吗而且 key 过期管理也很麻烦。有没有更省内存的方案”候选人 “对应该用 Bitmap位图。给每个用户分配一个数字 ID上线就在对应的 bit 位置 1下线置 0。统计在线总人数直接 BITCOUNT 命令查询单个用户是否在线用 GETBIT。一亿用户才占用约 12.5MB 内存非常省。”面试官 “很好方向对了Bitmap 确实是这类问题的经典解法。那咱们继续深挖。你考虑过这三个实际痛点吗心跳与脏数据—— 用户直接断网或 APP 崩溃没有发下线请求Bitmap 里会一直「在线」。多端登录—— 同一个用户手机、PC 同时在线你只用一个 bit 怎么区分亿级用户 ID 不连续—— 如果用户 ID 是字符串或非连续数字Bitmap 就不好使了。”你看这三点怎么优化候选人 “嗯…心跳问题可以让客户端定时发心跳包比如每 5 分钟服务端收到后更新 Bitmap。但宕机的用户 Bitmap 还是 1所以需要后台定时任务比如每隔 10 分钟扫描 Bitmap与「最后心跳时间表」对比把超时的 bit 置 0。多端登录的话一个 bit 不够可以拆成多个 bit比如用 2bit 表示00 离线01 手机在线10 PC 在线11 多端在线。或者直接用另一个 Redis Set 存在线设备详情Bitmap 只负责快速统计和判存。ID 不连续问题可以做一层「全局 ID 映射」内部生成连续递增数字 ID和业务 ID 一一对应放在本地缓存或 Redis Hash 里。”面试官 “不错都对症下药了。那咱们再上升一个维度说说整体架构和高可用。我画个简图你边看边补充。” 整体架构示意image图释心跳记录 用 Sorted Setscore最后心跳时间戳可以快速找到超时用户。Bitmap 负责在线状态判断和全局计数。定时任务只扫 Sorted Set 中过期的用户去 Bitmap 置 0避免全量扫描。面试官 ‍“那这个定时任务怎么扫才高效如果亿级用户同时在线心跳表也超大。”候选人 ⚡“Sorted Set 可以用 ZRANGEBYSCORE 分批拉取 score 小于当前时间 - 超时阈值的用户比如每次取 1000 条更新 Bitmap 后再删掉 Sorted Set 中的记录。这样每次只处理真正过期的用户压力可控。另外心跳写入用管道或异步批量写减少 Redis 连接开销。”面试官 ‍“不错。那如果要输出「当前在线用户列表」或「在线用户详情分页」Bitmap 能扛吗”候选人 “Bitmap 其实不适合做列表分页它只擅长判存和计数。如果需要在线用户列表可以用 Redis Sorted Set 或 Set 存储在线用户 ID分页用 ZSCAN 或 SSCAN。但这样内存会大一些可以结合业务需要用 Bitmap 做快速统计和大批量查询用另一个轻量集合做少量列表展示并设置 TTL 心跳续期。”面试官 “Okay最后来个总结。在亿级用户在线状态场景下你的核心方案是”候选人 “总结一下一个中心、两个基本点中心以 Redis Bitmap 作为核心存储极致节省内存提供 O(1) 查询和 bitcount 统计。心跳维护通过 Redis Sorted Set 记录最后心跳时间定时任务增量清理超时 bit。ID 映射用连续数字 ID 映射业务 ID解决稀疏 ID 问题。多端扩展通过多 bit 位或辅助 Set 解决。高可用Redis Cluster 分片 哨兵主从切换避免单点。整体是一个读多写少、内存友好、可线性扩展的在线状态系统。”面试官 “非常清晰技术点都踩到了。记得实际落地还要考虑网络抖动导致心跳误判可加重试窗口、Redis 大 key 问题一亿 bit 的 Bitmap 不算大 key 但也要注意分片、以及一致性AP 模型允许少量不一致。好了这题你通过了回去等通知吧” 场景设计题如何统计亿级用户的在线状态补充版面试官您好我来补充一下这个方案的核心生产级代码和完整技术难点解决方案。核心生产级代码实现 7.1 Redis 配置类技术亮点自定义分片连接池ConfigurationEnableCachingpublic class RedisConfig {// 技术亮点1预定义10个分片连接池避免运行时动态创建Bean(“redisShard0”)public RedisTemplateString, Object redisShard0() {return createRedisTemplate(“192.168.1.10:6379”);}Bean(redisShard1) public RedisTemplateString, Object redisShard1() { return createRedisTemplate(192.168.1.11:6379); } // ... 省略其他8个分片的Bean定义 // 技术亮点2统一创建模板配置最优序列化方式 private RedisTemplateString, Object createRedisTemplate(String address) { RedisTemplateString, Object template new RedisTemplate(); template.setConnectionFactory(createConnectionFactory(address)); // 使用String序列化器避免JDK序列化的性能开销 StringRedisSerializer stringSerializer new StringRedisSerializer(); template.setKeySerializer(stringSerializer); template.setHashKeySerializer(stringSerializer); template.setValueSerializer(stringSerializer); template.setHashValueSerializer(stringSerializer); template.afterPropertiesSet(); return template; } private LettuceConnectionFactory createConnectionFactory(String address) { String[] parts address.split(:); RedisStandaloneConfiguration config new RedisStandaloneConfiguration(); config.setHostName(parts[0]); config.setPort(Integer.parseInt(parts[1])); config.setDatabase(0); // 技术亮点3配置连接池参数优化高并发场景 LettucePoolingClientConfiguration poolConfig LettucePoolingClientConfiguration.builder() .poolConfig(new GenericObjectPoolConfig() {{ setMaxTotal(200); setMaxIdle(50); setMinIdle(10); setTestOnBorrow(true); }}) .commandTimeout(Duration.ofMillis(100)) .build(); return new LettuceConnectionFactory(config, poolConfig); }}

相关新闻

Git五大核心命令的本质:从对象模型看rebase、merge、stash、revert与reset

Git五大核心命令的本质:从对象模型看rebase、merge、stash、revert与reset

1. 项目概述:Git五大核心操作的本质与实战边界Git不是魔法,它是一套有明确物理意义的操作系统。我带过十几支开发团队,见过太多人把rebase当万能膏药,把reset --hard当后悔药,结果在周五下午三点毁掉整个发布分支。这五…

2026/10/20 4:17:31 阅读更多 →
【源码】openfeign集成spring-cloud-starter-circuitbreaker-resilience4j 源码分析

【源码】openfeign集成spring-cloud-starter-circuitbreaker-resilience4j 源码分析

依赖 本文源码分析pom依赖如下&#xff0c;版本 3.1.0<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId><version>3.1.0</version></depend…

2026/10/21 10:05:08 阅读更多 →
美的BD/BC-143KMF(E)冰柜评测:一级能效如何实现长期省电省钱

美的BD/BC-143KMF(E)冰柜评测:一级能效如何实现长期省电省钱

最近在帮朋友选购家用冰柜时&#xff0c;发现一个很有意思的现象&#xff1a;很多人在选购时只关注容量和价格&#xff0c;却忽略了一个更重要的指标——长期使用成本。一台看似便宜的冰柜&#xff0c;如果耗电量大&#xff0c;几年下来可能比购买时省下的钱还要多。今天我们就…

2026/10/21 8:16:08 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年&#xff0c;我大部分时间都在跟浏览器标签页打交道&#xff0c;直到第一次把桌面应用跑起来&#xff0c;才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词&#xff0c;最开始是在某段时间频繁出现在同事的讨论里&#xff0c;一个用HTML…

2026/10/21 9:32:40 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介&#xff1a;亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档&#xff0c;主要面向智能锁安装人员、终端用户及售后维护者&#xff0c;帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件&#xff0c;大小4.37MB&#xff0c;内容结构清晰&#xff…

2026/10/21 9:32:40 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统&#xff0c;整车要求空载跑到8m/s&#xff0c;电机转速已经拉到额定点&#xff0c;驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令&#xff0c;转速纹丝不动&#xff0c;三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/21 9:32:40 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年&#xff0c;用它写过嵌入式驱动、做过游戏服务端&#xff0c;也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词&#xff0c;我其实挺感慨的——这门语言的难点从来不是语法多&#xff0c;而是知识点之间的耦合太深…

2026/10/21 9:32:40 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介&#xff1a;亚太天能T109滑盖智能锁产品说明书以PDF格式发布&#xff0c;适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开&#xff0c;先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能&#xff0c;再给出完整安装步骤&…

2026/10/21 9:32:40 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历&#xff1a;翻开源码注释、论文摘要或者一份专业报告&#xff0c;每个字都认识&#xff0c;连在一起却像天书。想找人聊聊&#xff0c;身边要么没人懂&#xff0c;要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型&#xff0c;几秒钟之后&a…

2026/10/21 9:31:32 阅读更多 →

日新闻

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/22 1:47:45 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/21 21:32:28 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/21 4:27:17 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/21 21:50:56 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/21 21:51:55 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/21 16:24:48 阅读更多 →