java中使用redis

Java 中使用 redis

 

1、java-redis 客户端

2、常用的 redis 客户端操作:

3、redis 命令行文档: 

4、概念:

  •   Jedis:是 Redis 的 Java 实现客户端,提供了比较全面的 Redis 命令的支持,
  •   Redisson:实现了分布式和可扩展的 Java 数据结构。
  •   Lettuce:高级 Redis 客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。

5、优缺点:

 Jedis:比较全面的提供了 Redis 的操作特性,使用阻塞的 I/O,且其方法调用都是同步的。程序流需要等到 sockets 处理完 I/O 才能执行,不支持异步。Jedis 客户端实例不是线程安全的,所以需要通过连接池来使用 Jedis。

 Redisson:促使使用者对 Redis 的关注分离,提供很多分布式相关操作服务,例如,分布式锁,分布式集合,可通过 Redis 支持延迟队列。基于 Netty 框架的事件驱动的通信层,其方法调用是异步的。Redisson 的 API 是线程安全的,所以可以操作单个 Redisson 连接来完成各种操作。Redisson 对字符串的操作支持比较差。

 Lettuce:要在一些分布式缓存框架上使用比较多。基于 Netty 框架的事件驱动的通信层,其方法调用是异步的。Lettuce 的 API 是线程安全的,所以可以操作单个 Lettuce 连接来完成各种操作

6、 使用建议

结论:lettuce + Redisson

Jedis 和 lettuce 是比较纯粹的 Redis 客户端,几乎没提供什么高级功能。Jedis 的性能比较差,所以如果你不需要使用 Redis 的高级功能的话,优先推荐使用 lettuce。

Redisson 的优势是提供了很多开箱即用的 Redis 高级功能,如果你的应用中需要使用到 Redis 的高级功能,建议使用 Redisson。具体 Redisson 的高级功能可以参考:https://redisson.org/

 

7、下面我们使用 SpringData 提供的 Spring-data-redis 与 SpringBoot 项目实现完美的整合

SpringData 官网:https://docs.spring.io/spring-data/redis/docs/2.4.3/reference/html/#reference

整合:

1、 maven 依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <!--<version>2.1.4.RELEASE</version>-->
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>

 

2、yml:

spring:
  redis:
    # Redis 数据库索引(默认为 0)
    database: 0
    # Redis 服务器地址
    host: 192.168.252.128
    # Redis 服务器连接端口
    port: 6379
    # Redis 服务器连接密码(默认为空)
    password: 123456
    # 连接超时时间(毫秒)默认是 2000ms
    timeout: 5000ms
  # 连接器客户端配置
    lettuce:
      pool:
        # 连接池最大连接数(使用负值表示没有限制)
        max-active: 200
        # 连接池中的最大空闲连接
        max-idle: 20
        # 连接池中的最小空闲连接
        min-idle: 0
        # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1ms
  # 集群模式的 redis 配置
#    cluster:
#      max-redirects: 3
#      nodes: 192.168.0.201:7001,192.168.0.201:7002,192.168.0.201:7003,192.168.0.201:7004,192.168.0.201:7005,192.168.0.201:7006
  # 主从模式的 redis 配置
#    sentinel:
#      #哨兵监听的 master 名称
#      master: mymaster
#      # 哨兵地址列表,多个以, 分割
#      nodes: 192.168.0.201:7001,192.168.0.201:7002
#      password: 123456

 

3、配置 RedisTemplate 的序列化方式,默认使用 jdk 提供的序列化方式,不可读,配置自己的序列化方式 :

@Configuration
@EnableCaching //支持缓存注解
public class MyRedisConfig extends CachingConfigurerSupport {
@Bean
</span><span style="color: rgba(0, 0, 255, 1)">public</span> RedisTemplate&lt;String, Object&gt;<span style="color: rgba(0, 0, 0, 1)"> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {
    RedisTemplate</span>&lt;String, Object&gt; redisTemplate = <span style="color: rgba(0, 0, 255, 1)">new</span> RedisTemplate&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
    redisTemplate.setKeySerializer(</span><span style="color: rgba(0, 0, 255, 1)">new</span> StringRedisSerializer());                    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> key序列化</span>
    redisTemplate.setValueSerializer(<span style="color: rgba(0, 0, 255, 1)">new</span> FastJsonRedisSerializer&lt;&gt;(Object.<span style="color: rgba(0, 0, 255, 1)">class</span>));     <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> value序列化</span>
    redisTemplate.setHashKeySerializer(<span style="color: rgba(0, 0, 255, 1)">new</span> StringRedisSerializer());                <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Hash key序列化</span>
    redisTemplate.setHashValueSerializer(<span style="color: rgba(0, 0, 255, 1)">new</span> StringRedisSerializer()); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Hash value序列化</span>

redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}

}

 4、RedisTemplate 操作 Redis  Api 请参考: https://www.cnblogs.com/dw3306/p/12840012.html 

 

8、Springboot + Spring cache + redis 实现缓存

        Spring Cache 是 Spring3 版本之后引入的一项技术,可以简化对于缓存层的操作,spring cache 与 springcloud stream 类似,都是基于抽象层,可以任意切换其实现。其核心是CacheManagerCache这两个接口,所有由 spring 整合的 cache 都要实现这两个接口、Redis 的实现类则是 RedisCache 和 RedisManager。

 1. 首先认识几个用到的名称及注解

@EnableCaching 开启基于注解的缓存,写在启动类上,这个注解会被 spring 发现,并且会创建一个切面(aspect) 并触发 Spring 缓存注解的切点(pointcut) 。 根据所使用的注解以及缓存的状态, 这个切面会从缓存中获取数据, 将数据添加到缓存之中或者从缓存中移除某个值。
@Cacheable 标注在方法上,如果该方法结果存在缓存则使用缓存,否则执行方法并将结果缓存
@CacheEvict 清除缓存
@CachePut 不管有没有缓存都要执行方法,且把结果缓存,如果存在缓存就更新
@Caching 重新组合多个缓存操作以应用于一个方法。
@CacheConfig 在类级别共享一些常见的缓存相关设置
Cache 缓存接口,定义缓存操作
CacheManager 缓存管理器,管理各种缓存组件
keyGenerator 缓存数据时 key 的生成策略
serialize 缓存数据时,value 的序列化策略

当然,缓存管理器除了 RedisCacheManager 还有一些其他的。例如:

  1. SimpleCacheManager
  2. NoOpCacheManager
  3. ConcurrentMapCacheManager
  4. CompositeCacheManager
  5. EhCacheCacheManager

默认缓存管理器:ConcurrentMapCacheManager,这个简单的缓存管理器使用 java.util.concurrent.ConcurrentHashMap 作为其缓存存储。

@Cacheable 属性讲解

@Cacheable 是一个既可以应用于方法级别,也可用于类级别的注解。自 spring3.1 开始就通过它实现了缓存管理。

@Cacheable能干什么?
为了通俗易懂的理解,举个栗子:一个方法,getBooksByUsernameAndLanguage(String username, int language),显然,是一个获取数据库里所有我的英文书对象的方法,返回应该是一个列表。如果这个函数的返回值很大,而且会在页面上被经常调用,那么每一次调用都要重新连接数据库并返回一个数据量庞大的 list,可能页面响应和资源占用会比较大。而我们希望的是,第一次调用这个方法时,返回的数据能被放到服务器端的缓存里,以便于后面要调用这个方法时,能直接从缓存里取到,这样就不用再查数据库占用资源了。而@Cacheable的作用就是这个。

@Cacheable怎么用?

@Cacheable(value = "CACHE_BOOK",key = "#username", condition = "#language = 1")
public List<Book> getBooksByUsernameAndLanguage(String username, int language) {
     // balabalabala... 里面的代码不重要
     return bookList;
}
  • value : 必须要的。就是个自己取的名字,通过它指明了第一次调用这个方法时返回的 bookList 将被存在内存的哪里。
  • key : 可选。要使用 SpEL 表达式,这里与参数username对应,当传入的 username 值变了的话就不去取缓存里的数据了,而是执行getBooksByUsernameAndLanguage方法。(这是必须的,因为 username 变了,返回值也就变了,缓存里的数据不符合了,因此这个选项很重要)。spring 默认用方法的签名来当做 key。(如果您不熟悉 SpEL,请帮自己一个忙,阅读Spring Expression Language
  • condition:方法返回的结果 bookList,要不要缓存起来?condition 就添加了一个限定条件。这个例子中,只有传入的语言代码是 1,返回的 bookList 才会被缓存起来,如果给 language 传了别的值,那么 bookList 是不会缓存起来的。

 @Cacheable 注解中参数详情见下表:

参数名作用
cacheNames  被缓存的时候的命名空间
key 这里的 key 的优先级是最高的,可以覆盖掉全局配置的 key,如果不配置的话使用的就是全局的 key
keyGenerator  指定的缓存的 key 的生成器,默认没有。请指定KeyGenerator要使用的 bean 实现的名称
cacheManager  指定要使用哪个缓存管理器。默认是底层自动配置的管理器
condition 满足什么条件会进行缓存,里面可以写简单的表达式进行逻辑判断
unless 满足什么条件不进行缓存,里面可以写简单的表达式进行逻辑判断
sync 加入缓存的这个操作是否是同步的
value
指定将方法的返回结果放在哪个缓存中,可以指定多个,用大括号保存
key 与 keyGenerator,cacheManager 与 cacheResolver 参数是互斥的,并且指定两者的操作会导致异常,因为实现 CacheManager 会忽 自定义 CacheResolver。
@CacheEvict 简单属性讲解
@CacheEvict 的参数信息见下表:
参数名描述
allEntries 是否删除该命名空间下面的全部缓存,默认是 false
beforeInvocation 在执行删除方法前就执行清空缓存操作,默认是 false,如果删除方法执行报错该注解则不执行
@CacheConfig 简化注解配置

标注在类上,类中方法就不需要再指定该注解说配置好的属性

在 controller 或者 service 的类上面添加 @CacheConfig ,注解里面的参数详情见下表:

参数名参数值作用
cacheNames 可以随意填写,一般是一个模块或者一个很重要的功能名称 无具体作用,只是用来区分缓存,方便管理
keyGenerator 就是自己配置的 KeyGenerator 的名称 全局 key 都会以他的策略去生成
cacheManager 自己配置的 CacheManager 用来操作 Cache 对象的,很多对于缓存的配置也由他去管理

在标有 @CacheConfig 的类里面编写一个查询单个对象的方法并添加 @Cacheable注解

 eg: 

@Cacheable(key = "#id", unless = "#result == null") 
@PatchMapping("/course/{id}")
public Course courseInfo(@PathVariable Integer id) {
    log.info("进来了 ..");
    return courseService.getCourseInfo(id);
}

 #result 是代表函数的返回值

cache 配置:

在上面的  MyRedisConfig 中添加如下配置:

  /**
     * key 的生成策略的配置: 类名 + 方法名 + 参数列表的类型 + 参数值 再做 哈希散列 作为 key
     *
     * @return
     */
    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        log.info("RedisCacheConfig.keyGenerator()");
        return new KeyGenerator() {
            @Override
            public Object generate(Object o, Method method, Object... objects) {
                StringBuilder sb = new StringBuilder();
                sb.append(o.getClass().getSimpleName())
                        .append(":").append(method.getName())
                        .append(":");
                for (Object obj : objects) {
                    if (null != obj) {// 替换字符串
                        String objKey = JSON.toJSONString(obj);
                        objKey = objKey.replace(":", "=");
                        sb.append(objKey);
                    }
                }
                return sb.toString();}
        };
    }
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">缓存生存时间</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> Duration timeToLive = Duration.ofHours(1<span style="color: rgba(0, 0, 0, 1)">);

@Bean
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">redis缓存配置</span>
    RedisCacheConfiguration config =<span style="color: rgba(0, 0, 0, 1)"> RedisCacheConfiguration.defaultCacheConfig()
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">缓存生存时间</span>
            .entryTtl(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.timeToLive)
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 配置序列化(解决乱码的问题)</span>

.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
new FastJsonRedisSerializer<>(Object.class)))
// 不缓存空值
.disableCachingNullValues();
//根据 redis 缓存配置和 reid 连接工厂生成 redis 缓存管理器
RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build();
log.debug(
"自定义 RedisCacheManager 加载完成");
return redisCacheManager;
}

</span><span style="color: rgba(0, 0, 255, 1)">private</span> RedisSerializer&lt;String&gt;<span style="color: rgba(0, 0, 0, 1)"> keySerializer() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> StringRedisSerializer();
}</span></pre>

启动类上添加: @EnableCaching // 支持缓存注解