239_Redis_数据特殊类型_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对比
- 点赞
- 收藏
- 关注作者
评论(0)