1. bitmaps与bitfields
Redis中提供了bitmaps实现对位的操作,应用在统计月活跃用户数【活跃用户量千万级别】时比set能够明显的提高内存使用率
我们可以把bitmaps想象为一个以位为单位的数组,数组每个单元智能存储0和1,数字的下标在bitmaps中称为偏移量offset
1.1 bitmaps基本操作及底层数据结构
# setbit <key> <offset> <value>设置键指定偏移量的值
127.0.0.1:6379> setbit user 0 1
(integer) 0
127.0.0.1:6379> setbit user 1 1
(integer) 0
127.0.0.1:6379> setbit user 2 0
(integer) 0
127.0.0.1:6379> setbit user 3 1
(integer) 0
# getbit <key> <offset>获取键指定便宜量的值
127.0.0.1:6379> getbit user 0
(integer) 1
# bitcount <key> [start end]统计指定范围内比特值为1的数量
127.0.0.1:6379> bitcount user 0 -1
(integer) 3
127.0.0.1:6379> setbit user_day2 0 0
(integer) 0
127.0.0.1:6379> setbit user_day2 1 1
(integer) 0
127.0.0.1:6379> setbit user_day2 2 1
(integer) 0
127.0.0.1:6379> setbit user_day2 3 0
(integer) 0
# bitop and/or <destkey> key [key...]返回多个bitmaps的交集/并集到目标key中
127.0.0.1:6379> bitop and user_day1_2 user user_day2
(integer) 1
## 比如该例子返回两天都访问的用户数
127.0.0.1:6379> bitcount user_day1_2
(integer) 1
127.0.0.1:6379> bitop or all_day1_2 user user_day2
(integer) 1
## 比如该例子返回两天总共访问的用户数
127.0.0.1:6379> bitcount all_day1_2
(integer) 4
# 可以看到内部编码为字符串的编码方式raw
127.0.0.1:6379> object encoding user
"raw"
1.2 bitfields基本操作及底层数据结构
对于我们想自定义存储位数,可以采用bitfield
# bitfield key set/get encoding offset value
# bitfield存储32位偏移量为0的值为8888
127.0.0.1:6379> bitfield user set u32 0 8888
1) (integer) 3489660928
# bitfield获取32位偏移量为0的值
127.0.0.1:6379> bitfield user get u32 0
1) (integer) 8888
# bitfield为32位偏移量为0的值增加1000
127.0.0.1:6379> bitfield user incrby u32 0 1000
1) (integer) 9888
# 可以看到内部编码为字符串的编码方式raw
127.0.0.1:6379> object encoding user
"raw"
2. HyperLogLog
我们讲解Redis中基数统计利器HyperLogLog[简称HLL]基本操作及原理
对于海量级别UV(独立访客)、独立IP数等需要去重计数问题如何解决?对于求集合中不重复元素个数的问题称为基数问题
2.1 基本操作
# pfadd添加元素至HLL
127.0.0.1:6379> pfadd user 1 2 3
(integer) 3
# 再次添加重复元素返回0,不成功
127.0.0.1:6379> pfadd user 2
(integer) 0
# pfcount计算HLL的近似基数
127.0.0.1:6379> pfcount user
(integer) 3
127.0.0.1:6379> pfadd user2 2 4
(integer) 2
# pfmerge合并两个或多个HLL至新的HLL
127.0.0.1:6379> pfmerge all_user user user2
OK
127.0.0.1:6379> pfcount all_user
(integer) 4
2.2 底层原理
# 内部编码同样为raw
127.0.0.1:6379> object encoding all_user
"raw"
为了解决基数问题,我们可以通过数据库distinct或者用redis中我们之前说到的hash \set\bitmaps来处理,对于bitmaps最为节省:一个1G的空间,有 8102410241024=8.5810^9bit, 也就是可以表示85亿个不同的数,但是当数据量为2^64bit=2048PB,这已经不是硬件可以解决的问题了,所以HLL就派上了用场
每个HyperLogLog键只需要占用12KB内存,就可以计算接近2^64个不同元素的基数。
因为HyperLogLog只会根据输入元素来近似计算基数,而不会储存输入元素本身,所以 HyperLogLog不返回输入的各个元素
HyperLogLog可类比伯努利试验,HyperLogLog设置为:m=16834[分桶对应实验轮数],p=6[每个桶占用bit位存储存储k_max最大抛掷次数],L[总长度]=16834 * 6。占用内存为=16834 * 6 / 8 / 1024 = 12K
具体参考https://juejin.cn/post/6844903785744056333#heading-4
3. GeoSpatial
我们讲解redis中对经纬度操作支持的数据类型GeoSpatial
3.1 geoadd经纬度添加
我们打开https://lbs.amap.com/tools/picker进行坐标拾取,分别拾取静安区
、黄浦区、杨浦区、浦东新区坐标点
127.0.0.1:6379> geoadd region 121.447348 31.227718 jingan
(integer) 1
127.0.0.1:6379> geoadd region 121.48442 31.231661 huangpu
(integer) 1
127.0.0.1:6379> geoadd region 121.525409 31.259588 yangpu
(integer) 1
127.0.0.1:6379> geoadd region 121.544346 31.221461 pudong
(integer) 1
3.2 geodist求两点之间距离
# 计算静安与黄浦直线距离
127.0.0.1:6379> geodist region jingan huangpu
"3553.2114"
3.3 geohash编码
GeoHash就是一种将经纬度转换成字符串的方法,字符串前缀匹配越多的距离越近
127.0.0.1:6379> geohash region jingan huangpu
1) "wtw3esg68k0"
2) "wtw3smw3850"
3.4 geopos返回坐标点
127.0.0.1:6379> geopos region jingan
1) 1) "121.44734770059585571"
2) "31.22771779071442921"
3.5 返回圆形内所有点
# 自从6.2.0开始,采用geosearch
# 通过坐标点名搜索周边4公里点
127.0.0.1:6379> geosearch region frommember jingan BYRADIUS 4 KM
1) "jingan"
2) "huangpu"
# 通过具体坐标点搜索周边4公里点
127.0.0.1:6379> geosearch region fromlonlat 121.447348 31.227718 BYRADIUS 4 KM
1) "jingan"
2) "huangpu"
# 返回额外距离、经纬度信息
127.0.0.1:6379> geosearch region fromlonlat 121.447348 31.227718 BYRADIUS 4 KM withdist withcoord
1) 1) "jingan"
2) "0.0000"
3) 1) "121.44734770059585571"
2) "31.22771779071442921"
2) 1) "huangpu"
2) "3.5532"
3) 1) "121.48442119359970093"
2) "31.23166181683836129"
3.6 返回矩形内点
# 以静安为矩形坐标中心点,查找宽高各为8KM的矩形内点
127.0.0.1:6379> geosearch region fromlonlat 121.447348 31.227718 bybox 8 8 KM
1) "jingan"
2) "huangpu"
3.7 存储搜索结果到新key
127.0.0.1:6379> geosearchstore result region fromlonlat 121.447348 31.227718 bybox 8 8 KM
(integer) 2
127.0.0.1:6379> zrange result 0 -1
1) "jingan"
2) "huangpu"
一般在工作中,用ES坐标点geo_point及形状geo_shape[支持多边形围栏搜索]比较多些,但也要看实际工作需求了