提及redis
的数据结构,你们可能对五大基础数据类型比较熟悉:String
,Hash
,List
,Set
,Sorted Set
。那么除此以外,还有三大衍生数据结构,你们平时是不多接触的,即:bitmaps
、hyperloglog
、geo
另外,我以为,这三个数据结构,只能说是锦上添花。真正在项目中,我还真没用过。 下面你们来看看这三大数据结构的定义和用途java
说到这个bitmaps
,其实它就是String
,但它能够对String
的位进行操做。而后呢,这个位操做,有本身的命令,因此和操做String
的redis
命令又不大同样! 能够这么理解redis
bitmaps为一个以位为单位的数组,数组的每一个单元只能存储0和1算法
下面举个例子,好比咱们要作一个set操做,key
为w
,value
为h
,那么执行以下命令数组
127.0.0.1:6379> set w h
OK
127.0.0.1:6379> get w
"h"
复制代码
那么h
的ASCII为0110 1000
接下来,你能够用位命令getbit
命令取出,取出每一位的内容。bash
127.0.0.1:6379> getbit w 0 #用getbit获取w第0位的值
(integer) 0
127.0.0.1:6379> getbit w 1 #用getbit获取w第1位的值
(integer) 1
127.0.0.1:6379> getbit w 2 #用getbit获取w第2位的值
(integer) 1
127.0.0.1:6379> getbit w 3 #用getbit获取w第3位的值
(integer) 0
复制代码
网上传言,此结构用来统计必定时间内的,活跃的用户数,使用bitmap
的结构比传统的set
结构省空间。然而,这种用途有很大的局限性,我后文会说到。先说一下,网上的说法。 假设有30个用户,其中有5个用户,在2018-10-04
这天登录了。假设这5个用户的userid=2,4,8,11,12。 那么,咱们假设key为users:2018-10-04
,将其value
值用于记录用户登录信息。那么为了记录上述5个用户登录过,咱们将该value
值的第2位,第4位,第8位,第11位,第12位设为1,即执行下述命令数据结构
127.0.0.1:6379> setbit users:2018-10-04 2 1
(integer) 0
127.0.0.1:6379> setbit users:2018-10-04 4 1
(integer) 0
127.0.0.1:6379> setbit users:2018-10-04 8 1
(integer) 0
127.0.0.1:6379> setbit users:2018-10-04 11 1
(integer) 0
127.0.0.1:6379> setbit users:2018-10-04 12 1
(integer) 0
复制代码
这个时候,好比你要判断userid=11的用户,在2018-10-04这天,有没有登录过,就执行下述命令函数
127.0.0.1:6379> getbit users:2018-10-04 11
(integer) 1
复制代码
结果为1,就表明用户登录过。若是返回结果为0,则表明用户没登录过。 若是要统计,2018-10-04,这一天登录的用户数,能够执行下面的命令大数据
127.0.0.1:6379> bitcount users:2018-10-04
(integer) 5
复制代码
上面的命令就能够统计出,2018-10-04,这一天5个用户登录过。 ok,到这里你们就查很少能明白了。 先说一下,这里的userid=2,4,8,11,12
,能够理解为偏移量。好比实际项目中的userid位1000002,那么偏移量就是2。你们在项目中,能够灵活变通。 然而这种方式有一个局限性。咱们在实际项目中,若是userid是使用uuid生成的,那么,你要如何根据这些userid生成偏移量?莫非你还要去找一个hash函数,生成偏移量?就算找到了相应的hash函数,你能确保必定不发生hash碰撞,致使偏移量一致? 因此,你们了解便可。ui
HyperLogLog
并非一种数据结构,而是一种算法,能够利用极小的内存空间完成独立总数的统计。 其实,你们可能对该算法比较陌生。咱们java
中有一个库叫stream-lib
,其中也实现了HyperLogLog
算法。我大概说一下该算法的原理,我不想去长篇大论的搬出数学论文来,你们看着也无聊,这里Hyper
指的是超级的意思,它的前世是LogLog算法。这里我走马观花的装13一下,你们能领悟到精髓便可。 假设有以下对话spa
我:"小曲啊,假设啊,我一轮丢5次硬币,丢了不少轮以后,发现这几轮中,最多出现连续的2次反面1次正面,你能猜出来我丢了多少轮么!" 小曲:"应该没几轮吧,顶多就七八轮。" 我:"卧槽,这么机智,怎么算的?" 小曲:"很简单啊,正反面几率都是1/2,连着二次反面,一次正面。不就是1/21/21/2么!" 我:"那要是最多出现连续的4次反面1次正面呢?" 小曲:"那应该是不少不少轮吧!" 我:"果真机智!"
上述聊天,出自我和同事曲之间的,平常互吹!若有雷同,纯属巧合!
好了,原理讲完了!只是他的估算算法比较复杂!没这么简单而已!并且这么估,偏差还比较大!下面给出算法的伪代码。
输入:一个集合
输出:集合的独立总数
算法:
max = 0
对于集合中的每一个元素:
hashCode = hash(元素)
num = hashCode二进制表示中最前面连续的0的数量
if num > max:
max = num
最后的结果是2的(max + 1)次幂
复制代码
须要说明的是
hashCode = hash(元素)
复制代码
就是把你的输入元素,映射成二进制长串。映射成二进制长串后,就能够类比到我最早说的抛硬币的结果了。至于最后的结果为何用(max+1)
,你们能够去查文献。毕竟这文章是在讲redis
,不是在讲这个算法。并且这个算法,后面还通过了一系列演进,好比将入参集合分为m个部分,而后将m
个部分的结果求一个平均数(avg)
,最后以2的(avg + 1)
次幂,来估计独立总数!这些读者有兴趣能够自行查询!
这个结构能够很是省内存的去统计各类计数,好比注册IP
数、每日访问IP
数。固然,存在偏差!Redis官方给出的数字是0.81%的失误率。 用法也很简单以下所示
127.0.0.1:6379> pfadd ips:2018-10-04 "127.0.0.1" "127.0.0.2" "127.0.0.3" "127.0.0.4"
(integer) 1
127.0.0.1:6379> pfcount ips:2018-10-04
(integer) 4
复制代码
上面就是演示了,2018-10-04这天,约4个ip登录了系统! 网上有一张和传统集合结构的占用空间对比图,贴出来,给你们看看
注意了,再强调一次,使用此结构是存在偏差的!好比你pfadd
了一百万条数据进去,结果pfcount
的结果可能就999756条!
Geo能够用于存储经纬度、计算两地之间的距离、范围计算等。其底层实现是zset。
主要有如下六组命令
geoadd
:增长某个地理位置的坐标。geopos
:获取某个地理位置的坐标。geodist
:获取两个地理位置的距离。georadius
:根据给定地理位置坐标获取指定范围内的地理位置集合。georadiusbymember
:根据给定地理位置获取指定范围内的地理位置集合geohash
:获取某个地理位置的geohash值。我这里直接贴官网文档的例子,你们有兴趣能够自行查询. 首先,先给key增长两个坐标
redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
复制代码
其次,计算两个坐标之间的举例
redis> GEODIST Sicily Palermo Catania
"166274.15156960039"
复制代码
最后,计算距离经纬度(15,37)
距离100km
和200km
范围内的坐标有哪些
redis> GEORADIUS Sicily 15 37 100 km
1) "Catania"
redis> GEORADIUS Sicily 15 37 200 km
1) "Palermo"
2) "Catania"
复制代码