三种特殊数据类型04

geospatial 地理位置

简介

Redis 的 GEO 特性在 Redis 3.2 版本中推出, 这个功能可以将用户给定的地理位置信息储存起来, 并对这些信息进行操作。来实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能。geo的数据类型为 zset。

GEO 的数据结构总共有六个常用命令:geoadd、geopos、geodist、georadius、 georadiusbymember、gethash

官方文档:https://www.redis.net.cn/order/3685.html

可以查询一些测试数据:http://www.jsons.cn/lngcode/

geoadd

功能:

将指定的地理空间位置(纬度、经度、名称)添加到指定的key中

解析:

# 语法
geoadd key longitude latitude member ...

# 将给定的空间元素(经度、纬度、名字)添加到指定的键里面。
# 这些数据会以有序集合的形式被储存在键里面,从而使得georadius和georadiusbymember这样的命令可以在之后通过位置查询取得这些元素。
# geoadd命令以标准的x,y格式接受参数,所以用户必须先输入经度,然后再输入纬度。
# geoadd能够记录的坐标是有限的:非常接近两极的区域无法被索引。我们一般会下载城市数据,直接通过Java程序一次导入
# 有效的经度介于-180-180度之间,有效的纬度介于-85.05112878 度至 85.05112878 度之间。
# 当用户尝试输入一个超出范围的经度或者纬度时,geoadd命令将返回一个错误。

测试:百度搜索经纬度查询,模拟真实数据

127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai 
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing 114.08 22.547 shenzhen
(integer) 2
127.0.0.1:6379> geoadd china:city 120.15 30.28 hangzhou 125.14 42.92 xian
(integer) 2

geopos

功能:

从key里返回所有给定位置元素的位置(经度和纬度)

应用场景:获得当前定位:一定是一个坐标值!

解析:

# 语法
geopos key member [member...]
#从key里返回所有给定位置元素的位置(经度和纬度)

测试:

127.0.0.1:6379> GEOPOS china:city beijing # 获取指定城市的经度和纬度
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
127.0.0.1:6379> GEOPOS china:city chongqing
1) 1) "106.49999767541885376"
   2) "29.52999957900659211"

geodist

功能:

返回两个给定位置之间的距离

解析:

# 语法
geodist key member1 member2 [unit]

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

测试:

127.0.0.1:6379> GEODIST china:city beijing shanghai		# 查看北京到上海的直线距离 单位:米
"1067378.7564"
127.0.0.1:6379> GEODIST china:city beijing shanghai km	# 查看北京到上海的直线距离 单位:千米
"1067.3788"
127.0.0.1:6379> GEODIST china:city beijing chongqing km	# 查看北京到重庆的直线距离 单位:千米
"1464.0708"

georadius

功能:

以给定的经纬度为中心, 找出某一半径内的元素

解析:

# 语法
georadius key longitude latitude radius m|km|ft|mi [withcoord][withdist]
[withhash][asc|desc][count count]

# 以给定的经纬度为中心, 找出某一半径内的元素

测试:重新连接 redis-cli,增加参数 --raw ,可以强制输出中文,不然会乱码 redis-cli --raw -p 6379

127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km # 在 china:city 中寻找坐标 110 30 半径为 1000km 的城市
1) "chongqing"
2) "shenzhen"
3) "hangzhou"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km
1) "chongqing"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist # withdist 返回位置名称和中心距离
1) 1) "chongqing"
   2) "341.9374"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord # withcoord 返回位置名称和经纬度
1) 1) "chongqing"
   2) 1) "106.49999767541885376"
      2) "29.52999957900659211"
      
# withdist withcoord 返回位置名称 距离 和经纬度 count 限定寻找个数
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist withcoord count 1 
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885376"
      2) "29.52999957900659211"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist withcoord count 3
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885376"
      2) "29.52999957900659211"
2) 1) "shenzhen"
   2) "923.2321"
   3) 1) "114.08000081777572632"
      2) "22.54699993773966327"
3) 1) "hangzhou"
   2) "976.4868"
   3) 1) "120.15000075101852417"
      2) "30.2800007575645509"

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 1000 km
1) "beijing"
2) "xian"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city shanghai 400 km
1) "hangzhou"
2) "shanghai"

geohash

功能:

回一个或多个位置元素的 Geohash 表示

解析:

# 语法
geohash key member [member...]
# Redis使用geohash将二维经纬度转换为一维字符串,该命令将返回11个字符的Geohash字符串,两个字符串越相似表示距离越近。

测试:

127.0.0.1:6379> geohash china:city beijing chongqing
1) "wx4fbxxfke0"
2) "wm5xzrybty0"

zrem

GEO没有提供删除成员的命令,但是因为GEO的底层实现是zset,所以可以借用zrem命令实现对地理位置信息的删除.

我们可以使用zSet命令来操控geo

127.0.0.1:6379> ZRANGE china:city 0 -1
1) "chongqing"
2) "shenzhen"
3) "hangzhou"
4) "shanghai"
5) "beijing"
6) "xian"
127.0.0.1:6379> zrem china:city beijing
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "chongqing"
2) "shenzhen"
3) "hangzhou"
4) "shanghai"
5) "xian"

HyperLogLog 基数统计

简介

Redis 在 2.8.9 版本添加了 HyperLogLog 结构。

Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积 非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。

在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。

HyperLogLog则是一种算法,它提供了不精确的去重计数方案。

举个栗子:假如我要统计网页的UV(浏览用户数量,一天内同一个用户多次访问只能算一次),传统的解决方案是使用Set来保存用id,然后统计Set中的元素数量来获取页面UV。但这种方案只能承载少量 用户,一旦用户数量大起来就需要消耗大量的空间来存储用户id。我的目的是统计用户数量而不是保存 用户,这简直是个吃力不讨好的方案!而使用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 的基数估算值。
[PFMERGE destkey sourcekey [sourcekey ...] 将多个 HyperLogLog 合并为一个 HyperLogLog,并集计算

测试

127.0.0.1:6379> PFADD mykey a b c d e f g h i j		# 创建第一组元素 mykey
(integer) 1
127.0.0.1:6379> PFCOUNT mykey	# 统计 mykey 元素的基数数量
(integer) 10
127.0.0.1:6379> PFADD mykey2 i j z x c v b n m		# 创建第二组元素 mykey2
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 9
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2		# 合并两组 mykey mykey2 => mykey3 并集
OK
127.0.0.1:6379> PFCOUNT mykey3	# 看并集的数量
(integer) 15

如果允许容错,那么一定可以使用 HyperLogLog !

如果不允许容错,那就使用 set 或者自己的数据类型即可!

BitMap 位图场景

简介

在开发中,可能会遇到这种情况:需要统计用户的某些信息,如活跃或不活跃,登录或者不登录;又如 需要记录用户一年的打卡情况,打卡了是1, 没有打卡是0,如果使用普通的 key/value存储,则要记录 365条记录,如果用户量很大,需要的空间也会很大,所以 Redis 提供了 Bitmap 位图这中数据结构, Bitmap 就是通过操作二进制位来进行记录,即为 0 和 1;如果要记录 365 天的打卡情况,使用 Bitmap 表示的形式大概如下:0101000111000111...........................,这样有什么好处呢?当然就是节约内存 了,365 天相当于 365 bit,又 1 字节 = 8 bit , 所以相当于使用 46 个字节即可。

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

setbit 设置操作

SETBIT key offset value : 设置 key 的第 offset 位为value (1或0)

# 使用 bitmap 来记录上述事例中一周的打卡记录如下所示:
# 周一:1,周二:0,周三:0,周四:1,周五:1,周六:0,周天:0 (1 为打卡,0 为不打卡)
127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(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 0
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0

getbit 获取操作

GETBIT key offset 获取offset设置的值,未设置过默认返回0

127.0.0.1:6379> getbit sign 3	# 查看周四是否打卡
(integer) 1
127.0.0.1:6379> getbit sign 6	# 查看周七是否打卡
(integer) 0

bitcount 统计操作

bitcount key [start, end] 统计 key 上位为1的个数

# 统计这周打卡的记录,可以看到只有3天是打卡的状态:
127.0.0.1:6379> bitcount sign
(integer) 3
posted @ 2022-02-13 00:14  EdgarStudy  阅读(46)  评论(0编辑  收藏  举报