redis-04-redis高级数据结构功能

1-HyperLogLog

1.1-HyperLogLog介绍

Redis 在2.8.9 版本添加了 HyperLogLog 结构

Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。

在 Redis 里面,每个 HyperLogLog 健只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗内存就越多的焦合形成鲜明对比。但是,因为 HyperLogLog 只会根输入元素来计算基数,而不会存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。

https://redis.io/docs/data-types/hyperloglogs/

http://antirez.com/news/75

数学原理

伯努利试验(Bernoulli experiment):是在同样的条件下重复地、相互独立地进行的一种随机试验,其特点是该随机试验只有两种可能结果:发生或者不发生。我们假设该项试验独立重复地进行了n次,那么就称这一系列重复独立的随机试验为n重伯努利试验,或称为伯努利概型。

HyperLogLog基于概率论中伯努利试验并结合了极大似然估算方法,并做了分桶优化

HyperLogLog统计数据误差有0.81%;

1.2-使用场景

一般用于海量统计数据(需要去重复),不太涉及具体的统计对象的内容和精准性;

比如统计网站(淘宝,京东,天猫首页)的UV(Unique Visitor,独立访客,一般理解为客户端IP,需要去重复)。

每日注册 IP 数、每日访问 IP 数、用户访问数UV ;

因为主要的目标高效、巨量地进行计数,所以对存储的数据的内容并不太关心。

1.3-相关命令

pfadd key element [element …]

pfadd 添加元素,如果该元素不存在,返回1,存在返回0

pfcount key [key …]

pfcount key1:统计key1里面元素的个数

pfcount key1 key2:统计key1,key2里面中去掉重复元素个数

pfmerge destkey sourcekey [sourcekey ... ]

pfmerge可以求出多个HyperLogLog的并集并赋值给destkey

详细情况测试见如下命令

root@42f79a3aab8b:/data# redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> 
127.0.0.1:6379> pfadd hll 101
(integer) 1
127.0.0.1:6379> pfadd hll 102
(integer) 1
127.0.0.1:6379> pfadd hll 103
(integer) 1
127.0.0.1:6379> pfadd hll 104
(integer) 1
127.0.0.1:6379> pfadd hll 101
(integer) 0
127.0.0.1:6379> pfcount hll
(integer) 4
127.0.0.1:6379> 
127.0.0.1:6379> pfadd hll01 1001
(integer) 1
127.0.0.1:6379> pfadd hll01 1002
(integer) 1
127.0.0.1:6379> pfadd hll01 1003
(integer) 1
127.0.0.1:6379> pfadd hll01 1004
(integer) 1
127.0.0.1:6379> pfadd hll01 101
(integer) 1
127.0.0.1:6379> pfcount hll01
(integer) 5
127.0.0.1:6379> pfcount hll hll01
(integer) 8
127.0.0.1:6379> 
127.0.0.1:6379> pfmerge hll-dest hll hll01
OK
127.0.0.1:6379> pfcount hll-dest
(integer) 8
127.0.0.1:6379> 

2-Bitmaps和布隆过滤器

2.1-Bitmaps

记录签到未签到

LocalDateTime localDateTime=LocalDateTime.now();
int dayOfMonth = localDateTime.getDayOfMonth();
String yyyyMMdd = localDateTime.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
String userId="1001";
String key=new StringBuilder("user:sign:").append(userId).append(":").append(yyyyMMdd).toString();
stringRedisTemplate.opsForValue().setBit(key,dayOfMonth-1,true);

setbit user:sign:1001:202301 0 1:表示id=1001的用户在2023年1月1日签到了...

setbit user:sign:1001:202301 1 1:表示id=1001的用户在2023年1月2日签到了...

setbit user:sign:1001:202301 2 1:表示id=1001的用户在2023年1月3日签到了...

setbit user:sign:1001:202301 3 0:表示id=1001的用户在2023年1月4日没有签到了...

.....

setbit user:sign:1001:202301 13 0

签到天数

BITCOUNT key [start end [BYTE | BIT]]

BITCOUNT :统计BitMap中值为1的bit位的数量

bitcount user:sign:1001:202301:统计id=1001的用户在2023年1月签到天数10

BITFIELD :操作(查询、修改、自增)BitMap中bit数组中的指定位置(offset)的值

BITFIELD key [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]

<SET encoding offset value | INCRBY encoding offset increment>

[GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]

<SET encoding offset value | INCRBY encoding offset increment>

...]]

bitfield user:sign:1001:202301 get u16 0

 LocalDateTime localDateTime=LocalDateTime.now();
        int dayOfMonth = localDateTime.getDayOfMonth();
        String yyyyMMdd = localDateTime.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String userId="1001";
        String key=new StringBuilder("user:sign:").append(userId).append(":").append(yyyyMMdd).toString();


        //连续签到
        //获取本月截止到今天为止所有的签到记录,返回的是一个十进制的数字  bitfield user:sign:1001:202301 get u16 0
        List<Long> result = stringRedisTemplate.opsForValue().bitField(
                key,
                BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
        );
        int count=0;//连续签到的天数
        if(!CollectionUtils.isEmpty(result)){
            Long num = result.get(0);
            while (true){
                if((num & 1)==0){
                    //如果为0,说明未签到,结束
                    break;
                }else{
                    count++;
                }
                //继续右移一位,抛弃最后一个bit,继续下一个bit
                num>>>=1;
            }


        }

本月签到天数:

 LocalDateTime localDateTime=LocalDateTime.now();
        int dayOfMonth = localDateTime.getDayOfMonth();
        String yyyyMMdd = localDateTime.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String userId="1001";
        String key=new StringBuilder("user:sign:").append(userId).append(":").append(yyyyMMdd).toString();
      //签到天数 
        int days  = (int) redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(key.getBytes()));

2.2-布隆过滤器介绍

Bloom Filter 相当于是一个不太精确的 set 集合,我们可以利用它里边的 contains 方法去判断某一个对象是否存在,但是需要注意,这个判断不是特别精确。一般来说,通过 contains 判断某个值不存在,那就一定不存在,但是判断某个值存在的话,则他可能不存在。一般不能在布隆过滤器中删除元素,因为hash会出现误删的可能性,如果满了,建议重建布隆过滤器。

每一个布隆过滤器,在 Redis 中都对应了一个大型的位数组以及几个不同的 hash 函数(无偏hash)。

优点:占用空间少【不存储内容,只存标志位】

缺点:不能删除元素,存在误判,不能精准...存在是可能存在,不存在是肯定不存在

2.3-使用场景

比如我们有一个黑名单电话集合的,判断一个电话是否在该集合中;比如我们判断用户存在不存在,先同步布隆过滤器来检查,如果不存在肯定不存在,就不用去mysql查询

2.4-相关命令

https://oss.redislabs.com/redisbloom/Quick_Start/

bf.add 添加

bf.madd 批量添加

bf.exists 判断是否存在

bf.mexists 批量判断


<dependency>
    <groupId>com.redislabs</groupId>
    <artifactId>jrebloom</artifactId>
    <version>2.2.2</version>
</dependency>

3-GEO

3.1-GEO简介

https://redis.io/docs/data-types/geospatial/

https://redis.io/commands/?group=geo

redis GEO在3.2版本就支持,存储地理位置坐标信息,根据地理位置坐标信息(经度,纬度来计算或者检索数据);

3.2-使用场景

比如微信附近的人,美团附近的酒店之类的

3.3-相关命令

GEOADD key [NX | XX] [CH] longitude latitude member [longitude latitude member ...]

示例:

geoadd city 116.41339 39.91092 beijing

geoadd city 114.06455 22.54846 shenzhen

geoadd city 113.27143 23.13534 guangzhou

geoadd city 121.48054 31.23593 shanghai

geoadd city -73.86483 40.84478 NewYork

geoadd city -118.24532 34.05349 LosAngeles

GEODIST key member1 member2 [M | KM | FT | MI]

示例:

geodist city beijing NewYork KM

geodist city shenzhen guangzhou KM

geodist city beijing guangzhou KM

GEOPOS key [member [member ...]]

示例:

geopos city guangzhou

GEORADIUS key longitude latitude radius <M | KM | FT | MI>

[WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]

[STORE key] [STOREDIST key]

GEORADIUS (deprecated) 这个命令已经在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]

WITHDIST:在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。

WITHCOORD:将位置元素的经度和维度也一并返回。

WITHHASH:以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试实际中的作用并不大

示例:

geosearch city frommember guangzhou byradius 1500 KM desc count 6 withcoord withdist withhash

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]] [STOREDIST]

This command is like GEOSEARCH, but stores the result in destination key.

127.0.0.1:6379> geoadd city 116.41339 39.91092 beijing
(integer) 1
127.0.0.1:6379> geoadd city 114.06455 22.54846 shenzhen
(integer) 1
127.0.0.1:6379> geoadd city 113.27143 23.13534 guangzhou
(integer) 1
127.0.0.1:6379> geoadd city 121.48054 31.23593 shanghai
(integer) 1
127.0.0.1:6379> geoadd city -73.86483 40.84478 NewYork
(integer) 1
127.0.0.1:6379> geoadd city -118.24532 34.05349 LosAngeles
(integer) 1
127.0.0.1:6379> 



127.0.0.1:6379> geodist city beijing NewYork KM
"10978.5011"
127.0.0.1:6379> geodist city shenzhen guangzhou KM
"104.2608"
127.0.0.1:6379> geodist city beijing guangzhou KM
"1889.1561"
127.0.0.1:6379> 


127.0.0.1:6379> geopos city guangzhou
1) 1) "113.2714274525642395"
   2) "23.13533913547613707"
127.0.0.1:6379> 


127.0.0.1:6379> geosearch city frommember guangzhou byradius 1500 KM desc count 6 withcoord withdist withhash
1) 1) "shanghai"
   2) "1212.1327"
   3) (integer) 4054803475556787
   4) 1) "121.48053735494613647"
      2) "31.23593028727068344"
2) 1) "shenzhen"
   2) "104.2608"
   3) (integer) 4046432291729766
   4) 1) "114.06455129384994507"
      2) "22.5484599371274399"
3) 1) "guangzhou"
   2) "0.0000"
   3) (integer) 4046533940206308
   4) 1) "113.2714274525642395"
      2) "23.13533913547613707"
127.0.0.1:6379> 
127.0.0.1:6379> geosearch city frommember guangzhou byradius 1500 KM desc count 6  withdist
1) 1) "shanghai"
   2) "1212.1327"
2) 1) "shenzhen"
   2) "104.2608"
3) 1) "guangzhou"
   2) "0.0000"

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值