12 | 有一亿个keys要统计,应该用哪种集合?

2024-04-20 21:32

本文主要是介绍12 | 有一亿个keys要统计,应该用哪种集合?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • Redis核心技术与实战
    • 实践篇
      • 12 | 有一亿个keys要统计,应该用哪种集合?
        • 聚合统计
        • 排序统计
        • 二值状态统计
        • 基数统计
        • Redis 集合类型比较


Redis核心技术与实战

实践篇

12 | 有一亿个keys要统计,应该用哪种集合?

要想选择合适的集合,就得了解常用的集合统计模式。集合类型常见的四种统计模式,包括聚合统计、排序统计、二值状态统计和基数统计。

聚合统计

聚合统计,就是指统计多个集合元素的聚合结果,包括:统计多个集合的共有元素(交集统计);把两个集合相比,统计其中一个集合独有的元素(差集统计);统计多个集合的所有元素(并集统计)。

应用场景: 统计手机 App 每天的新增用户数和第二天的留存用户数

解决方案: 用一个集合记录所有登录过 App 的用户 ID,同时,用另一个集合记录每一天登录过 App 的用户 ID。然后,再对这两个集合做聚合统计。

记录所有登录过 App 的用户 ID 可以直接使用 Set 类型,把 key 设置为 user:id,表示记录的是用户 ID,value 就是一个 Set 集合,里面是所有登录过 App 的用户 ID,把这个 Set 叫作累计用户 Set,如下图所示:

在这里插入图片描述

接统计每天的新增用户,key 是 user:id 以及当天日期,例如 user : id :20200803,value 是 Set 集合,记录当天登录的用户 ID。如下图所示:

在这里插入图片描述

假设手机 App 在 2020 年 8 月 3 日上线,那么,8 月 3 日前是没有用户的。此时,累计用户 Set 是空集,当天登录的用户 ID 会被记录到 key 为 user : id: 20200803 的 Set 中。所以,user : id : 20200803 这个 Set 中的用户就是当天的新增用户。

然后,计算累计用户 Set 和 user : id : 20200803 Set 的并集(SUNIONSTORE) 结果,结果保存在 user:id 这个累计用户 Set 中,如下所示:

SUNIONSTORE  user:id  user:id  user:id:20200803 

等到 8 月 4 日再统计时,把 8 月 4 日登录的用户 ID 记录到 user : id: 20200804 的 Set 中。接下来,计算累计用户 Set 和 user : id : 20200804 Set 的差集(SDIFFSTORE),结果保存在 key 为 user:new 的 Set 中,如下所示:

SDIFFSTORE  user:new  user:id:20200804 user:id  

计算 8 月 4 日的留存用户时,只需要再计算 user : id : 20200803 和 user : id : 20200804 两个 Set 的交集(SINTERSTORE)

Set 的差集、并集和交集的计算复杂度较高,在数据量较大的情况下,如果直接执行这些计算,会导致 Redis 实例阻塞。所以,可以从主从集群中选择一个从库,让它专门负责聚合计算,或者是把数据读取到客户端,在客户端来完成聚合统计,这样可以规避阻塞主库实例和其他从库实例的风险。

排序统计

应用场景: 电商网站上提供最新评论列表

解决方案: 要求集合类型能对元素保序

在 Redis 常用的 4 个集合类型中(List、Hash、Set、Sorted Set),List 和 Sorted Set 属于有序集合。
List 是按照元素进入 List 的顺序进行排序,而 Sorted Set 可以根据元素的权重来排序。

  • 采用 List 的情况。每个商品对应一个 List,这个 List 包含了对这个商品的所有评论,而且会按照评论时间保存这些评论,每来一个新评论,就用 LPUSH 命令把它插入 List 的队头。
    一旦涉及到分页操作,List 就可能会出现问题。 原因在于,List 是通过元素在 List 中的位置来排序的,当有一个新元素插入时,原先的元素在 List 中的位置都后移了一位。所以,对比新元素插入前后,List 相同位置上的元素就会发生变化,用 LRANGE 读取时,就会读到旧元素。
  • 采用 Sorted Set 的情况。可以按评论时间的先后给每条评论设置一个权重值,然后再把评论保存到 Sorted Set 中。Sorted Set 的 ZRANGEBYSCORE 命令就可以按权重排序后返回元素。这样的话,即使集合中的元素频繁更新,Sorted Set 也能通过 ZRANGEBYSCORE 命令准确地获取到按序排列的数据。

在面对需要展示最新列表、排行榜等场景时,如果数据更新频繁或者需要分页显示,建议优先考虑使用 Sorted Set。

二值状态统计

二值状态就是指集合元素的取值就只有 0 和 1 两种。

应用场景: 用户签到

解决方案: 选择 Bitmap

Bitmap 本身是用 String 类型作为底层数据结构实现的一种统计二值状态的数据类型。 可以把 Bitmap 看作是一个 bit 数组。

Bitmap 提供了 GETBIT/SETBIT 操作,使用一个偏移值 offset 对 bit 数组的某一个 bit 位进行读和写。不过,需要注意的是,Bitmap 的偏移量是从 0 开始算的,也就是说 offset 的最小值是 0。当使用 SETBIT 对一个 bit 位进行写操作时,这个 bit 位会被设置为 1。Bitmap 还提供了 BITCOUNT 操作,用来统计这个 bit 数组中所有“1”的个数。

统计 ID 3000 的用户在 2020 年 8 月份的签到情况

  1. 第一步,执行下面的命令,记录该用户 8 月 3 号已签到。
SETBIT uid:sign:3000:202008 2 1 

key = uid:sign:3000:202008 ;offset = 2(因为是3号); value = 1

  1. 第二步,检查该用户 8 月 3 日是否签到。
GETBIT uid:sign:3000:202008 2 
  1. 第三步,统计该用户在 8 月份的签到次数。
BITCOUNT uid:sign:3000:202008

统计出 10 天连续签到的用户总数
Bitmap 支持用 BITOP 命令对多个 Bitmap 按位做“与”“或”“异或”的操作,操作的结果会保存到一个新的 Bitmap 中。
三个 Bitmap bm1、bm2 和 bm3,对应 bit 位做“与”操作,结果保存到了一个新的 Bitmap 中(示例中,这个结果 Bitmap 的 key 被设为“resmap”)。

在这里插入图片描述

在统计 1 亿个用户连续 10 天的签到情况时,把每天的日期作为 key,每个 key 对应一个 1 亿位的 Bitmap,每一个 bit 对应一个用户当天的签到情况。接下来,对 10 个 Bitmap 做“与”操作,得到的结果也是一个 Bitmap。在这个 Bitmap 中,只有 10 天都签到的用户对应的 bit 位上的值才会是 1。最后,用 BITCOUNT 统计 Bitmap 中的 1 的个数,即为连续签到 10 天的用户总数。

基数统计

基数统计就是指统计一个集合中不重复的元素个数。

应用场景: 网页 UV 统计

解决方案: Redis 的集合类型中,Set 类型默认支持去重。

有一个用户 user1 访问 page1 时,把这个信息加到 Set 中。

SADD page1:uv user1

当需要统计 UV 时,直接用 SCARD 命令返回一个集合中的元素个数。

如果 page1 非常火爆,UV 达到了千万,这个时候,一个 Set 就要记录千万个用户 ID。对于一个搞大促的电商网站而言,这样的页面可能有成千上万个,如果每个页面都用这样的一个 Set,就会消耗很大的内存空间。

也可以用 Hash 类型记录 UV。

把用户 ID 作为 Hash 集合的 key,当用户访问页面时,就用 HSET 命令(用于设置 Hash 集合元素的值),对这个用户 ID 记录一个值“1”,表示一个独立访客,用户 1 访问 page1 后,就记录为 1 个独立访客,如下所示:

HSET page1:uv user1 1

即使用户 1 多次访问页面,重复执行这个 HSET 命令,也只会把 user1 的值设置为 1,仍然只记为 1 个独立访客。当要统计 UV 时,可以用 HLEN 命令统计 Hash 集合中的所有元素个数。

Hash 类型和 Set 类型相似,当页面很多时,会消耗很大的内存空间。

有什么办法既能完成统计,还能节省内存?

HyperLogLog 是一种用于统计基数的数据集合类型,它的最大优势就在于,当集合元素数量非常多时,它计算基数所需的空间总是固定的,而且还很小。

在 Redis 中,每个 HyperLogLog 只需要花费 12 KB 内存,就可以计算接近 2^64 个元素的基数。
在统计 UV 时,用 PFADD 命令(用于向 HyperLogLog 中添加新元素) 把访问页面的每个用户都添加到 HyperLogLog 中。

PFADD page1:uv user1 user2 user3 user4 user5

PFCOUNT 命令直接获得 page1 的 UV 值

PFCOUNT page1:uv

HyperLogLog 的统计规则是基于概率完成的,所以它给出的统计结果有一定误差,标准误算率是 0.81%。 这也就意味着,使用 HyperLogLog 统计的 UV 是 100 万,但实际的 UV 可能是 101 万。虽然误差率不算大,但是,如果需要精确统计结果的话,最好还是继续用 Set 或 Hash 类型。

Redis 集合类型比较

在这里插入图片描述

这篇关于12 | 有一亿个keys要统计,应该用哪种集合?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/921368

相关文章

hdu1496(用hash思想统计数目)

作为一个刚学hash的孩子,感觉这道题目很不错,灵活的运用的数组的下标。 解题步骤:如果用常规方法解,那么时间复杂度为O(n^4),肯定会超时,然后参考了网上的解题方法,将等式分成两个部分,a*x1^2+b*x2^2和c*x3^2+d*x4^2, 各自作为数组的下标,如果两部分相加为0,则满足等式; 代码如下: #include<iostream>#include<algorithm

购买磨轮平衡机时应该注意什么问题和技巧

在购买磨轮平衡机时,您应该注意以下几个关键点: 平衡精度 平衡精度是衡量平衡机性能的核心指标,直接影响到不平衡量的检测与校准的准确性,从而决定磨轮的振动和噪声水平。高精度的平衡机能显著减少振动和噪声,提高磨削加工的精度。 转速范围 宽广的转速范围意味着平衡机能够处理更多种类的磨轮,适应不同的工作条件和规格要求。 振动监测能力 振动监测能力是评估平衡机性能的重要因素。通过传感器实时监

uva 11178 计算集合模板题

题意: 求三角形行三个角三等分点射线交出的内三角形坐标。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <

cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个?

跨平台系列 cross-plateform 跨平台应用程序-01-概览 cross-plateform 跨平台应用程序-02-有哪些主流技术栈? cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个? cross-plateform 跨平台应用程序-04-React Native 介绍 cross-plateform 跨平台应用程序-05-Flutte

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

hdu4267区间统计

题意:给一些数,有两种操作,一种是在[a,b] 区间内,对(i - a)% k == 0 的加value,另一种操作是询问某个位置的值。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import

hdu4417区间统计

给你一个数列{An},然后有m次查询,每次查询一段区间 [l,r] <= h 的值的个数。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamRead

hdu3333区间统计

题目大意:求一个区间内不重复数字的和,例如1 1 1 3,区间[1,4]的和为4。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;

实例:如何统计当前主机的连接状态和连接数

统计当前主机的连接状态和连接数 在 Linux 中,可使用 ss 命令来查看主机的网络连接状态。以下是统计当前主机连接状态和连接主机数量的具体操作。 1. 统计当前主机的连接状态 使用 ss 命令结合 grep、cut、sort 和 uniq 命令来统计当前主机的 TCP 连接状态。 ss -nta | grep -v '^State' | cut -d " " -f 1 | sort |

Java基础回顾系列-第六天-Java集合

Java基础回顾系列-第六天-Java集合 集合概述数组的弊端集合框架的优点Java集合关系图集合框架体系图java.util.Collection接口 List集合java.util.List接口java.util.ArrayListjava.util.LinkedListjava.util.Vector Set集合java.util.Set接口java.util.HashSetjava