Redis(二)基础:三大特殊数据类型的学习和理解
在上一篇文章中我们讲了五大基本类型,他们是最常用的,而且百分之九十的程序员对于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 | 127.0.0.1:6379> geoadd city 118.8921 31.32751 nanjing 197.30794 31.79322 |
②georadius(查询附近位置)操作
1 | 127.0.0.1:6379> ZRANGE city 0 -1 #查看城市 |
③ georadiusbymember (查找指定元素指定范围内的元素)、geohash (返回经纬度的hash值)、zrange、zrem(使用zset命令操作geo)
1 | #查询南京 500公里范围有哪些城市 |
④总结:实际需求中,我们可以用来查询附近的人、计算两人之间的距离等。当然,那些所需的经纬度我们肯定要结合java代码来一次导入,手动查询和录入太过于浪费时间!
详细了解
- geoadd
- 格式:
GEOADD key longitude latitude member [longitude latitude member …]
- 功能:将一到多个空间元素添加到指定的空间集合中。
- 说明:当用户尝试输入一个超出范围的经度或者纬度时,该命令会返回一个错误。
- geopos
- 格式:
GEOPOS key member [member …]
- 功能:从指定的地理空间中返回指定元素的位置,即经纬度。
- 说明:因为 该命令接受可变数量元素作为输入,所以即使用户只给定了一个元素,命令也会返回数组。
- geodist
- 格式:
GEODIST key member1 member2 [unit]
- 功能:返回两个给定位置之间的距离。其中 unit 必须是以下单位中的一种:
- m :米,默认
- km :千米
- mi :英里
- ft:英尺
- 说明:如果两个位置之间的其中一个不存在, 那么命令返回空值。另外,在计算距离时会假设地球为完美的球形, 在极限情况下, 这一假设最大会造成 0.5% 的误差。
- geohash
- 格式:
GEOHASH key member [member …]
- 功能:返回一个或多个位置元素的 Geohash 值。
- 说明:GeoHash 是一种地址编码方法。他能够把二维的空间经纬度数据编码成一个字符串。该值主要用于底层应用或者调试, 实际中的作用并不大。
- 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 选项去获取少量元素,该命令的执行速度也可能会非常慢。
- 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 | 127.0.0.1:6379> pfadd dataList 1 2 3 4 5 6 7 #添加数据集 |
②总结:如果在实际业务中,允许一定的误差值,我们可以使用基数统计来计算~效率非常高!比如:网站的访问量,就可以利用Hyperloglog来进行计算统计!
详细了解
- pfadd
- 格式:
PFADD key element [element …]
- 功能:将任意数量的元素添加到指定的 HyperLogLog 集合里面。如果内部存储被修改了返回 1,否则返回 0。
- pfcount
- 格式:
PFCOUNT key [key …]
- 功能:该命令作用于单个 key 时,返回给定 key 的 HyperLogLog 集合的近似基数;该命令作用于多个 key 时,返回所有给定 key 的 HyperLogLog 集合的并集的近似基数;如果key 不存在,则返回 0。
- 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 | 127.0.0.1:6379> setbit login 1 1 #添加周一已登陆 为1 |
②总结:实际需求中,可能需要我们统计用户的登陆信息,员工的打卡信息等等。只要是事务的只有两个状态的,我们都可以用Bitmap来进行操作!!!
详细了解
- setbit
- 格式:
SETBIT key offset value
- 功能:为给定 key 的 BitMap 数据的 offset 位置设置值为 value。其返回值为修改前该 offset位置的 bitValue
- 说明:对于原 BitMap 字符串中不存在的 offset 进行赋值,字符串会自动伸展以确保它可以将 value 保存在指定的 offset 上。当字符串值进行伸展时,空白位置以 0 填充。当然,设置的 value 只能是 0 或 1。不过需要注意的是,对使用较大 offset 的 SETBIT 操作来说,内存分配过程可能造成 Redis 服务器被阻塞。
- getbit
- 格式:
GETBIT key offset
- 功能:对 key 所储存的 BitMap 字符串值,获取指定 offset 偏移量上的位值 bitValue。
- 说明:当 offset 比字符串值的长度大,或者 key 不存在时,返回 0 。
- bitcount
- 格式:
BITCOUNT key [start] [end]
- 功能:统计给定字符串中被设置为 1 的 bit 位的数量。一般情况下,统计的范围是给定的整个 BitMap 字符串。但也可以通过指定额外的 start 或 end 参数,实现仅对指定字节范围内字符串进行统计,包括 start 和 end 在内。注意,这里的 start 与 end 的单位是字节,不是 bit,并且从 0 开始计数。
- 说明:start 和 end 参数都可以使用负数值: -1 表示最后一个字节, -2 表示倒数第二个字节,以此类推。另外,对于不存在的 key 被当成是空字符串来处理,因此对一个不存在的 key 进行 BITCOUNT 操作,结果为 0 。
- bitpos
- 格式:
BITPOS key bit [start] [end]
- 功能:返回 key 指定的 BitMap 中第一个值为指定值 bit(非 0 即 1) 的二进制位的位置。pos,即 position,位置。在默认情况下, 命令将检测整个 BitMap,但用户也可以通过可选的 start 参数和 end 参数指定要检测的范围。
- 说明:start 与 end 的意义与 bitcount 命令中的相同。
- 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更合适。