如何统计亿级用户的在线状态
这个问题本质上不是简单的计数问题而是高并发写入 低延迟读取 海量数据的综合挑战写入 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/7/19 5:32:16 阅读更多 →
【源码】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/7/19 5:32:16 阅读更多 →
美的BD/BC-143KMF(E)冰柜评测:一级能效如何实现长期省电省钱

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

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

2026/7/19 5:31:16 阅读更多 →

最新新闻

嵌入式SPI通信进阶:McSPI多通道架构、配置与实战调试指南

嵌入式SPI通信进阶:McSPI多通道架构、配置与实战调试指南

1. 项目概述与McSPI核心价值在嵌入式系统开发中&#xff0c;与外设通信是家常便饭。无论是读取传感器数据、驱动显示屏&#xff0c;还是与存储芯片交互&#xff0c;一个高效、可靠的通信接口至关重要。SPI&#xff08;Serial Peripheral Interface&#xff09;以其简单、高速、…

2026/7/19 8:28:24 阅读更多 →
ARM Cortex-A嵌入式开发:McSPI DMA与电源管理实战配置指南

ARM Cortex-A嵌入式开发:McSPI DMA与电源管理实战配置指南

1. 项目概述与核心价值在嵌入式开发&#xff0c;尤其是基于ARM Cortex-A系列处理器的复杂应用中&#xff0c;SPI总线的效率与系统功耗是两个永恒的核心议题。我们经常遇到这样的场景&#xff1a;一个传感器需要以1MHz的速率持续上传数据&#xff0c;如果采用传统的中断方式&…

2026/7/19 8:28:24 阅读更多 →
AT89S52单片机驱动LED点阵屏设计与实现

AT89S52单片机驱动LED点阵屏设计与实现

1. 项目概述&#xff1a;AT89S52单片机驱动LED点阵屏的设计初衷 第一次接触单片机LED点阵显示是在大三的电子设计课上&#xff0c;当时看到学长用几块小小的芯片就能控制整面墙的LED显示文字&#xff0c;那种震撼感至今难忘。这个基于AT89S52的LED点阵显示项目&#xff0c;正是…

2026/7/19 8:28:24 阅读更多 →
嵌入式单线通信协议HDQ与1-Wire深度解析与工程实践

嵌入式单线通信协议HDQ与1-Wire深度解析与工程实践

1. 单线通信&#xff1a;在“一根线”上做文章的艺术 在嵌入式系统设计里&#xff0c;我们总是在和有限的资源较劲&#xff1a;有限的引脚、有限的空间、有限的成本。当你的MCU引脚被各种外设瓜分殆尽&#xff0c;或者需要在狭小的空间内连接多个传感器时&#xff0c;传统的I2C…

2026/7/19 8:28:24 阅读更多 →
2026年Android开发新趋势与工具链深度解析

2026年Android开发新趋势与工具链深度解析

1. Android开发技术周报概述 作为Android开发者&#xff0c;保持对最新技术动态的敏感度至关重要。本期技术周报聚焦2026年Android开发生态的核心变化&#xff0c;特别是Android Studio Quail 1 | 2026.1.1 Patch 2版本的重大更新。这个版本不仅带来了性能提升&#xff0c;更在…

2026/7/19 8:28:23 阅读更多 →
好心情:靠谱的恋爱关系修复心理教育机构

好心情:靠谱的恋爱关系修复心理教育机构

在感情的长河里漂泊&#xff0c;我们都曾遇到过迷雾与暗礁。明明深爱着&#xff0c;却因为一次次争吵、冷战或信任危机&#xff0c;把彼此推得越来越远。那种无力感&#xff0c;真的很折磨人。很多人在试图修复关系时&#xff0c;往往只停留在问题的表面&#xff1a;纠结于谁对…

2026/7/19 8:27:23 阅读更多 →

日新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中&#xff0c;我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源&#xff0c;还是配置文件、证书等&#xff0c;都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下&#xff0c;但这…

2026/7/19 0:00:40 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP&#xff08;轻量级目录访问协议&#xff09;作为企业级身份认证的黄金标准&#xff0c;已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时&#xff0c;发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/19 0:00:40 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”&#xff0c;而是以可解释、可审计、可迭代的方式&#xff0c;赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/19 0:00:40 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中&#xff0c;我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源&#xff0c;还是配置文件、证书等&#xff0c;都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下&#xff0c;但这…

2026/7/19 0:00:40 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP&#xff08;轻量级目录访问协议&#xff09;作为企业级身份认证的黄金标准&#xff0c;已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时&#xff0c;发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/19 0:00:40 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”&#xff0c;而是以可解释、可审计、可迭代的方式&#xff0c;赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/19 0:00:40 阅读更多 →

月新闻