Redis基础知识二

1、redis发布订阅模式

Redis中的list结构可以作为队列来满足一些生产消费的业务场景。实际上Redis还提供了发布/订阅(publish/subscribe)模式来实现类似的生产消费的功能。

1.1 list与发布/订阅的不同

  • list中的任务或消息无法被重复消费,消息被一个消费者pop 掉以后,其他消费者就获取不到了这个消息了。而发布/订阅模式中可以有多个订阅者消费同一个消息。
  • list可以保存任务或消息,直到客户端连接之后才消费掉。但发布/订阅模式中订阅者无法获取到订阅之前的历史消息,由于这个缺陷,在一些严格的生产消费场景下,建议还是用MQ的主题模式。

1.2 什么是发布订阅模式

发布/订阅模式模式包含两种角色:发布者和订阅者。订阅者可以订阅一个或多个通道(channel),而发布者可以向指定的通道(channel)中发送消息,所有此通道的订阅者都会收到消息。

1.3 发布订阅实现

打开两个客户端,一个客户端订阅信道,一个客户端发送消息,另一个客户端则会收到消息,效果如下:

2、redis和SpringBoot整合

2.1 jedis测试

1、需要修改redis配置文件,设置redis.conf中注释掉bind 127.0.0.1 ,然后 protected-mode no 2、引入依赖

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

3、测试demo

public class JedisDemo1 {

    public static void main(String[] args) {
        //创建Jedis对象
        Jedis jedis = new Jedis("具体ip",6379);
        //测试
        String value = jedis.ping();
        System.out.println(value);
        jedis.close();
    }
}

2、SpringBooot整合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.yml配置redis配置

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

3、 使用ObjectMapper 需要导入依赖

<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-databind</artifactId>
	<version>2.9.5</version>
</dependency>

<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-core</artifactId>
	<version>2.9.5</version>
</dependency>

<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-annotations</artifactId>
	<version>2.9.5</version>
</dependency>

4、添加redis的配置类

/**
 * 功能说明: redis配置类
 *
 *
 * @author yangxu
 * @date 2022/4/3
 */

@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {


  @Bean
  public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
    // 改为自己要操作的对象模式
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(factory);
    // Object 如何序列化的问题
    Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
    ObjectMapper mapper = new ObjectMapper();
    mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    jackson2JsonRedisSerializer.setObjectMapper(mapper);

    StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
    // key: String的序列化方式
    template.setKeySerializer(stringRedisSerializer);
    // hash: String的序列化方式
    template.setHashKeySerializer(stringRedisSerializer);
    // Object: Jsckson
    template.setValueSerializer(jackson2JsonRedisSerializer);
    // hashObject: Jsckson
    template.setHashValueSerializer(jackson2JsonRedisSerializer);
    // 其与的类型设置同理
    template.afterPropertiesSet();
    return template;
  }



    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
      RedisSerializer<String> redisSerializer = new StringRedisSerializer();
      Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
      // 解决查询缓存转换异常的问题
      ObjectMapper om = new ObjectMapper();
      om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
      om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
      jackson2JsonRedisSerializer.setObjectMapper(om);
      // 配置序列化(解决乱码的问题),过期时间3600秒
      RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
          .entryTtl(Duration.ofSeconds(3600))
          .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
          .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
          .disableCachingNullValues();
      RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(config).build();
      return cacheManager;
    }

}

5、测试 1、测试demo

@RestController
@RequestMapping("/redisTest")
public class RedisTestController {
    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping
    public String testRedis() {
        String name = "xuzai";
        //设置值到redis
        redisTemplate.opsForValue().set("name",name);
        //从redis获取值
        String username = (String)redisTemplate.opsForValue().get("name");
        return username;
    }
}

2、测试效果

3、redis事务

Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

MULTI,EXEC,DISCARD,WATCH 四个命令是 Redis 事务的四个基础命令。其中:

# MULTI,告诉 Redis 服务器开启一个事务。注意,只是开启,而不是执行 。
# EXEC,告诉 Redis 开始执行事务 。
# DISCARD,告诉 Redis 取消事务。
# WATCH,监视某一个键值对,它的作用是在事务执行之前如果监视的键值被修改,事务会被取消。

从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行。 组队的过程中可以通过discard来放弃组队。

3.1 事务的错误处理

组队中某个命令出现了报告错误,执行时整个的所有队列都会被取消。

如果执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。

3.2 WATCH key [key2]...

在执行multi之前,先执行watch key1 [key2],可以监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

3.3 unwatch

取消 WATCH 命令对所有 key 的监视。 如果在执行 WATCH 命令之后,EXEC 命令或DISCARD 命令先被执行了的话,那么就不需要再执行UNWATCH 了。

3.4 Redis事务小结

①Watch指令,类似乐观锁,事务提交时,如果Key的值已被别的客户端改变,比如某个list已被别的客户端push/pop过了,整个事务队列都不会被执行。通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化,EXEC命令执行的事务都将被放弃,同时返回Nullmulti-bulk应答以通知调用者事务执行失败 ②事务的三个阶段:   开启:以MULTI开始一个事务   入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到待执行的事务队列里面   执行:由EXEC命令触发事务 ③事务的三个特性:   a、单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。   b、没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题   c、不保证原子性:有时redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。

end
  • 作者:旭仔(联系作者)
  • 发表时间:2022-06-04 18:32
  • 版权声明:自由转载-非商用-非衍生-保持署名
  • 转载声明:如果是转载栈主转载的文章,请附上原文链接
  • 公众号转载:请在文末添加作者公众号二维码(公众号二维码见右边,欢迎关注)
  • 评论