本文主要是介绍【Redis】4、详解三种特殊数据类型 Geospatial、Hyperloglog、Bitmap,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
这三种特殊数据类型其实不是一个新的类型;底层还是五大类型中的一种;如:Zset、String
1、Geospatial 地理位置
geo:地理位置 spatial: [ˈspeɪʃl] 空间的
朋友的定位,附近的人,打车距离计算?
Redis 的 Geo 在Redis3.2 版本就推出了! 这个功能可以推算地理位置的信息,两地之间的距离,方圆
几里的人!可以查询一些测试数据:http://www.jsons.cn/lngcodeinfo/0706D99C19A781A3/
官方文档:Redis命令中心 GEO
1)添加地理位置
geoadd key [NX|XX] [CH] longitude latitude member [longitude latitude member ...]
添加一个或多个位置的经度、纬度;先经度,后纬度
规则:
- 有效的经度从
-180
度到180
度- 有效的纬度从
-85.05112878
度到85.05112878
度当坐标位置超出上述指定范围时,该命令将会返回一个错误。
两级无法直接添加,一般会下载城市数据,直接通过java程序一次性导入!
# 添加北京经纬度
127.0.0.1:6379> geoadd china:city 116.405285 39.904989 beijing
(integer) 1# 查看key
127.0.0.1:6379> keys *
1) "china:city"# key类型:zset
127.0.0.1:6379> type china:city
zset# 添加上海、广州、重庆
127.0.0.1:6379> geoadd china:city 121.47264 31.231706 shanghai 113.28063 23.125178 guangzhou 106.504962 29.533155 chongqing
(integer) 3# 查询全部
127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "guangzhou"
3) "shanghai"
4) "beijing"
2)获取经纬度
geopos key member [member ...]
获取一个或多个位置的经纬度
127.0.0.1:6379> geopos china:city chongqing guangzhou
1) 1) "106.50495976209640503"2) "29.53315530684997015"
2) 1) "113.28062742948532104"2) "23.12517743834835215"
3)获取两地间的距离
geodist key member1 member2 [m|km|ft|mi]
获取两个位置成员间的距离
单位:
m
表示单位为米(默认)km
表示单位为千米mi
表示单位为英里ft
表示单位为英尺
# 北京、上海间的距离
127.0.0.1:6379> geodist china:city beijing shanghai km
"1067.5978"
# 广州、重庆间的距义
127.0.0.1:6379> geodist china:city guangzhou chongqing
"981476.5000"
4)找出特定坐标范围内的其它元素
georadius key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]
获取特定位置为中心,radius为半径的范围的其它位置longitude latitude
中心坐标radius m|km|ft|mi
半径及单位;中间有空格withcoord
将位置元素的经度和纬度也一并返回withdist
与中心之间的距离也一并返回withhash
位置哈希值一并返回count
返回元素的数量
我附近的人? (获得所有附近的人的地址,定位!)通过半径来查询!
获得指定数量的人,200
所有数据应该都录入:china:city ,才会让结果更加请求!
# 找出距离位置(100,30)方圆500km的其它城市
127.0.0.1:6379> georadius china:city 100 30 500 km
(empty array)# 找出距离位置(100,30)方圆1000km的其它城市,包含经纬度、距离、哈希值
127.0.0.1:6379> georadius china:city 100 30 1000 km withcoord withdist withhash
1) 1) "chongqing"2) "630.1140"3) (integer) 40260421178873714) 1) "106.50495976209640503"2) "29.53315530684997015"
5)找出特定成员范围内的其它元素
georadiusbymember key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]
找出距离member成员,方圆radius范围内的其它成员
127.0.0.1:6379> georadiusbymember china:city beijing 100 km
1) "beijing"
127.0.0.1:6379> georadiusbymember china:city beijing 1000 km
1) "beijing"
127.0.0.1:6379> georadiusbymember china:city beijing 2000 km
1) "chongqing"
2) "guangzhou"
3) "shanghai"
4) "beijing"127.0.0.1:6379> georadiusbymember china:city beijing 2000 km withcoord withdist withhash
1) 1) "chongqing"2) "1464.2210"3) (integer) 40260421178873714) 1) "106.50495976209640503"2) "29.53315530684997015"
2) 1) "guangzhou"2) "1889.3707"3) (integer) 40465337640664754) 1) "113.28062742948532104"2) "23.12517743834835215"
3) 1) "shanghai"2) "1067.5978"3) (integer) 40548034648170624) 1) "121.47264093160629272"2) "31.23170490709807012"
4) 1) "beijing"2) "0.0000"3) (integer) 40698853706710104) 1) "116.40528291463851929"2) "39.9049884229125027"
6)返回位置的哈希字符串
geohash key member [member ...]
返回一个或多个位置元素的哈希字符串(11个字符)
# 将二维的经纬度转换为一维的字符串,如果两个字符串越接近,那么则距离越近!
127.0.0.1:6379> geohash china:city beijing chongqing
1) "wx4g0b7xrt0"
2) "wm78p86e170"
7)底层原理
GEO 底层的实现原理其实就是
Zset
!可以使用Zset命令来操作geo!
127.0.0.1:6379> keys *
1) "china:city"
# key类型zset
127.0.0.1:6379> type china:city
zset# 查看所有
127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "guangzhou"
3) "shanghai"
4) "beijing"# 删除
127.0.0.1:6379> zrem china:city chongqing
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "guangzhou"
2) "shanghai"
3) "beijing"
2、Hyperloglog
1)介绍
走近源码:神奇的HyperLogLog - 知乎 (zhihu.com)
2)应用场景
- 统计注册 IP 数
- 统计每日访问 IP 数
- 统计页面实时 UV 数
- 统计在线用户数
- 统计用户每天搜索不同词条的个数
网页的访问量 UV (一个人访问一个网站多次,但是还是算作一个人!)
传统的方式,set 保存用户的id,然后就可以统计 set 中的元素数量作为标准判断!这个方式如果保存大量的用户id,就会比较麻烦!我们的目的是为了计数,而不是保存用户id;
使用HyperLogLog非常合适;
HyperLogLog有0.81% 错误率! 统计UV任务,可以忽略不计的!
如果允许容错,那么一定可以使用 Hyperloglog !
如果不允许容错,就使用 set 或者自己的数据类型即可!
3)添加 pfadd
pfadd key element [element ...]
向key中添加一个或多个元素
# 向mykey1中添加v1、v2、v3
127.0.0.1:6379> pfadd mykey1 v1 v2 v3
(integer) 1# key:mykey1
127.0.0.1:6379> keys *
1) "mykey1"# key的类型 string
127.0.0.1:6379> type mykey1
string# 元素数量 (数据量大时有误差)
127.0.0.1:6379> pfcount mykey1
(integer) 3# 字符串的值(无法查看mykey1中的具体元素)
127.0.0.1:6379> getrange mykey1 0 -1
"HYLL\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00G\x1f\x80o4\x88@P\x8cIV"
4)数量 pfcount
pfcount key [key ...]
获取一个或多个key中不重复元素的总数量
# 向mykey2中添加v10 v11 v12
127.0.0.1:6379> pfadd mykey2 v10 v11 v12
(integer) 1# mykey1中元素数量
127.0.0.1:6379> pfcount mykey1
(integer) 3 # 分别为v1 v2 v3
# 再向mykey1中添加v11
127.0.0.1:6379> pfadd mykey1 v11
(integer) 1
# mykey1中元素数量
127.0.0.1:6379> pfcount mykey1
(integer) 4 # 分别为v1 v2 v3 v11# mykey1和mykey2中元素的总数量(不重复元素)
127.0.0.1:6379> pfcount mykey1 mykey2
(integer) 6
5)合并 pfmerge
pfmerge destkey sourcekey [sourcekey ...]
把一个或多个sourcekey合并到一个目标destkey中
127.0.0.1:6379> keys *
1) "mykey2"
2) "mykey1"# 把mykey1、mykey2合并到mykey3
127.0.0.1:6379> pfmerge mykey3 mykey1 mykey2
OK127.0.0.1:6379> keys *
1) "mykey3"
2) "mykey2"
3) "mykey1"# mykey3中不重复元素的个数;效果等同于pfcount mykey1 mykey2
127.0.0.1:6379> pfcount mykey3
(integer) 6
3、Bitmap
原文链接:Redis 中 BitMap 的使用场景
1)概念
BitMap 原本的含义是用一个比特位来映射某个元素的状态。由于一个比特位只能表示 0
和 1
两种状态,所以 BitMap 能映射的状态有限,但是使用比特位的优势是能大量的节省内存空间。
在 Redis 中,可以把 Bitmap 想象成一个以比特位为单位的数组,数组的每个单元只能存储0和1,数组的下标在 Bitmap 中叫做偏移量。
需要注意的是:BitMap 在 Redis 中并不是一个新的数据类型,其底层是 Redis 的 String
类型。
2)相关命令
(1)设置值 setbit
# 设置值,其中value只能是 0 和 1
setbit key offset value
(2)获取值 getbit
# 获取值
getbit key offset
(3)获取1的个数 bitcount
# 获取指定范围内值为 1 的个数
# start 和 end 以字节为单位
bitcount key start end
(4)BitMap间运算 bitop
# BitMap间的运算
# operations 位移操作符,枚举值AND 与运算 &OR 或运算 |XOR 异或 ^NOT 取反 ~
# result 计算的结果,会存储在该key中
# key1 … keyn 参与运算的key,可以有多个,空格分割,not运算只能一个key
# 当 BITOP 处理不同长度的字符串时,较短的那个字符串所缺少的部分会被看作 0。返回值是保存到 destkey 的字符串的长度(以字节byte为单位),和输入 key 中最长的字符串长度相等。
bitop [operations] [result] [key1] [keyn…]
(5)值第一次出现的位置 bitpos
# 返回指定key中第一次出现指定value(0/1)的位置
bitpos [key] [value]
3)占用空间
在弄清 BitMap 到底占用多大的空间之前,我们再来重申下:Redis 其实只支持 5 种数据类型,并没有 BitMap 这种类型,BitMap 底层是基于 Redis 的字符串
类型实现的。
我们通过下面的命令来看下 BitMap 占用的空间大小:
# 首先将偏移量是0的位置设为1;其中 csx:key:1 表示一个key
127.0.0.1:6379> setbit csx:key:1 0 1
(integer) 0
# 通过STRLEN命令,我们可以看到字符串的长度是1
127.0.0.1:6379> STRLEN csx:key:1
(integer) 1
# 将偏移量是1的位置设置为1
127.0.0.1:6379> setbit csx:key:1 1 1
(integer) 0
# 此时字符串的长度还是为1,因为一个字符串有8个比特位,不需要再开辟新的内存空间
127.0.0.1:6379> STRLEN csx:key:1
(integer) 1
# 将偏移量是8的位置设置成1
127.0.0.1:6379> setbit csx:key:1 8 1
(integer) 0
# 此时字符串的长度编程2,因为一个字节存不下9个比特位,需要再开辟一个字节的空间
127.0.0.1:6379> STRLEN csx:key:1
(integer) 2
通过上面的实验我们可以看出,BitMap 占用的空间,就是底层字符串占用的空间。假如 BitMap 偏移量的最大值是 OFFSET_MAX,那么它底层占用的空间就是:
(OFFSET_MAX/8)+1 = 占用字节数
因为字符串内存只能以字节分配,所以上面的单位是字节。
但是需要注意,Redis 中字符串的最大长度是 512M
,所以 BitMap 的 offset 值也是有上限的,其最大值是:
8 * 1024 * 1024 * 512 = 2^32
由于 C语言中字符串的末尾都要存储一位分隔符,所以实际上 BitMap 的 offset 值上限是:
(8 * 1024 * 1024 * 512) -1 = 2^32 - 1
4)使用场景
1. 用户签到
很多网站都提供了签到功能,并且需要展示最近一个月的签到情况,这种情况可以使用 BitMap 来实现。
根据日期 offset = (今天是一年中的第几天) % (今年的天数),key = 年份:用户id
。
如果需要将用户的详细签到信息入库的话,可以考虑使用一个一步线程来完成。
2. 统计活跃用户(用户登陆情况)
使用日期作为 key,然后用户 id 为 offset,如果当日活跃过就设置为1。具体怎么样才算活跃这个标准大家可以自己指定。
假如 20201009 活跃用户情况是: [1,0,1,1,0]
20201010 活跃用户情况是 :[ 1,1,0,1,0 ]
统计连续两天活跃的用户总数:
# 合并两天的key
bitop and dest1 20201009 20201010
# dest1 中值为1的offset,就是连续两天活跃用户的ID
bitcount dest1
统计20201009 ~ 20201010 活跃过的用户:
# 或运算(并集)
bitop or dest2 20201009 20201010
3. 统计用户是否在线
如果需要提供一个查询当前用户是否在线的接口,也可以考虑使用 BitMap 。即节约空间效率又高,只需要一个 key,然后用户 id 为 offset,如果在线就设置为 1,不在线就设置为 0。
4. 实现布隆过滤器
这篇关于【Redis】4、详解三种特殊数据类型 Geospatial、Hyperloglog、Bitmap的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!