Redis 三种特殊类型
Geospatial(地理空间)
朋友圈的定位、附近的人、打车距离 … 如何计算?
Redis 的Geo在Redis3.2版本就推出了,这个功能可以推算出地理位置的信息,两地之间的距离、方圆几里的人
geoadd 添加一个或多个地理空间位置(经度、纬度、名称)到添加到指定的key中
-
参数:key 值(经度、纬度、名称)
-
两极地区无法直接添加
-
一般会下载城市数据,直接通过程序一次性导入
-
有效的经度从-180度到180度
-
有效的纬度从-85.05112878度到85.05112878度
当坐标位置超出上述指定范围时,该命令将会返回一个错误。
127.0.0.1:6379> geoadd china:city 116.405289 39.904987 beijing # 添加一个
(integer) 1
127.0.0.1:6379> geoadd china:city 121.472641 31.231707 shanghai 113.28064 23.125177 guangzhou # 添加多个
(integer) 2
127.0.0.1:6379> geoadd china:city 114.085945 22.547001 shenzhen 120.15358 30.287458 hangzhou
(integer) 2
geopos 返回地理空间的经纬度
127.0.0.1:6379> geopos china:city beijing
1) 1) "116.40528827905654907"
2) "39.90498588819134085"
127.0.0.1:6379> geopos china:city shanghai
1) 1) "121.47264093160629272"
2) "31.23170744181923197"
geodist 返回两个给定位置之间的距离
如果两个位置之间的其中一个不存在, 那么命令返回空值。
指定单位的参数 unit 必须是以下单位的其中一个:
- m 表示单位为米。
- km 表示单位为千米。
- mi 表示单位为英里。
- ft 表示单位为英尺。
如果用户没有显式地指定单位参数, 那么 GEODIST
默认使用米作为单位。
127.0.0.1:6379> geodist china:city beijing shanghai # 北京到上海直线距离(米)
"1067597.0432"
127.0.0.1:6379> geodist china:city beijing shanghai km # 千米
"1067.5970"
georadius 以给定的经纬度为中心, 找出某一半径内的元素
范围可以使用以下其中一个单位:
- m 表示单位为米。
- km 表示单位为千米。
- mi 表示单位为英里。
- ft 表示单位为英尺。
在给定以下可选项时, 命令会返回额外的信息:
WITHDIST
: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。WITHCOORD
: 将位置元素的经度和纬度也一并返回。
127.0.0.1:6379> georadius china:city 110 20 500 km # 以110,20 这个经纬度为中心,寻找半径500km以内的城市
1) "guangzhou"
127.0.0.1:6379> georadius china:city 110 20 1000 km # 半径1000km以内
1) "shenzhen"
2) "guangzhou"
127.0.0.1:6379> georadius china:city 110 20 1000 km withcoord # withcoord 额外返回经纬度
1) 1) "shenzhen"
2) 1) "114.08594459295272827"
2) "22.54699993773966327"
2) 1) "guangzhou"
2) 1) "113.28063815832138062"
2) "23.12517743834835215"
127.0.0.1:6379> georadius china:city 110 20 1000 km withdist # withdist 额外返回到中心的直线距离
1) 1) "shenzhen"
2) "509.4622"
2) 1) "guangzhou"
2) "485.7406"
127.0.0.1:6379> georadius china:city 110 20 1000 km withcoord withdist count 1 # count 获取前 N 个匹配元素
1) 1) "guangzhou"
2) "485.7406"
3) 1) "113.28063815832138062"
2) "23.12517743834835215"
georadiusbymember 找出位于指定范围内的元素,中心点是由给定的位置元素决定
这个命令和 GEORADIUS 命令一样, 都可以找出位于指定范围内的元素, 但是 GEORADIUSBYMEMBER
的中心点是由给定的位置元素决定的, 而不是像 GEORADIUS 那样, 使用输入的经度和纬度来决定中心点
指定成员的位置被用作查询的中心。
127.0.0.1:6379> georadiusbymember china:city guangzhou 1000 km # 以广州为中心,查询半径1000km以内的城市
1) "shenzhen"
2) "guangzhou"
127.0.0.1:6379> georadiusbymember china:city shanghai 500 km # 以上海为中心,查询半径500km以内的城市
1) "hangzhou"
2) "shanghai"
geohash 返回一个或多个位置元素的 Geohash 表示
该命令将返回11个字符的Geohash字符串
将二维的经纬度转换为一维的字符串,如果两个字符串越接近,则代表该距离越近
127.0.0.1:6379> geohash china:city guangzhou # 返回一个
1) "ws0e9cb3yj0"
127.0.0.1:6379> geohash china:city guangzhou shenzhen # 返回多个
1) "ws0e9cb3yj0"
2) "ws10k0dcg10"
小结
Geo 底层的实现原理其实就是Zset,我们可以使用Zset命令来实现Geo
# 使用Zset命令操作Geo
127.0.0.1:6379> zrange china:city 0 -1 # 查看地图中的全部元素
1) "shenzhen"
2) "guangzhou"
3) "hangzhou"
4) "shanghai"
5) "beijing"
127.0.0.1:6379> zrem china:city hangzhou # 移除指定元素
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "shenzhen"
2) "guangzhou"
3) "shanghai"
4) "beijing"
HyperLogLog(基数统计)
Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
什么是基数?
比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。
统计网站的UV?(uv 访客数,一天内同个访客多次访问仅计算一个UV)
传统方式:
- set保存用户的id,然后就可以统计set中元素数量作为判断标准。
- 如果保存大量的用户id,相对麻烦,我们的目的是计数而不是为了保存用的id
HyperLogLog :
- 占用的内存是固定的,计算 2^64 个不同元素的基数,只需要花费 12 KB 内存。
- 如果从内存的角度来比较的话 HyperLogLog 首选
- 0.81%错误率,统计UV,可以忽略不计
# pfadd 添加指定元素到 HyperLogLog 中
# pfcount 返回给定 HyperLogLog 的基数估算值
# pfmerge 将多个 HyperLogLog 合并为一个 HyperLogLog
127.0.0.1:6379> pfadd mykey1 A B C D # 创建第一组元素 mykey1
(integer) 1
127.0.0.1:6379> pfcount mykey1 # 统计 mykey1 元素的基数值
(integer) 4
127.0.0.1:6379> pfadd mykey2 C D E F # 创建第一组元素 mykey2
(integer) 1
127.0.0.1:6379> pfcount mykey2 # 统计 mykey2 元素的基数值
(integer) 4
127.0.0.1:6379> pfmerge mykey3 mykey1 mykey2 # 合并 mykey1 + mykey2 => mykey3(并集)
OK
127.0.0.1:6379> pfcount mykey3 # 合并后的基数值
(integer) 6
Bitmaps(位图)
什么是 Bitmaps ?
Bitmaps是一种数据结构,通过操作二进制的位来记录,只有 0 或 1两种状态
Bitmaps 并不是实际的数据类型,而是定义在String类型上的一个面向字节操作的集合。因为字符串是二进制安全的块,他们的最大长度是512M,最适合设置成2^32个不同字节。
Bitmaps 的最大优势之一在存储信息时极其节约空间。
在计算机系统中,最小的信息单位是字节,1个字节等于8位,每一位都只可能是0或1(计算机只认识这两个数)。使用Bitmaps可以直接对位进行操作。
可以把bigmaps看做一个数组,数组里每一位只可能是0或者1,数组的下标在这里看做偏移量
可用于位存储,使用场景:
- 打卡
- 活跃度:活跃、不活跃
- 登录状态:登录、未登录
- 两个状态的切换、表示…
使用bitmaps来记录周一到周日打卡状态
# setbit 对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)
# getbit 对 key 所储存的字符串值,获取指定偏移量上的位(bit)
# bitcount 统计字符串被设置为1的bit数
# 0 未打卡,1 已打卡
127.0.0.1:6379> setbit sign 0 0 # 周日 未打卡
(integer) 0
127.0.0.1:6379> setbit sign 1 1 # 周一 已打卡
(integer) 0
127.0.0.1:6379> setbit sign 2 1 # 周二 已打卡
(integer) 0
127.0.0.1:6379> setbit sign 3 1 # 周三 已打卡
(integer) 0
127.0.0.1:6379> setbit sign 4 1 # 周四 已打卡
(integer) 0
127.0.0.1:6379> setbit sign 5 1 # 周五 已打卡
(integer) 0
127.0.0.1:6379> setbit sign 6 0 # 周六 未打卡
(integer) 0
127.0.0.1:6379> getbit sign 1 # 获取周一打卡状态,已打卡
(integer) 1
127.0.0.1:6379> bitcount sign # 统计一周打卡天数
(integer) 5