239_Redis_数据特殊类型_Geo_HyperLogLog_BitMap

举报
alexsully 发表于 2021/11/27 11:41:42 2021/11/27
【摘要】 Geo_HyperLogLog_Bitmap类型&常用操作

三种特殊数据类型 Geo HyperLogLog_BitMap

1 GeoHash 算法计算 GEO地理位置 


Redis的GEO特性在Redis3.2版本中推出,可以地理位置信息储存起来,并对这些信息进行操作,geo的数据类型为zset(skipList)
GeoHash 算法将二维的经纬度映射到一维的整数, 距离最近的二维数据映射到一维上距离也是最近


注意事项:

Geo 数据结构的数据 全部放到一个zset集合中,如果Redis集群部署,且zset很大会导致数据迁移,性能不好,建议单个Key 不超过1M 且单独部署
如果数据过大 请考虑拆分实现,按城市/按国家, 目的是减小Zset 集合大小

实现场景:

来实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能。

1.1 常用命令:

geoadd
geopos
geodist
georadius
georadiusbymember
gethash


增删

增 geoadd

#语法
# geoadd<key>< longitude><latitude><member> [longitude latitude member...]   添加地理位置(经度,纬度,名称)
geoadd key longitude latitude member ...

#将给定的空间元素(纬度,经度,名字)添加到指定的键里面
#数据会以有序集合的形式被储存在键里面,使得georadius和georadiusbymember 可以在之后通过位置查询取得这些元素
# geoadd命令以标准的x,y格式接受参数,所以用户必须先输入经度,然后再输入纬度
# geoadd能够记录的坐标是有限的:非常接近两极的区域无法被索引
# 有效的经度介于-180-180度之间,有效的纬度介于-85.05112878 度至 85.05112878 度之间,超出会报错

127.0.0.1:6379> geoadd china:city 106.54 29.40 chongqing 108.93 34.23 xian 114.02 30.58 wuhan  116.23 40.22 beijing 121.48 31.40 shanghai 113.88 22.55 shenzhen 120.21 30.20 hangzhou
(integer) 7

127.0.0.1:6379> geopos china:city  chongqing
 1) "chongqing"
 2) "4026043655521786"
删 zrem 
GEO没有提供删除成员的命令,但是因为GEO的底层实现是zset,所以可以借用zrem命令实现对地理位置信息的删除
127.0.0.1:6379> zrange china:city 0 -1 # 查看全部的元素
127.0.0.1:6379> zrem china:city beijin # 移除元素

127.0.0.1:6379> ZRANGE china:city 0 -1 withscores
 1) "chongqing"
 2) "4026043655521786"
 3) "xian"
 4) "4040115258486138"


查 距离_geodist
geodist key member1 member2 [unit]
# 返回两个给定位置之间的距离,如果两个位置之间的其中一个不存在,那么命令返回空值。
# 指定单位的参数unit必须是以下单位的其中一个:
# m表示单位为米 # km表示单位为千米
# mi表示单位为英里 # ft表示单位为英尺
# 如果用户没有显式地指定单位参数,那么geodist默认使用米作为单位
#geodist命令在计算距离时会假设地球为完美的球形,在极限情况下,这一假设最大会造成0.5%的误差

127.0.0.1:6379> GEODIST china:city chongqing beijing km
"1491.6716"
127.0.0.1:6379> GEODIST china:city chongqing beijing m
"1491671.5628"

查 georadius
# 以给定的经纬度为中心, 找出某一半径内的元素
georadius key longitude latitude radius m|km|ft|mi [withcoord][withdist][withhash][asc|desc][count count]
127.0.0.1:6379> georadius china:city 100 30 1000 km
1) "chongqing"
2) "xian"

127.0.0.1:6379>  georadius china:city 100 30 1000 km withdist
1) 1) "chongqing"
   2) "635.2850"
2) 1) "xian"
   2) "963.3171"
   
# withdist 返回位置名称和中心距离
# withcoord 返回位置名称和经纬度
# withdist withcoord 返回位置名称 距离 和经纬度 count 限定寻找个数

127.0.0.1:6379> GEORADIUS china:city 100 30 1000 km  withdist withcoord count 1
1) 1) "chongqing"
   2) "635.2850"
   3) 1) "106.54000014066696167"
      2) "29.39999880018641676"
	  
	  
查 georadiusbymember
# 找出位于指定范围内的元素,中心点是由给定的位置元素决定
georadiusbymember key member radius m|km|ft|mi [withcoord][withdist][withhash][asc|desc][count count]

127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1100 km
1) "beijing"
2) "wuhan"
3) "shanghai"
4) "xian"


查 geohash
# Redis使用geohash将二维经纬度转换为一维字符串,字符串越长表示位置更精确,两个字符串越相似表示距离越近
geohash key member [member...]
127.0.0.1:6379> geohash china:city beijing  chongqing
1) "wx4sucu47r0"
2) "wm5z22h53v0"


HyperLogLog

Redis在2.8.9版本添加了HyperLogLog 结构做基数统计的算法; hyperloglog数据结构需要12KB的存储空间(稀疏矩阵)

HyperLogLog 优点

  • 在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。
  • 每个HyperLogLog 键只需要花费12KB内存,就可以计算接近 2^64 个不同元素的基数
  • HyperLogLog 是一种算法,它提供了不精确的去重计数方案

场景举例:

HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素

  • 统计网页的UV(浏览用户数量,一天内同一个用户多次访问只能算一次),
  • 传统方式:使用Set来保存用户id,然后统计Set中的元素数量来获取页面UV
  • 传统缺点:只能承载少量用户,一旦用户数量大起来就需要消耗大量的空间来存储用户id

hyperloglog: 统计用户数量而不是保存用户,使用Redis的HyperLogLog最多需要12k就可以统计大量的用户数,
尽管它大概有0.81%的错误率,但对于统计UV这种不需要很精确的数据是可以忽略不计的

基数
基数估计就是在误差可接受的范围内,快速计算基数。
比如数据集{1, 3, 5, 7, 5, 7, 8},那么这个数据集的基数集为{1, 3, 5 ,7, 8}, 基数(不重复元素)为5

常用命令 

增 
PFADD key element [element ...] 添加指定元素到 HyperLogLog中

查
PFCOUNT key [key ...]  #返回给定 HyperLogLog 的基数估算值

改 合并
#将多个HyperLogLog合并为一个HyperLogLog,并集计算
PFMERGE destkey sourcekey [sourcekey ...]

127.0.0.1:6379> PFADD hyperlog1 k1 k2 k3
(integer) 1
127.0.0.1:6379> pfadd hyperlog2 k1 k3 k4
(integer) 1
127.0.0.1:6379> PFCOUNT hyperlog1
127.0.0.1:6379> PFCOUNT hyperlog2
(integer) 3
127.0.0.1:6379> PFMERGE hyperlog3 hyperlog1 hyperlog2
OK
127.0.0.1:6379> PFCOUNT hyperlog3
(integer) 4


BitMap位图

位图不是特殊的数据结构,就是普通字符串(byte数组),位数组是自动扩展的,如果设置超出了偏移量现有内容范围,将会自动将位数组进行零填充

Redis提供了Bitmaps这个“数据类型”可以实现对位的操作

  • Bitmaps本身不是一种数据类型,实际上它就是字符串(key-value),但是它可以对字符串的位进行操作
  • Bitmaps单独提供了一套命令,所以在Redis中使用Bitmaps和使用字符串的方法不同.
  • Bitmaps想象成一个以位为单位的数组,数组的每个单元只能存储0和1, 数组的下标在Bitmaps中叫做偏移量

    背景&场景举例:

需要统计用户的某些信息,如活跃或不活跃,登录或者不登录
需要记录用户一年的打卡情况,打卡了是1, 没有打卡是0

缺点:

  • 如果使用普通的 key/value存储,则要记录365条记录,如果用户量很大,需要的空间也会很大
  • Bitmap 位图这中数据结构通过操作二进制位来进行记录,即为0和1;

例如:
如果要记录 365 天的打卡情况,使用 Bitmap表示的形式如:0101000111000111....................
优点:节约内存365 天相当于365bit,又 1 字节 = 8 bit , 所以相当于使用 46 个字节即可


BitMap 就是通过一个 bit 位来表示某个元素对应的值或者状态,其中的 key 就是对应元素本身,实际上底层也是通过对字符串的操作来实现。
Redis2.2 版本之后新增了setbit, getbit, bitcount 等几个bitmap 相关命令


常用操作

 增 setbit 设置操作

 SETBIT key offset value: 设置 key的第offset 位为value (1或0)
 # 使用bitmap来记录上述事例中一周的打卡记录如下所示:
 # 周一:1,周二:0,周三:1,周四:0,周五:1(1 为打卡,0 为不打卡)
 127.0.0.1:6379> setbit daka 0 1
(integer) 0
127.0.0.1:6379> SETBIT daka 1 0
(integer) 0
127.0.0.1:6379> setbit daka 2 1
(integer) 0
127.0.0.1:6379> setbit daka 3 0
(integer) 0
127.0.0.1:6379> setbit daka 4 1
(integer) 0


查 getbit 获取操作 GETBIT key offset 获取offset设置的值,未设置过默认返回0
127.0.0.1:6379> getbit daka 0
(integer) 1

查 bitcount 统计操作, 统计指定范围内 1 的个数
bitcount key [start, end] 统计key上 value为1的个数  如果指定[start , end]是字节索引,所以位范围必须是8的倍数

127.0.0.1:6379> BITCOUNT daka 0  -1
(integer) 3
127.0.0.1:6379> BITCOUNT daka
(integer) 3

127.0.0.1:6379> set sword3 hello
OK
127.0.0.1:6379> bitcount sword3
(integer) 21
127.0.0.1:6379> BITCOUNT sword3 0  0
(integer) 3
127.0.0.1:6379> BITCOUNT sword3 4 4
(integer) 6
127.0.0.1:6379> BITCOUNT sword3 5 5   #hello 5个字符, 字符索引0 ~4 
(integer) 0

查 bitpos 查找指定范围内 第一个0或1 
127.0.0.1:6379> BITPOS daka 1
(integer) 0
127.0.0.1:6379> BITPOS daka 0
(integer) 1
127.0.0.1:6379> BITPOS sword3 1 0 0    # 第一个字符中 的 第一个 1 出现的位置
(integer) 1

#h 01101000 
127.0.0.1:6379> BITFIELD sword3 get u4 0   #从第一位开始取 4个位,0110 -> 转10进制 6
1) (integer) 6
127.0.0.1:6379> BITFIELD sword3 get u3 2   # 从第三位开始取 3个位  101  -》 转10进制 5 
1) (integer) 5
127.0.0.1:6379>


常用操作2 设置某个字符串 h  "零存整取" & "整存零取"

常用操作2 设置某个字符串 h  "零存整取"
将h 转为二进制 ob 0110 1000  #设置位数组的前8位, 只需要设置为1的位,既 1/2/4
127.0.0.1:6379> setbit sword 1 1
(integer) 0
127.0.0.1:6379> setbit sword 2 1
(integer) 0
127.0.0.1:6379> setbit sword 4 1
(integer) 0
127.0.0.1:6379> get sword
"h"

常用操作2 整存零取
127.0.0.1:6379> set sword2 h
OK
127.0.0.1:6379> getbit sword2 1
(integer) 1
127.0.0.1:6379> getbit sword2 2
(integer) 1
127.0.0.1:6379> getbit sword2 4
(integer) 1
127.0.0.1:6379> getbit sword2 5
(integer) 0


bitop

bitop  and(or/not/xor) <destkey> [key…]

bitop是一个复合操作, 它可以做多个Bitmaps的and(交集) 、 or(并集) 、 not(非) 、 xor(异或) 操作并将结果保存在destkey中

2020-11-04 日访问网站的userid=1,2,5,9。
setbit unique:users:20201104 1 1
setbit unique:users:20201104 2 1
setbit unique:users:20201104 5 1
setbit unique:users:20201104 9 1



2020-11-03 日访问网站的userid=0,1,4,9。
setbit unique:users:20201103 0 1
setbit unique:users:20201103 1 1
setbit unique:users:20201103 4 1
setbit unique:users:20201103 9 1

计算出两天都访问过网站的用户数量
bitop and unique:users:and:20201104_03 unique:users:20201103unique:users:20201104

计算出任意一天都访问过网站的用户数量(例如月活跃就是类似这种) , 可以使用or求并集
bitop or unique:users:or:20201104_03 unique:users:20201103unique:users:20201104

Bitmaps与set对比

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。