Redis-2-发布订阅&新数据类型&Jedis

1 发布&订阅

1.1 发布订阅模型

发布/订阅:一种消息通信模式
发布者(pub)发送消息 订阅者(sub)接收消息
Redis客户端可以订阅任意数量的频道

请添加图片描述

1.2 命令实现发布/订阅

1,打开redis-server

2,打开redis-cli 称为cli1
subscribe channel1 
订阅channel1

3,打开redis-cli 称为cli2
publish channel1 hello
向所有订阅了channel1的机器发布消息

在这里插入图片描述
关于发布/订阅模型,类似于观察者模式,一个Speaker保存一个List,该List中存的是Listener
当Speaker发布消息时,遍历这个List实现一个方法即可

2 新数据类型

2.1 Bitmap

一个字节有8位,合理的操作位能有效提高内存使用率和开发效率
Redis中提供了Bitmap这个数据类型,可以实现对位的操作

1,Bitmap本身不算是一种数据结构 实际上就是字符串KV
但它可以对字符串的位进行操作

2,Bitmap单独提供了一套命令,所以在Redis中使用Bitmap和使用字符串的方法不太相同
可以把Bitmaps看作一个以位为单位的数组,数组的每个单元只能存储0或1 数组的下标在Bitmap中称为偏移量

请添加图片描述

2.1.1 命令

1,setbit key offset value
设置Bitmap中某个偏移量的值(0或1) 偏移量从0开始

可以将每个独立用户是否访问网站的记录存放在bitmap中 访问记1 未访问记0
用偏移量作为用户的id 假设现在有20个用户 偏移量从0-19 其中1,6,11,15用户访问了网站
key记为users:20211215
设置bitmap位则是 setbit users:20211215 1 1

请添加图片描述
很多用户的id可能以一个数字如1000开头,直接将offset和用户id映射会造成不必要的浪费
通常做法是每次setbit时将用户id减去这个id,在第一次初始化bitmap时,假如偏移量非常大可能造成Redis阻塞

2,getbit key offset
获取bitmap中某个偏移量的值

如getbit user:20211215 14

请添加图片描述

3,bitcount key [start end]
获取key对应的bitmap中值为1的个数
加上start和end表指定偏移量区间 

2.1.2 Bitmap和set的比较

假设某网站有1亿用户,每天访问量5千万,用set和bitmap记录活跃用户对比如下:
请添加图片描述
在这种场景下使用bitmap能大大节约内存,且随着时间推移bitmap更加节省内存
请添加图片描述
但bitmap的使用也要结合场景,假如该网站每天只有10万访问量,那么此时set就更加合适
请添加图片描述

2.2 HyperLogLog

2.2.1 简介

在工作中有统计网站访问量等问题,可以用Redis的incr,incrby来解决
但是像求独立访问者,独立ip数,搜索记录数等需要去重和计数的问题应当如何解决
这种问题称为基数问题

什么是基数
数据集{1,3,5,7,5,7,8} 其基数集为{1,3,5,7,8}
基数(不重复元素)为5

解决基数问题有多种方案如使用Redis的hash或set来存储
或者存储到MySQL中使用distinct count来去重
但这些解决方案都需要存储数据,对非常大的数据集不切实际,而我们的目的也只是"不重复的有多少"

Redis的HyperLogLog是用来做基数统计的算法
在输入数据集非常大时,计算基数所需的空间是固定且很小的
在Redis中每个HyperLogLog键只需要12KB内存 就可以计算接近2^64个不同元素基数 非常节省空间

2.2.2 命令

1,pfadd key element [element...]
添加指定元素到HyperLogLog中

2,pfcount key
统计key中基数个数

请添加图片描述

2.3 Geospatial

2.3.1 简介

在Redis3.2中增加了对GEO类型的支持 GEO即Geospatial地理信息的缩写
该类型就是元素的二维坐标,地图上就是经纬
Redis基于该类型,提供了经纬度查询,范围查询,距离查询,经纬度Hash等常见操作

2.3.2 命令

1,geoadd key 经度 纬度 member
添加member的经纬到key中 

2,geopos key member
查询key中member的经纬

在这里插入图片描述

3 Jedis使用

3.1 Jedis使用测试

导入依赖:

	<dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.7.0</version>
      </dependency>

测试能否连接成功:
请添加图片描述

3.2 API的简单使用

1,使用String相关API:

   @Test
    public void demo1() {
        Jedis jedis = new Jedis("127.0.0.1", 6379);

        // 添加kv
        jedis.set("name", "lucky");

        // 根据k获取v 获取过期时间
        System.out.println(jedis.get("name") + " TTL:" + jedis.ttl("name"));

        // 设置多个kv
        jedis.mset("k1", "v1", "k2", "v2");
        List<String> mget = jedis.mget("k1", "k2");
        System.out.println(mget.get(0) + ", " + mget.get(1));
        
        // 

        // 获取当前库中所有key
        Set<String> set =  jedis.keys("*");
        for (String key : set) {
            System.out.println(key);
        }

    }

2,使用其他数据结构API

	@Test
    public void demo2() {
        Jedis jedis = new Jedis("127.0.0.1", 6379);

        // 使用list
        jedis.lpush("list1", "l1", "l2", "l3", "l4");

        List<String> values = jedis.lrange("list1", 0, -1);
        System.out.println(values);

        // 使用Set
        jedis.sadd("name", "lucy", "lucy", "jack", "jack", "lucy");
        Set<String> set = jedis.smembers("name");
        System.out.println(set);

        // 使用hash
        jedis.hset("users", "field1", "20");
        System.out.println(jedis.hget("users", "field1"));

        // 使用zset
        jedis.zadd("class1", 100, "tom");
        jedis.zadd("class1", 95, "jack");
        jedis.zadd("class1", 120, "lucy");
        System.out.println(jedis.zrange("class1", 0, -1));

    }

3.3 Jedis模拟验证码发送

要求:

1,输入手机号,点击生成6位验证码,2分钟有效(SE的RandomAPI+RedisTTL)

2,输入验证码,点击验证,返回成功/失败(根据K取V判断)

3,每个手机号每天只能输入3次(Redis的incr)

伪代码:

	public static void main(String[] args) {

        Jedis jedis = new Jedis("127.0.0.1", 6379);

        verifyCode("1111111");
        System.out.println(checkCode("1111111", jedis.get("verify" + "1111111" + "code")));

    }

    private static String creatRandomCode() {
        Random random = new Random();
        String code = "";
        for(int i = 0; i < 6; i++) {
            code += random.nextInt(10);
        }
        return code;
    }

    private static void verifyCode(String phoneNum) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);

        String countKey = "verify" + phoneNum + "count";
        String codeKey = "verify" + phoneNum + "code";

        String count = jedis.get(codeKey);
        if(count == null) {
            jedis.setex(countKey, 24*60*60, "1");
        } else if(Integer.valueOf(count) <= 2) {
            jedis.incr(countKey);
        } else if(Integer.parseInt(count) > 2) {
            System.out.println("次数超过3次");
            jedis.close();
            return;
        }

        jedis.setex(codeKey, 120, creatRandomCode());
        jedis.close();
    }

    private static boolean checkCode(String phoneNum, String code) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);

        String codeKey = "verify" + phoneNum + "code";
        String redisCode = jedis.get(codeKey);
        if(code.equals(redisCode)) {
            return true;
        }
        return false;
    }

3.4 SpringBoot整合Redis

1,导入依赖

		<!--redis-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency> 

		<!--spring2.x集成redis所需common-pool2-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.6.0</version>
        </dependency>

2,application.properties配置redis配置

# Redis数据库索引(默认为0)  
spring.redis.database=0  
# Redis服务器地址  
spring.redis.host=192.168.0.24  
# Redis服务器连接端口  
spring.redis.port=6379  
# Redis服务器连接密码(默认为空)  
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)  
spring.redis.pool.max-active=200  
# 连接池最大阻塞等待时间(使用负值表示没有限制)  
spring.redis.pool.max-wait=-1  
# 连接池中的最大空闲连接  
spring.redis.pool.max-idle=10 
# 连接池中的最小空闲连接  
spring.redis.pool.min-idle=0  
# 连接超时时间(毫秒)  
spring.redis.timeout=1000 

3,添加Redis配置类

@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(
            RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    public StringRedisTemplate stringRedisTemplate(
            RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

}

4,测试

@RestController
@RequestMapping("/redisTest")
public class TestRedis {
    
    @Autowired
    private RedisTemplate redisTemplate;
    
    @GetMapping
    public String testRedis() {
        redisTemplate.opsForValue().set("name", "tom");
        return (String) redisTemplate.opsForValue().get("name");
    }
    
   
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值