当前位置: 首页 > news >正文

从 Hash 到 HyperLogLog:Redis 海量 UV 统计的 3 种高阶玩法

真实业务场景

假设我们正在开发一个电商大促活动页,产品经理提了一个“简单”的需求:我们需要实时显示“当前正在浏览商品的用户数”。这个数字每秒可能变化数万次。在 10 万级并发下,传统关系型数据库会面临什么问题?

经典错误场景:

// Java 初学者的常见做法 public synchronized void addCount() { int count = getFromMySQL(); updateMySQL(count + 1); }

上述代码在高并发下会导致:

  1. 数据库连接池过载(超时错误)

  2. 行锁竞争(死锁风险)

  3. CPU 飙升至 100%(性能崩溃)

这正是我们需要设计 Redis 统计方案的核心原因——在这种场景下,关系型数据库的 ACID 特性反而成了负担。

Redis 方案的演进

阶段 1:Hash 方案——精确统计的起点

原理:使用哈希表记录每个独立访客,适用于对精度要求高、中小型规模的场景。

// 将用户访问记录添加到 Hash Jedis jedis = new Jedis("localhost", 6379); String pageKey = "page_visit_count:examplePage"; // 页面访问计数 Key String userId = "user_123"; // 已登录用户 String visitorId = "visitor_abc123"; // 未登录用户 // 使用 HSET 命令添加数据,值简单设为 "1" jedis.hset(pageKey, userId, "1"); jedis.hset(pageKey, visitorId, "1"); // 统计访问量,直接使用 HLEN 命令获取字段数量 long visitCount = jedis.hlen(pageKey); System.out.println("The page visit count is: " + visitCount);

这种方法的优点是简单直接——实现容易,查询方便,且精度极高。但缺点也显而易见。

随着访问页面的增加,Key 像滚雪球一样膨胀,链表变长,内存占用迅速飙升,性能逐渐下降。

因此,这种方法更适合流量相对较低的页面,作为初期的尝试。

阶段 2:Bitmap 方案——空间与性能的平衡

当用户基数变大时,使用 Hash 在空间上就显得有些浪费了。

这正是 Bitmap 大显身手的时候,它是空间效率的大师。它巧妙地利用了 32 位 int 类型——不再存储一个完整的用户 ID,而是拆解 32 位,每一位代表一个用户,直接节省了 32 倍的空间。

对于已登录用户,其 ID 直接映射到 Bitmap 中的特定位。对于未登录用户,通过哈希算法将随机字符串标识符转换为哈希值,再映射到特定位。例如,用户 ID 为 5,对应 Bitmap 第 5 位;随机字符串哈希值为 10,对应第 10 位。

// 使用 Bitmap 统计页面访问量 Jedis jedis = new Jedis("localhost", 6379); String pageKey = "bitmap_visit_count:examplePage"; // Bitmap 访问计数 Key String loginUserId = "5"; // 已登录用户 ID String nonLoginUserKey = "random_str_123"; // 未登录用户的随机 Key // 对于已登录用户,直接设置对应位 jedis.setbit(pageKey, Long.parseLong(loginUserId), true); // 对于未登录用户,先对 Key 进行哈希,再设置位 String hashValue = DigestUtils.md5DigestAsHex(nonLoginUserKey.getBytes()); long hashBitIndex = Long.parseLong(hashValue.substring(0, 16), 16) % Long.MAX_VALUE; jedis.setbit(pageKey, hashBitIndex, true); // 使用 BITCOUNT 命令统计访问量 long visitCount = jedis.bitcount(pageKey); System.out.println("The page visit count is: " + visitCount);

这种方法的优势在于极小的内存占用和便捷的查询,甚至可以检查特定用户是否访问过页面。

但也存在明显的缺点。多个用户可能会碰撞到同一位上,导致统计略有偏差。此外,如果用户分布稀疏——例如用户 ID 是 1 亿——则需要分配 1 亿个位,可能会造成内存浪费。

因此,这种方法更适合用户分布相对密集的场景。

阶段 3:HyperLogLog 方案——超大数据量的“量子”统计

突破性思维:通常,系统不需要绝对精确的数据,可以用可控的误差换取内存和性能的巨大提升。

它不再追求记录每个用户的确切计数,而是使用概率算法估算近似访问量,误差控制在 0.81% 以内,这在实际应用中通常已经足够。

// 使用 HyperLogLog 统计页面访问量 Jedis jedis = new Jedis("localhost", 6379); String pageKey = "hyperloglog_visit_count:examplePage"; // HyperLogLog 访问计数 Key String userId = "user_456"; // 用户标识,可以是 ID 或其他唯一标识 // 使用 PFADD 命令添加用户访问记录 jedis.pfadd(pageKey, userId); // 使用 PFCOUNT 命令统计访问量 long approxVisitCount = jedis.pfcount(pageKey); System.out.println("The approximate page visit count is: " + approxVisitCount);

总结

这三种方案各有所长。

Hash 就像一本细致的账本,适用于小规模、高精度的统计。Bitmap 是空间压缩的大师,适合用户分布密集的才中等规模场景。HyperLogLog 则是概率统计专家,专为超高并发、高流量的网站设计。

在实际项目中,我们需要根据业务场景灵活选择,从而在并发战场中掌控全局,精准高效地统计用户访问量。

http://www.jsqmd.com/news/349803/

相关文章:

  • 抚顺市英语雅思培训辅导机构推荐、2026权威出国雅思课程中心学校口碑排行榜 - 老周说教育
  • 讲讲艺术漆奢华品牌厂家哪家好,南昌地区有哪些高性价比的品牌推荐 - 工业推荐榜
  • 可持续软件开发的测试标准:专业指南
  • 家居投资集团规模化扩张,如何破解跨域管理痛点?
  • 2026年湖南口碑好的GEO优化推广服务商排名,专业服务 - 工业品网
  • 临沧车牌靓号代选,临沧车牌靓号价格-上牌选号 - dasggg
  • ThreadLocal 入门 —— 是什么、为什么用、怎么用
  • 计算机毕业设计 | SpringBoot+vue社区养老服务平台 养老院管理系统(附源码+论文)
  • 聊聊激光测振仪优质品牌,哪家性价比高 - 工业品牌热点
  • 横评后发现! 降AI率平台 千笔·专业降AIGC智能体 VS PaperRed,专科生首选!
  • 车铣一体机生产厂家哪家好?行业实力厂家排行榜 - 品牌推荐大师
  • 2026年电子秤精品定制推荐,靠谱电子秤厂推荐与选购指南 - mypinpai
  • 宣和:以创新与品质领跑智能麻将机行业,创新新篇章 - 速递信息
  • NMN哪个品牌好?2026年10大NMN排行:口碑、价格与副作用全揭秘 - 速递信息
  • 2026年五星级酒店同款床垫推荐:五家优选质价比解析 - 速递信息
  • ClaudeOpus4.6震撼发布:AI界新王者降临
  • Python装饰器:动态增强函数的神器
  • 2026年权威指南:如何找到正规的danclan液氮罐/杜瓦罐/气相液氮罐总代理 - 品牌推荐大师1
  • 鞍山市英语雅思培训辅导机构推荐/2026权威出国雅思课程中心学校口碑排行榜 - 老周说教育
  • 邵阳车牌靓号代选,邵阳车牌靓号价格-上牌选号 - dasggg
  • 2026年深圳德耀星通科技有限公司市场口碑排名,专业德国保健品企业全解析 - 工业推荐榜
  • 2026年临沂短视频运营服务机构推荐:尚帝传媒助力企业数字化营销升级 - 速递信息
  • (10-1-03)模块集成与总装流程:模块化拆分与装配策略(3)软件模块接口
  • (10-2)模块集成与总装流程:装配流程与工具链
  • 探讨湖南专业GEO优化推广,哪家口碑好值得推荐? - 工业品网
  • 为什么脑机接口抗干扰测试成登月计划核心?
  • 雅安车牌靓号代选,雅安车牌靓号价格-上牌选号 - dasggg
  • php怎么实现订单接口状态轮询请求
  • ‌社群运营秘籍:测试从业者私域流量增长指南
  • 讲讲济南靠谱的短视频运营企业,山东领享网络科技怎么样 - 工业品牌热点