Redis使用场景

场景1:基数统计

  基数统计是指统计一个集合内不重复的元素,常见应用场景有日活DAU、月活MAU、网页UV等;

方案一:Set

  • 特点:存储统计元素、精确统计,不适用于海量数据场景,占用内存且会阻塞Redis主线程;
  • 说明:如果一定要使用set处理海量数据,可以在从机执行,或者返回客户端执行数据聚合;

方案二:Bitmap

  • 特点:仅标记元素是否存在、精确统计,每个统计元素用1bit表示是否存在,1亿元素占用约12MB内存;
  • 日活DAU:使用bitmap表示每天的DAU,每个用户占用1bit,访问时对应bit设置为1,bitcount统计1的数量;
  • 月活MAU:对30天的bitmap进行或运算,bitcount统计1的数量;

方案三:HyperLogLog(推荐)

  • 特点:仅标记元素是否存在,概率统计、存在一定误差,海量数据场景下,占用空间固定为12KB;
  • 日活DAU:使用HyperLogLog表示每天的DAU,统计的过程就是不断PFADD添加元素的过程,PFCOUNT获取基数值;
  • 月活MAU:使用PMERGE对30天的HyperLogLog进行合并,PFCOUNT获取基数值;

场景2:排序

  对集合内的元素按照某个维度进行排序,常见应用场景有排行榜、最新评论列表等;

方案一:List

  • 特点:只能按照访问的先后顺序单个维度排序;

方案二:ZSet

  • 特点:有序、唯一,可以按照任意一个维度排序,只需将该维度的优先级设置为元素的权重即可;

场景3:LBS场景

方案:GEO

  LBS(Location Based Service),基于距离的服务,常见场景如附近的人、附近的商家等。GEO是专门针对LBS场景的数据结构,底层数据结构仍是ZSet。
  GEO原理:首选,通过GeoHash编码将经纬度(二维信息)生成一个编码(一维信息),该编码作为元素的权重存储在ZSet中;然后,通过ZSet的范围查询获取到附近的距离信息;

  • GEOADD命令:用于把一组经纬度信息和相对应的一个 ID 记录到 GEO 类型集合中;
  • GEORADIUS命令:会根据输入的经纬度位置,查找以这个经纬度为中心的一定范围内的其他元素;

原理:GeoHash编码

编码过程

  1. 首先,分别对经纬度编码。纬度区间是[-90,90],经度区间是[-180,180],不断利用二分查找,确定定位点所在的区间,左边为0,右边为1,以此得到二进制编码,编码位数越多表示精度越高;
  2. 其次,对经纬度的二进制串组码,偶数位放经度,奇数位放纬度,组成新的二进制串,然后进行Base32编码,得到最终的GeoHash编码;

使用注意

问题1:突变性

  • 问题:大多情况下,编码越相似,表明距离越近,但GeoHash编码存在突变型,有些编码相邻但距离却相差很远;
    Redis使用场景

  • 解决方案:先通过GeoHash编码获取到相邻的区域,然后通过计算实际地理距离进行过滤;

问题2:边界性

  • 问题: 由于GeoHash是将区域划分为一个个规则矩形,并对每个矩形进行编码,这样在查询附近POI信息时会导致以下问题,比如红色的点是我们的位置,绿色的两个点分别是附近的两个餐馆,但是在查询的时候会发现距离较远餐馆的GeoHash编码与我们一样(因为在同一个GeoHash区域块上),而较近餐馆的GeoHash编码与我们不一致,如下图所示:Redis使用场景
  • 解决方案: 先通过GeoHash编码获取到定位点以及周边总共9个区域,然后计算实际地理距离进行排序;

场景4:二值状态统计

  二值状态是指集合内元素的值只有0和1两种,常见应用场景有签到打卡

方案:Bitmap

  • 每个用户使用一个bitmap记录签到情况,每个bit表示某天是否签到;
  • 特点:占用内存小,且可以利用位操作,高效;

参考:

  1. GeoHash核心原理解析