(三)redis的其他功能-Bitmap,HyperLogLog,GEO
redis除了5种数据类型之外,还提供了其他功能,如:慢查询,pipeline,事务,发布订阅和消息队列,Bitmap,HyperLogLog,GEO
1.Bitmap(位图)
BitMap,即位图,其实也就是 byte 数组,用二进制表示,只有 0 和 1 两个数字。
就是通过一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身。我们知道8个bit可以组成一个Byte,所以bitmap本身会极大的节省储存空间。
API:
命令 | 含义 |
---|---|
getbit key offset | 对key所存储的字符串值,获取指定偏移量上的位(bit) |
setbit key offset value | 对key所存储的字符串值,设置或清除指定偏移量上的位(bit)1. 返回值为该位在setbit之前的值。2. value只能取0或1。3. offset从0开始,即使原位图只能10位,offset可以取1000 |
bitcount key [start end] | 获取位图指定范围中位值为1的个数 如果不指定start与end,则取所有 |
bitop op destKey key1 [key2...] | 做多个BitMap的and(交集)、or(并集)、not(非)、xor(异或)操作并将结果保存在destKey中 |
bitpos key tartgetBit [start end] | 计算位图指定范围第一个偏移量对应的的值等于targetBit的位置。1. 找不到返回-1。2. start与end没有设置,则取全部。3. targetBit只能取0或者1 |
使用场景:
用户签到:
根据日期 offset =hash % 365 ; key = 年份#用户id统计活跃用户:
使用时间作为cacheKey,然后用户ID为offset,如果当日活跃过就设置为1。
那么我该如果计算某几天/月/年的活跃用户呢(暂且约定,统计时间内只有有一天在线就称为活跃),有请下一个redis的命令
命令 BITOP operation destkey key [key ...]
说明:对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。
说明:BITOP 命令支持 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种参数
20190216 活跃用户 【1,2】
20190217 活跃用户 【1】
统计20190216~20190217 总活跃用户数: 1
统计20190216~20190217 在线活跃用户数: 2用户在线状态:
使用bitmap是一个节约空间效率又高的一种方法,只需要一个key,然后用户ID为offset,如果在线就设置为1,不在线就设置为0,和上面的场景一样,5000W用户只需要6MB的空间
使用经验:
- type = string,BitMap 是 sting 类型,最大 512 MB。
- 注意 setbit 时的偏移量,可能有较大耗时。
- 位图不是绝对好。
优势:
- 基于最小的单位bit进行存储,所以非常省空间。
- 设置时候时间复杂度O(1)、读取时候时间复杂度O(n),操作是非常快的。
- 二进制数据的存储,进行相关计算的时候非常快。
- 方便扩容。
限制:
redis中bit映射被限制在512MB之内,所以最大是2^32位。建议每个key的位数都控制下,因为读取时候时间复杂度O(n),越大的串读的时间花销越多。
bitmap空间、时间粗略计算方式:
在一台2010MacBook Pro上,offset为2^32-1(分配512MB)需要~300ms,
offset为230-1(分配128MB)需要~80ms,offset为228-1(分配32MB)需要~30ms,
offset为2^26-1(分配8MB)需要8ms。<来自官方文档>
大概的空间占用计算公式是:($offset/8/1024/1024)MB
2.HyperLogLog
- 基于HyperLogLog算法:极小空间完成独立数量统计。
- 本质还是字符串。
举个栗子:假如我要统计网页的UV(浏览用户数量,一天内同一个用户多次访问只能算一次),传统的解决方案是使用Set来保存用户id,然后统计Set中的元素数量来获取页面UV。但这种方案只能承载少量用户,一旦用户数量大起来就需要消耗大量的空间来存储用户id。我的目的是统计用户数量而不是保存用户,这简直是个吃力不讨好的方案!而使用Redis的HyperLogLog最多需要12k就可以统计大量的用户数,尽管它大概有0.81%的错误率,但对于统计UV这种不需要很精确的数据是可以忽略不计的。
API:
序号 | 命令及描述 |
---|---|
1 | PFADD key element [element ...] 添加指定元素到 HyperLogLog 中。 |
2 | PFCOUNT key [key ...] 返回给定 HyperLogLog 的基数估算值。 |
3 | PFMERGE destkey sourcekey [sourcekey ...] 将多个 HyperLogLog 合并为一个 HyperLogLog。 |
使用场景:
- 基数不大,数据量不大就用不上,会有点大材小用浪费空间。
- 有局限性,就是只能统计基数数量,而没办法去知道具体的内容是什么。
- 和bitmap相比,属于两种特定统计情况,简单来说,HyperLogLog 去重比 bitmap 方便很多。
- 一般可以bitmap和hyperloglog配合使用,bitmap标识哪些用户活跃,hyperloglog计数。
一般使用:
- 统计注册 IP 数。
- 统计每日访问 IP 数。
- 统计页面实时 UV 数。
- 统计在线用户数。
- 统计用户每天搜索不同词条的个数。
3.GEO(地理信息定位)
GEO功能在Redis3.2版本提供,支持存储地理位置信息用来实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能.geo的数据类型为zset。
存储经纬度,计算两地距离,范围计算等。
3.1 geoadd
语法:geoadd key longitude latitude member [longitude latitude member...]
解析:
将给定的空间元素(纬度、经度、名字)添加到指定的键里面。这些数据会以有序集he的形式被储存在键里面,从而使得georadius和georadiusbymember这样的命令可以在之后通过位置查询取得这些元素。
geoadd命令以标准的x,y格式接受参数,所以用户必须先输入经度,然后再输入纬度。
geoadd能够记录的坐标是有限的:非常接近两极的区域无法被索引的精确的坐标限制由EPSG:900913 / EPSG:3785 / OSGEO:41001 等坐标系统定义, 具体如下
有效的经度介于-180-180度之间
有效的纬度介于-85.05112878 度至 85.05112878 度之间。
当用户尝试输入一个超出范围的经度或者纬度时,geoadd命令将返回一个错误。
返回值:
新添加到键里面的空间元素数量,不包括那些已经存在但是被更新的元素。
geoadd cities:locations 116.28 39.55 beijing geopos cities:locations beijing
3.2 geopos
语法:
geopos key member [member...]
解析:
从键里面返回所有给定位置元素的位置(经度和纬度)
因为geopos命令接受可变数量的位置元素作为输入,所以即使用户只给定了一个位置元素,命令也会返回数组的回复。
返回值:
geopos命令返回一个数组,数组中的每个项都由两个元素组成:第一个元素为给定位置元素的经度,而第二个元素则为给定位置元素的纬度。当给定的位置元素不存在时,对应的数组项为空值。
geoadd cities:locations 117.12 39.08 tianjin 114.29 38.02 ?shijiazhuang 118.01 39.38 tangshan 115.29 38.51 baoding geopos cities:locations tianjin shijiazhuang
3.3 geodist
语法:geodist key member1 member2 [unit]
解析:
如果两个位置之间的其中一个不存在,那么命令返回空值。
指定单位的参数unit必须是以下单位的其中一个:
m表示单位为米
km表示单位为千米
mi表示单位为英里
ft表示单位为英尺
如果用户没有显式地指定单位参数,那么geodist默认使用米作为单位。
geodist命令在计算距离时会假设地球为完美的球形,在极限情况下,这一假设最大会造成0.5%的误差。
返回值:
计算出的距离会以双精度浮点数的形式被返回。如果给定的位置元素不存在,那么命令返回空值。
geoadd cities:locations 117.12 39.08 tianjin 114.29 38.02 ?shijiazhuang 118.01 39.38 geodist cities:locations tianjin shijiazhuang geodist cities:locations tianjin shijiazhuang km #如果不存在返回nil geodist cities:locations beijing zhengzhou
3.4 georadius
语法:georadius key longitude latitude radius m|km|ft|mi [withcoord][withdist][withhash][asc|desc][count count]
解析:
以给定的经纬度为中心,返回键包含的位置元素当中,与中心的距离不超过给定最大距离的所有位置元素。
范围可以使用以下其中一个单位:
m 表示单位为米。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
在给定以下选项时,命令会返回额外的信息:
withdist:在返回位置元素的同时,将位置元素与中心之间的距离也一并返回.距离的单位和用户给定的范围单位保持一致。
withcoord:将位置元素的经度和纬度也一并返回。
withhash:以52位有符号整数的形式,返回位置元素经过原始geohash编码的有序集合分值。这个选项主要用于底层应用或者调试,实际中的作用不大。
命令默认返回未排序的位置元素。通过以下两个参数,用户可以指定被返回位置元素的排序方式:
asc:根据中心的位置,按照从近到远的方式返回位置元素
desc:根据中心的位置,按照从远到近的方式返回位置元素。
在默认情况下,georadius命令会返回所有匹配的位置元素.虽然用户可以使用count选项去获取N个匹配元素,但是因为命令在内部
可能会需要对所有被匹配的元素进行处理,所以在对一个非常大的区域进行搜索时,即使只使用count选项去获取少量元素,
命令的执行速度也可能非常慢。但从另一方面说,使用count选项去减少需要返回的元素数量,对于减少带宽来说仍然是非常有用的。
返回值:
georadius命令返回一个数组,具体来说:
在没有给定任何with选项的情况下,命令只会返回一个像["Beijing","Tianjin"]这样的线性列表
在指定了withcoord、withdist、withhash等选项的情况下,命令返回一个二层嵌套数组,内层的每个子数组就表示一个元素。
在返回嵌套数组时,子数组的第一个元素总是位置元素的名字.至于额外的信息,则会作为子数组的后续元素,按照以下顺序被返回:
(1).以浮点数格式返回的中心位置元素之间的距离,单位与用户指定范围时的单位一致。
(2).geohash整数
(3).由两个元素组成的坐标,分别为经度和纬度。
geoadd cities:locations 117.12 39.08 tianjin 114.29 38.02 ?shijiazhuang 118.01 39.38 tangshan 115.29 38.51 baoding #withdist 返回位置名称和中心距离 georadius cities:locations 117 39 200 km withdist #withcoord 返回位置名称和经纬度 georadius cities:locations 117 39 200 km withcoord #withdist withcoord 返回位置名称 距离 和经纬度 georadius cities:locations 117 39 200 km withdist withcoord
3.5 georadiusbymember
语法:georadiusbymember key member radius m|km|ft|mi [withcoord][withdist][withhash][asc|desc][count count]
解析:
这个命令和georadius命令一样,都可以找出位于指定范围内的元素,但是georadiusbymember的中心点是由给定的位置元素决定的,而不是像georadius那样,使用输入的经度和纬度来决定中心点。
返回值:
一个数组,数组中的每个项表示一个范围之内的位置元素。
geoadd cities:locations 117.12 39.08 tianjin 114.29 38.02 ?shijiazhuang 118.01 39.38 tangshan 115.29 38.51 baoding georadiusbymember cities:locations tianjin 100 km
3.6 geohash
语法:geohash key member [member...]
解析:
Redis使用geohash将二维经纬度转换为一维字符串,字符串越长表示位置更精确,两个字符串越相似表示距离越近。
返回值:
一个数组,数组的每个项都是一个geohash。命令返回的geohash的位置与用户给定的位置元素的位置一一对应。
geoadd cities:locations 118.01 39.38 tangshan 115.29 38.51 baoding geohash cities:locations tangshan baoding
3.7 zrem
GEO没有提供删除成员的命令,但是因为GEO的底层实现是zset,所以可以借用zrem命令实现对地理位置信息的删除。
zrem cities:locations tianjin
参考:https://www.cnblogs.com/wuhaidong/articles/10389484.html
参考:https://www.jianshu.com/p/f008ae58336a
参考:https://blog.csdn.net/qq_34206560/java/article/details/91049218