​ 在上一篇文章中我们讲了五大基本类型,他们是最常用的,而且百分之九十的程序员对于Redis只限于了解String这种最常用的。但是我个人认为,既然Redis官方提供了其他的数据类型,肯定是有相应的考量的,在某些特殊的业务场景中,这些特殊的类型还是能够给我们多一种解决思路!那么,这个学习就是值得的!!!接下来我们就来讲一讲三大特殊类型。

Geospatial: 地理位置

简介

​ Geospatial,地理空间。
​ Redis 在 3.2 版本中引入了 Geospatial 这种新的数据类型。该类型本质上仍是一种集合,只不过集合元素比较特殊,是一种由三部分构成的数据结构,这种数据结构称为空间元素:

  • 经度:longitude。有效经度为[-180,180]。正的表示东经,负的表示西经。
  • 纬度:latitude。有效纬度为[-85.05112878, 85.05112878]。正的表示北纬,负的表示南纬。
  • 位置名称:为该经纬度所标注的位置所命名的名称,也称为该 Geospatial 集合的空间元素名称。

​ 通过该类型可以设置、查询某地理位置的经纬度,查询某范围内的空间元素,计算两空间元素间的距离等。

快速上手

城市经纬度查询:

注意点1:两极无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入!

注意点2:有效的经度从-180度到180度。

注意点3:有效的纬度从-85.05112878度到85.05112878度。

注意点4:m 为米。km 为千米。mi 为英里。ft 为英尺。

①geoadd(添加)、geopos(查看)、geodist(计算距离)操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
127.0.0.1:6379> geoadd city 118.8921 31.32751 nanjing 197.30794 31.79322  
#当经纬度其中一个或者两个超过界限值,报错,信息如下:
(error) ERR syntax error. Try GEOADD key [x1] [y1] [name1] [x2] [y2] [name2] ...
#添加城市经纬度 语法格式: geoadd key 经度 纬度 name +++可多个添加
#添加成功后返回添加成功的数量值
127.0.0.1:6379> geoadd city 118.8921 31.32751 nanjing 117.30794 31.79322 hefei 102.82147 24.88554 kunming 91.13775 29.65262 lasa 116.23128 40.22077 beijing 106.54041 29.40268 chongqing
(integer) 6
127.0.0.1:6379> ZRANGE city 0 -1 #注意:geo的查看方式和zset的命令是一致的,
#由此可知,geo本质上还是个集合,不过Redis官方对其进行了二次封装
1) "lasa"
2) "kunming"
3) "chongqing"
4) "hefei"
5) "nanjing"
6) "beijing"
127.0.0.1:6379> geopos city nanjing #查看看指定城市的经纬度信息
1) 1) "118.89209836721420288"
2) "31.32750976275760735"
127.0.0.1:6379> geopos city nanjing beijing #查看看多个城市的经纬度信息
1) 1) "118.89209836721420288"
2) "31.32750976275760735"
2) 1) "116.23128265142440796"
2) "40.22076905438526495"
127.0.0.1:6379> geodist city nanjing beijing #计算南京到北京之间的距离,默认返回单位是m
"1017743.1413"
127.0.0.1:6379> geodist city nanjing beijing km #km 千米
"1017.7431"
127.0.0.1:6379> geodist city nanjing beijing mi #mi 英里
"632.3978"
127.0.0.1:6379> geodist city nanjing beijing ft #ft 英尺
"3339052.3010"

②georadius(查询附近位置)操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
127.0.0.1:6379> ZRANGE city 0 -1  #查看城市
1) "lasa"
2) "kunming"
3) "chongqing"
4) "hefei"
5) "nanjing"
6) "beijing"
#查看指定位置的1000公里范围内有哪些城市
127.0.0.1:6379> georadius city 120 38 1000 km
1) "beijing"
2) "hefei"
3) "nanjing"
127.0.0.1:6379> georadius city 120 38 400 km #查看指定位置的400公里范围内有哪些城市
(empty array)
127.0.0.1:6379> georadius city 120 38 550 km #查看指定位置的550公里范围内有哪些城市
1) "beijing"
#查看指定位置的550公里范围内有哪些城市,withcoord指定返回城市的name
127.0.0.1:6379> georadius city 120 38 1000 km withcoord
1) 1) "beijing"
2) 1) "116.23128265142440796"
2) "40.22076905438526495"
2) 1) "hefei"
2) 1) "117.30793744325637817"
2) "31.79321915080526395"
3) 1) "nanjing"
2) 1) "118.89209836721420288"
2) "31.32750976275760735"
#查看指定位置的550公里范围内有哪些城市,withdist指定返回城市的’经纬度‘值
127.0.0.1:6379> georadius city 120 38 1000 km withcoord withdist
1) 1) "beijing"
2) "408.3496"
3) 1) "116.23128265142440796"
2) "40.22076905438526495"
2) 1) "hefei"
2) "732.6371"
3) 1) "117.30793744325637817"
2) "31.79321915080526395"
3) 1) "nanjing"
2) "749.0265"
3) 1) "118.89209836721420288"
2) "31.32750976275760735"
#查看指定位置的550公里范围内有哪些城市,withhash指定返回城市的’经纬度‘的hash值
#如果两个城市的hash值越’像‘,证明城市距离越近!
127.0.0.1:6379> georadius city 120 38 1000 km withcoord withdist withhash
1) 1) "beijing"
2) "408.3496"
3) (integer) 4069896088584598
4) 1) "116.23128265142440796"
2) "40.22076905438526495"
2) 1) "hefei"
2) "732.6371"
3) (integer) 4052763834193093
4) 1) "117.30793744325637817"
2) "31.79321915080526395"
3) 1) "nanjing"
2) "749.0265"
3) (integer) 4054278565840695
4) 1) "118.89209836721420288"
2) "31.32750976275760735"
#查看指定位置的550公里范围内有哪些城市,count num 指定返回’num‘个城市数据量
127.0.0.1:6379> georadius city 120 38 1000 km withcoord withdist withhash count 2
1) 1) "beijing"
2) "408.3496"
3) (integer) 4069896088584598
4) 1) "116.23128265142440796"
2) "40.22076905438526495"
2) 1) "hefei"
2) "732.6371"
3) (integer) 4052763834193093
4) 1) "117.30793744325637817"
2) "31.79321915080526395"

③ georadiusbymember (查找指定元素指定范围内的元素)、geohash (返回经纬度的hash值)、zrange、zrem(使用zset命令操作geo)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#查询南京 500公里范围有哪些城市
127.0.0.1:6379> georadiusbymember city nanjing 500 km
1) "hefei"
2) "nanjing"
#查询重庆 1500公里范围有哪些城市
127.0.0.1:6379> georadiusbymember city chongqing 1500 km
1) "lasa"
2) "kunming"
3) "chongqing"
4) "hefei"
5) "nanjing"
6) "beijing"
#返回北京和南京的经纬度的 hash值
127.0.0.1:6379> geohash city beijing nanjing
1) "wx4sucvncn0"
2) "wtsd1qyxfx0"
#查看所有城市name
127.0.0.1:6379> ZRANGE city 0 -1
1) "lasa"
2) "kunming"
3) "chongqing"
4) "hefei"
5) "nanjing"
6) "beijing"
#根据geo中的name删除g元素
127.0.0.1:6379> ZREM city lasa
(integer) 1
#删除成功
127.0.0.1:6379> ZRANGE city 0 -1
1) "kunming"
2) "chongqing"
3) "hefei"
4) "nanjing"
5) "beijing"

④总结:实际需求中,我们可以用来查询附近的人、计算两人之间的距离等。当然,那些所需的经纬度我们肯定要结合java代码来一次导入,手动查询和录入太过于浪费时间!

详细了解

  1. geoadd
  • 格式:GEOADD key longitude latitude member [longitude latitude member …]
  • 功能:将一到多个空间元素添加到指定的空间集合中。
  • 说明:当用户尝试输入一个超出范围的经度或者纬度时,该命令会返回一个错误。
  1. geopos
  • 格式:GEOPOS key member [member …]
  • 功能:从指定的地理空间中返回指定元素的位置,即经纬度。
  • 说明:因为 该命令接受可变数量元素作为输入,所以即使用户只给定了一个元素,命令也会返回数组。
  1. geodist
  • 格式:GEODIST key member1 member2 [unit]
  • 功能:返回两个给定位置之间的距离。其中 unit 必须是以下单位中的一种:
    • m :米,默认
    • km :千米
    • mi :英里
    • ft:英尺
  • 说明:如果两个位置之间的其中一个不存在, 那么命令返回空值。另外,在计算距离时会假设地球为完美的球形, 在极限情况下, 这一假设最大会造成 0.5% 的误差。
  1. geohash
  • 格式:GEOHASH key member [member …]
  • 功能:返回一个或多个位置元素的 Geohash 值。
  • 说明:GeoHash 是一种地址编码方法。他能够把二维的空间经纬度数据编码成一个字符串。该值主要用于底层应用或者调试, 实际中的作用并不大。
  1. georadius
  • 格式:GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]
  • 功能:以给定的经纬度为中心,返回指定地理空间中包含的所有位置元素中,与中心距离不超过给定半径的元素。返回时还可携带额外的信息:
    • WITHDIST :在返回位置元素的同时,将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。
    • WITHCOORD :将位置元素的经维度也一并返回。
    • WITHHASH:将位置元素的 Geohash 也一并返回,不过这个 hash 以整数形式表示命令默认返回未排序的位置元素。 通过以下两个参数,用户可以指定被返回位置元素的排序方式:
    • ASC :根据中心的位置,按照从近到远的方式返回位置元素。
    • DESC :根据中心的位置,按照从远到近的方式返回位置元素。
  • 说明:在默认情况下, 该命令会返回所有匹配的位置元素。虽然用户可以使用 COUNT <count> 选项去获取前 N 个匹配元素,但因为命令在内部可能会需要对所有被匹配的元素进行处理,所以在对一个非常大的区域进行搜索时,即使使用 COUNT 选项去获取少量元素,该命令的执行速度也可能会非常慢。
  1. georadiusbymember
  • 格式:GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]
  • 功能:这个命令和 GEORADIUS 命令一样,都可以找出位于指定范围内的元素,但该命令的中心点是由位置元素形式给定的,而不是像 GEORADIUS 那样,使用输入的经纬度来指定中心点。
  • 说明:返回结果中也是包含中心点位置元素的

应用场景

​ Geospatial 的意义是地理位置,所以其主要应用地理位置相关的计算。例如,微信发现中的“附近”功能,添加朋友中“雷达加朋友”功能;QQ 动态中的“附近”功能;钉钉中的“签到”功能等。

Hyperloglog: 基数

简介

​ HyperLogLog 是 Redis 2.8.9 版本中引入的一种新的数据类型,其意义是 hyperlog log,超级日志记录。该数据类型可以简单理解为一个 set 集合,集合元素为字符串。但实际上HyperLogLog 是一种基数计数概率算法,通过该算法可以利用极小的内存完成独立总数的统计。其所有相关命令都是对这个“set 集合”的操作。

​ HyperLogLog 算法是由法国人 Philippe Flajolet 博士研究出来的,Redis的作者 Antirez 为了纪念 Philippe Flajolet 博士对组合数学和基数计算算法分析的研究,在设计 HyperLogLog 命令的时候使用了 Philippe Flajolet姓名的英文首字母 PF 作为前缀。遗憾的是 Philippe Flajolet 博士于 2011年 3 月 22 日因病在巴黎辞世。

快速上手

首先得明白什么是基数
再数学层面上可以说是:两个数据集中不重复的元素~
但是再Redis中,可能会有一定的误差性。 官方给出的误差率是0.81%
Hyperloglog的优点: 占用的内存是固定的,2^64个元素,相当于只需要12kb的内存即可。效率极高!

①pfadd(添加数据集)、pfcount(统计数据集)、pfmegre(合并数据集-自动去重)

1
2
3
4
5
6
7
8
9
10
11
12
13
127.0.0.1:6379> pfadd dataList 1 2 3 4 5 6 7  #添加数据集
(integer) 1
127.0.0.1:6379> pfcount dataList #统计数据集中的元素
(integer) 7
127.0.0.1:6379> pfadd dataList1 4 5 6 7 8 9 10 #添加数据集
(integer) 1
127.0.0.1:6379> pfcount dataList1 #统计数据集中的元素
(integer) 7
#将dataList 和dataList1 两个数据集合并成一个新的 newdata数据集,并且自动去重
127.0.0.1:6379> pfmerge newdata dataList dataList1
OK
127.0.0.1:6379> pfcount newdata
(integer) 10

②总结:如果在实际业务中,允许一定的误差值,我们可以使用基数统计来计算~效率非常高!比如:网站的访问量,就可以利用Hyperloglog来进行计算统计!

详细了解

  1. pfadd
  • 格式:PFADD key element [element …]
  • 功能:将任意数量的元素添加到指定的 HyperLogLog 集合里面。如果内部存储被修改了返回 1,否则返回 0。
  1. pfcount
  • 格式:PFCOUNT key [key …]
  • 功能:该命令作用于单个 key 时,返回给定 key 的 HyperLogLog 集合的近似基数;该命令作用于多个 key 时,返回所有给定 key 的 HyperLogLog 集合的并集的近似基数;如果key 不存在,则返回 0。
  1. pfmerge
  • 格式:PFMERGE destkey sourcekey [sourcekey …]
  • 功能:将多个 HyperLogLog 集合合并为一个 HyperLogLog 集合,并存储到 destkey 中,合并后的 HyperLogLog 的基数接近于所有 sourcekey 的 HyperLogLog 集合的并集。

应用场景

​ HyperLogLog 可对数据量超级庞大的日志数据做不精确的去重计数统计。当然,这个不精确的度在 Redis 官方给出的误差是 0.81%。这个误差对于大多数超大数据量场景是被允许的。对于平台上每个页面每天的 UV 数据,非常适合使用 HyperLogLog 进行记录。

Bitmap: 位存储

简介

​ BitMap 是 Redis 2.2.0 版本中引入的一种新的数据类型。该数据类型本质上就是一个仅包含 0 和 1 的二进制字符串。而其所有相关命令都是对这个字符串二进制位的操作。用于描述该字符串的属性有三个:key、offset、bitValue。

  • key:BitMap 是 Redis 的 key-value 中的一种 Value 的数据类型,所以该 Value 一定有其对应的 key。
  • offset:每个 BitMap 数据都是一个字符串,字符串中的每个字符都有其对应的索引,该索引从 0 开始计数。该索引就称为每个字符在该 BitMap 中的偏移量 offset。这个 offset的值的范围是[0,232-1],即该 offset 的最大值为 4G-1,即 4294967295,42 亿多。
  • bitValue:每个 BitMap 数据中都是一个仅包含 0 和 1 的二进制字符串,每个 offset 位上的字符就称为该位的值 bitValue。bitValue 的值非 0 即 1。

快速上手

Bitmap 位图,数据结构! 都是操作二进制位来进行记录,就只有0 和 1 两个状态!
①setbit(添加)、getset(获取)、bitcount(统计)操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
127.0.0.1:6379> setbit login 1 1   #添加周一已登陆 为1
(integer) 0
127.0.0.1:6379> setbit login 2 1
(integer) 0
127.0.0.1:6379> setbit login 3 1
(integer) 0
127.0.0.1:6379> setbit login 4 0 #添加周四已登陆 为0
(integer) 0
127.0.0.1:6379> setbit login 5 0
(integer) 0
127.0.0.1:6379> setbit login 6 1
(integer) 0
127.0.0.1:6379> setbit login 7 0
(integer) 0
127.0.0.1:6379> getbit login 1 #获取周一是否登录
(integer) 1
127.0.0.1:6379> getbit login 4 #获取周四是否登陆
(integer) 0
127.0.0.1:6379> bitcount login #统计这周登陆的天数
(integer) 4

②总结:实际需求中,可能需要我们统计用户的登陆信息,员工的打卡信息等等。只要是事务的只有两个状态的,我们都可以用Bitmap来进行操作!!!

详细了解

  1. setbit
  • 格式:SETBIT key offset value
  • 功能:为给定 key 的 BitMap 数据的 offset 位置设置值为 value。其返回值为修改前该 offset位置的 bitValue
  • 说明:对于原 BitMap 字符串中不存在的 offset 进行赋值,字符串会自动伸展以确保它可以将 value 保存在指定的 offset 上。当字符串值进行伸展时,空白位置以 0 填充。当然,设置的 value 只能是 0 或 1。不过需要注意的是,对使用较大 offset 的 SETBIT 操作来说,内存分配过程可能造成 Redis 服务器被阻塞。
  1. getbit
  • 格式:GETBIT key offset
  • 功能:对 key 所储存的 BitMap 字符串值,获取指定 offset 偏移量上的位值 bitValue。
  • 说明:当 offset 比字符串值的长度大,或者 key 不存在时,返回 0 。
  1. bitcount
  • 格式:BITCOUNT key [start] [end]
  • 功能:统计给定字符串中被设置为 1 的 bit 位的数量。一般情况下,统计的范围是给定的整个 BitMap 字符串。但也可以通过指定额外的 start 或 end 参数,实现仅对指定字节范围内字符串进行统计,包括 start 和 end 在内。注意,这里的 start 与 end 的单位是字节,不是 bit,并且从 0 开始计数。
  • 说明:start 和 end 参数都可以使用负数值: -1 表示最后一个字节, -2 表示倒数第二个字节,以此类推。另外,对于不存在的 key 被当成是空字符串来处理,因此对一个不存在的 key 进行 BITCOUNT 操作,结果为 0 。
  1. bitpos
  • 格式:BITPOS key bit [start] [end]
  • 功能:返回 key 指定的 BitMap 中第一个值为指定值 bit(非 0 即 1) 的二进制位的位置。pos,即 position,位置。在默认情况下, 命令将检测整个 BitMap,但用户也可以通过可选的 start 参数和 end 参数指定要检测的范围。
  • 说明:start 与 end 的意义与 bitcount 命令中的相同。
  1. bitop
  • 格式:BITOP operation destkey key [key …]
  • 功能:对一个或多个 BitMap 字符串 key 进行二进制位操作,并将结果保存到 destkey 上。
  • operation 可以是 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种:
    • BITOP AND destkey key [key …] :对一个或多个 BitMap 执行按位与操作,并将结果保存到 destkey 。
    • BITOP OR destkey key [key …] :对一个或多个 BitMap 执行按位或操作,并将结果保存到 destkey 。
    • BITOP XOR destkey key [key …] :对一个或多个 BitMap 执行按位异或操作,并将结果保存到 destkey 。
    • BITOP NOT destkey key :对给定 BitMap 执行按位非操作,并将结果保存到 destkey 。
  • 说明:
    • 除了 NOT 操作之外,其他操作都可以接受一个或多个 BitMap 作为输入。
    • 除了 NOT 操作外,其他对一个 BitMap 的操作其实就是一个复制。
    • 如果参与运算的多个 BitMap 长度不同,较短的 BitMap 会以 0 作为补充位与较长BitMap 运算,且运算结果长度与较长 BitMap 的相同。

应用场景

​ 由于 offset 的取值范围很大,所以其一般应用于大数据量的二值性统计。例如平台活跃用户统计(二值:访问或未访问)、支持率统计(二值:支持或不支持)、员工考勤统计(二值:上班或未上班)、图像二值化(二值:黑或白)等。
​ 不过,对于数据量较小的二值性统计并不适合 BitMap,可能使用 Set 更为合适。当然,具体多少数据量适合使用 Set,超过多少数据量适合使用 BitMap,这需要根据具体场景进行具体分析。
​ 例如,一个平台要统计日活跃用户数量。
​ 如果使用 Set 来统计,只需上线一个用户,就将其用户 ID 写入 Set 集合即可,最后只需统计出 Set 集合中的元素个数即可完成统计。即 Set 集合占用内存的大小与上线用户数量成正比。假设用户 ID 为 m 位 bit 位,当前活跃用户数量为 n,则该 Set 集合的大小最少应该是m*n 字节。
​ 如果使用 BitMap 来统计,则需要先定义出一个 BitMap,其占有的 bit 位至少为注册用户数量。只需上线一个用户,就立即使其中一个 bit 位置 1,最后只需统计出 BitMap 中 1 的个数即可完成统计。即 BitMap 占用内存的大小与注册用户数量成正比,与上线用户数量无关。假设平台具有注册用户数量为 N,则 BitMap 的长度至少为 N 个 bit 位,即 N/8 字节。
​ 何时使用 BitMap 更合适?令 m*n 字节 = N/8 字节,即 n = N/8/m = N/(8*m) 时,使用Set 集合与使用 BitMap 所占内存大小相同。以淘宝为例,其用户 ID 长度为 11 位(m),其注册用户数量为 8 亿(N),当活跃用户数量为 8 亿/(8*11) = 0.09 亿 = 9*10^6 = 900 万,使用 Set与 BitMap 占用的内存是相等的。但淘宝的日均活跃用户数量为 8 千万,所以淘宝使用 BitMap更合适。