Redis高级:GEO&BitMap&HyperLogLog
GEO、BitMap、HyperLogLog是redis中三种特殊的数据结构,一般用于实现特殊的业务场景,其底层都是基于redis中的基础数据类型存储的
1 GEO
GEO就是Geolocation的简写形式,代表一个地理坐标。Redis在3.2版本中加入了对GEO的支持,允许存储地理坐标信息,帮助我们根据经纬度来检索数据。
GEO底层是按照SortedSet存储的,value是我们在添加地理空间时设置的member,score是通过经纬度计算得来的Hash值
GEO常用命令如下:
GEOADD
-
作用:添加一个地理空间信息,包含:经度(longitude)、纬度(latitude)、值(member)
-
语法:
geoadd key [NX|XX] [CH] longitude latitude member [longitude latitude member ...]
-
备注:经纬度必须合法
-
案例:添加数据:北京南站(116.378248 39.865275)、北京站(116.42803 39.903738)、北京西站(116.322287 39.893729)
GEODIST
-
作用:计算指定的两个点之间的距离并返回
-
语法:
geodist key member1 member2 [m|km|ft|mi]
-
备注:不指定单位默认为米
-
案例:计算北京西站到北京站的距离
GEOPOS
-
作用:返回指定member的坐标
-
语法:
geopos key member [member ...]
-
案例:查询北京西站的地理坐标
GEOHASH
-
作用:将指定member的坐标转为hash字符串形式并返回
-
语法:
geohash key member [member ...]
-
案例:将北京西站的地理坐标转为hash字符串形式并返回
GEORADIUS
-
作用:指定圆心、半径,找到该圆内包含的所有member,并按照与圆心之间的距离排序后返回。6.2以后已废弃
-
语法:
georadius key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC|DESC] [STORE key] [STOREDIST key]
-
备注:各参数含义如下
参数 含义 `key longitude latitude radius m km [WITHCOORD] [WITHDIST] [WITHHASH]
分别对应:是否返回匹配对象的经纬坐标、是否返回匹配对象与圆心的距离、是否返回匹配对象对应的hash串 [COUNT count [ANY]]
需要查找的member的数量 `[ASC DESC]` [STORE key]
将查询结果存储到另一个geo中 [STOREDIST key]
将查询结果与圆心的距离的集合存储到一个SortedSet中 -
案例:搜索天安门(116.397904 39.909005)附近 10km 内的所有火车站,显示这些火车站的经纬度以及到天安门的距离,并按照距离升序排序
GEOSEARCH
-
作用:在指定范围内搜索member,并按照与指定点之间的距离排序后返回。范围可以是圆形或矩形。6.2新功能
-
语法:
geosearch key [FROMMEMBER member] [FROMLONLAT longitude latitude] [BYRADIUS radius m|km|ft|mi] [BYBOX width height m|km|ft|mi] [ASC|DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST] [WITHHASH]
-
备注:各参数含义如下
参数 含义 [FROMMEMBER member] [FROMLONLAT longitude latitude]
是根据指定member查询还是指定经纬度查询 `[BYRADIUS radius m km `[ASC DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST] [WITHHASH]` -
案例:搜索以天安门(116.397904 39.909005)为中心,10km 矩形范围内最近的前两个火车站(按照距离升序排序),显示这些火车站的经纬度以及到天安门的距离
GEOSEARCHSTORE
-
作用:与GEOSEARCH功能一致,不过可以把结果存储到一个指定的key, 6.2新功能
-
语法:
geosearchstore destination source [FROMMEMBER member] [FROMLONLAT longitude latitude] [BYRADIUS radius m|km|ft|mi] [BYBOX width height m|km|ft|mi] [ASC|DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST] [WITHHASH]
-
备注:source表示查询的geo,destination表示进行结果存储的geo,
-
案例:搜索以天安门(116.397904 39.909005)为中心,10km 矩形范围内最近的前两个火车站(按照距离升序排序),显示这些火车站的经纬度以及到天安门的距离,并将结果存储到另一个geo中
2 BitMap
BitMap 即位图数据结构,通过操作二进制位数据来记录信息,只包含0或1。
由于其占用内存空间小的特点,常用于数据量大但却只有两种情况的信息,这类信息使用mysql存储往往会占用大量磁盘资源,例如用户是否在线,用户是否登录,用户是否签到等等,像这类信息就适用于使用BitMap来存储
BitMap是基于String类型存储的,一个BitMap存储的上限取决于String大小的上限,而Redis中String大小上限是512M,转换成bit则是2^32个bit位
BitMap的常用操作命令如下:
SETBIT
-
作用:向指定位置(offset)存入一个0或1
-
语法:
setbit key offset value
-
案例:在bitmap的第5个位置存入1(注意索引从0开始)
执行结束后,我们可以去redis图形化界面中查看key为k3的数据
注意:BitMap是使用String类型来存储的,而String的最小单位是字节,1字节等于8bit,所以BitMap存储的数据个数必须为8的倍数,我们这里只将第5个位置的数据修改成了1,其他未被修改的位置会以0表示。
GETBIT
-
作用:获取指定位置(offset)的bit值
-
语法:
getbit key offset
-
演示:
BITCOUNT
-
作用:统计BitMap中值为1的bit位的数量,可以指定统计的范围
-
语法:
bitcount key [start end]
-
演示:
BITFIELD
-
作用:操作(查询、修改、自增)BitMap中bit数组中的指定位置(offset)的值
-
语法:
bitfield key [GET type offset] [OVERFLOW WRAP|SAT|FAIL]
-
备注:一般只会用来查询,故这里只列出查询的语法,查询的返回结果会换算成十进制
-
演示:
get u5 0
表示从第1个数字开始,截取5位数字,并将结果换算成无符号十进制整数。其中的u表示无符号,如果想要换算成有符号的十进制整数可以使用i,即get i5 0
,5表示截取5位数字,0表示从第1个数字开始这里返回结果为5,而k3中前五位数字换算成10进制的值就是5
BITFIELD_RO
- 作用:获取BitMap中bit数组,并以十进制形式返回
- 备注:与BITFIELD使用基本相同,只不过BITFIELD_RO只能用来查询
BITOP
- 作用:将多个BitMap的结果做位运算(与 、或、异或)
- 语法:
bitop operation destkey key [key ...]
BITPOS
-
作用:查找bit数组中指定范围内第一个0或1出现的位置
-
语法:
bitpos key bit [start] [end]
-
演示:
3 HyperLogLog
Hyperloglog(HLL)是从Loglog算法派生的概率算法,用于确定非常大的集合的基数,而不需要存储其所有值。相关算法原理大家可以参考:https://juejin.cn/post/6844903785744056333#heading-0
Hyperloglog适用于进行网站的UV统计
UV全称Unique Visitor,也叫独立访客量,是指通过互联网访问、浏览这个网页的自然人。1天内同一个用户多次访问该网站,只会记录1次访问。UV统计在服务端做会比较麻烦,因为要判断该用户是否已经统计过了,需要将统计过的用户信息保存。但是如果每个访问的用户都保存到Redis中,数据量会非常恐怖,这时候我们就可以通过Hyperloglog进行数量统计
Redis中的HLL是基于string结构实现的,单个HLL的内存永远小于16kb,内存占用低的令人发指!作为代价,其测量结果是概率性的,有小于0.81%的误差。不过对于UV统计来说,这完全可以忽略。
另外,Hyperloglog只能对不重复的数据进行统计,如果数据是允许重复的话,就不能使用Hyperloglog来统计了。
Hyperloglog的常用命令有以下几个:
PFADD
- 作用:将一条或多条数据插入到HLL中
- 语法:
pfadd key element [element ...]
PFCOUNT
- 作用:统计指定HLL中元素的个数,有小于0.81%的误差
- 语法:
pfcount key [key...]
演示:我们可以通过Java代码,向HyperLogLog添加100万条数据,看看统计结果如何:
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
void testHyperLogLog() {
String[] values = new String[1000];
int j = 0;
for (int i = 0; i < 1000000; i++) {
j = i % 1000;
values[j] = "user_" + i;
if(j == 999){
// 发送到 Redis
stringRedisTemplate.opsForHyperLogLog().add("hl2", values);
}
}
// 统计数量
Long count = stringRedisTemplate.opsForHyperLogLog().size("hl2");
System.out.println("count = " + count);
}
输出结果如下:
通过比对数据插入前后redis内存占用的变化,发现这100万条数据仅仅只占用了14KB的内存