spring boot 1.5.9 整合redis

  1. 云栖社区>
  2. 博客>
  3. 正文

spring boot 1.5.9 整合redis

技术小阿哥 2017-11-27 17:04:00 浏览2817
展开阅读全文

redis概述

Redis可以存储键与5种不同数据结构类型之间的映射,这5种数据结构类型分别为 
STRING(字符串)、LIST(列表)、SET(集合)、HASH(散列)和ZSET(有序集合)。 
有一部分Redis命令对于这5种结构都是通用的,如DEL、TYPE、RENAME等;但也有一部分Redis命令只能对特定的一种或者两种结构使用;

一些数据库和缓存服务器的特性与功能


名称 类型 数据存储选项 查询类型 附加功能
Redis 使用内存存储(in-memory)的非关系数据库 字符串、列表、集合、散列表、有序集合 每种数据类型都有自己的专属命令,另外还有批量操作(bulk operation)和不完全(partial)的事务支持 发布与订阅,主从复制(master/slave replication),持久化,脚本(存储过程,stored procedure)
memcached 使用内存存储的键值缓存 键值之间的映射 创建命令、读取命令、更新命令、删除命令以及其他几个命令 为提升性能而设的多线程服务器
MySQL 关系数据库 每个数据库可以包含多个表,每个表可以包含多个行;可以处理多个表的视图(view);支持空间(spatial)和第三方扩展 SELECT、 INSERT、 UPDATE、 DELETE、函数、存储过程 支持ACID性质(需要使用InnoDB),主从复制和主主复制 (master/master replication)
PostgreSQL 关系数据库 每个数据库可以包含多个表,每个表可以包含多个行;可以处理多个表的视图;支持空间和第三方扩展;支持可定制类型 SELECT、 INSERT、 UPDATE、 DELETE、内置函数、自定义的存储过程 支持ACID性质,主从复制,由第三方支持的多主复制(multi-master replication)
MongoDB 使用硬盘存储(on-disk)的非关系文档存储 每个数据库可以包含多个表,每个表可以包含多个无schema(schema-less)的BSON文档 创建命令、读取命令、更新命令、删除命令、条件查询命令等 支持map-reduce操作,主从复制,分片,空间索引(spatial index)

==表格样式不是我能决定的,凑合看吧==

Redis提供的5种结构


结构类型 结构存储的值 结构的读写能力
STRING 可以是字符串、整数或者浮点数 对整个字符串或者字符串的其中一部分执行操作;对整数和浮点数执行自增(increment)或者自减(decrement)操作
LIST 一个链表,链表上的每个节点都包含了一个字符串 从链表的两端推入或者弹出元素;根据偏移量对链表进行修剪(trim);读取单个或者多个元素;根据值查找或者移除元素
SET 包含字符串的无序收集器(unordered collection),并且被包含的每个字符串都是独一无二、各不相同的 添加、获取、移除单个元素;检查一个元素是否存在于集合中;计算交集、并集、差集;从集合里面随机获取元素
HASH 包含键值对的无序散列表 添加、获取、移除单个键值对;获取所有键值对
ZSET(有序集合) 字符串成员(member)与浮点数分值(score)之间的有序映射,元素的排列顺序由分值的大小决定 添加、获取、删除单个元素;根据分值范围(range)或者成员来获取元素

redis特性


源码简单 ,约23000行C语言源代码。

  1. 速度快 
    Redis是用C语言实现的;Redis的所有数据存储在内存中,用于快速地读写访问。
  2. 持久化 
    Redis的所有数据存储在内存中,对数据的更新将异步地保存到磁盘上; 
    redis的持久化有两种方式:AOF与RDB两种模式
  3. 支持多种数据结构 
    Redis支持五种数据结构:String、List、Set、Hash、Zset
  4. 支持多种编程语言 
    Java、php、Python、Ruby、Lua、Node.js等
  5. 功能丰富 
    除了支持五种数据结构之外,还支持缓存、事务、流水线、发布/订阅、消息队列等功能。 
    发布/订阅模型: Redis支持创建发布和订阅通道,这样Redis客户端可以订阅任意的通道来进行数据消费,并且任何已订阅该通道的客户端可以发布数据。
  6. 主从复制 
    主服务器(master)执行写入(增删改),从(slave)服务器执行查询。复制提供可伸缩性和可用性。任何一个slave宕机,其他的slave还可以提供数据访问。
  7. 虚拟内存 
    Redis使用RAM作为内存式存储。但是,在内存不足的情况下,它使用虚拟内存来保存数据。
  8. 高可用及分布式 
    Redis-Sentinel(v2.8)支持高可用
    Redis-Cluster(v3.0)支持分布式

redis部分命令

flushdb:清空当前数据库; 
select [index]:选择索引数据库,index为索引值名,如:select 1; 
del [key]:删除一条指定key的值; 
keys *:查看数据库内所有的key; 
flushall:清空所有数据库; 
quit:退出客户端连接。 
==友情提示:部分命令慎用,线上一定不能用==

redis安装

redis下载安装

  1. windows版本下载:https://github.com/MicrosoftArchive/redis/releases ,目前是redis-3.2.100版本。 
    image
  2. 解压下载的zip压缩包: 
    image 
    Redis on Windows.docx:关于Redis的描述文档
    redis.windeows.conf:默认的配置文件
    RedisService.docx:安装手册
    redis-server.exe:redis服务启动执行文件
    redis-cli.exe:redis客户端启动执行文件
  3. 注册redis服务到本机系统中 
    在当前redis解压文件目录下...\Redis-x64-3.2.100\,本机系统中注册服务:

redis-server --service-install redis.windows.conf --loglevel verbose 
卸载服务: 
redis-server --service-uninstall 
启动Redis: 
redis-server.exe 
指定配置文件启动,适用于多个redis数据库的时候 
redis-server.exe redis.windows.conf

停止Redis: 
redis-server --service-stop

安装成功: 
image 
启动redis: 
iamge

  1. redis客户端测试
    • 双击redis客户端执行文件:redis-cli.exe
    • 测试:ping 
      image

redis桌面可视化工具

redis Java客户端

redis支持的语言,可在官网查看!重点看下redis的java客户端jedis:

github地址:https://github.com/xetorthio/jedis

spring boot 整合redis

spring boot整合redis有两种方式: 
其一:使用外部配置,通过jedis技术框架实现; 
其二:通过spring boot提供的数据访问框架Spring Data Redis实现,它是基于Jedis的。

第一种方式,可以参考SSM框架整合jedis进行操作配置;重点是第二种实现方式!

spring data redis

通过spring boot中的redis自动配置类,关于redis自动配置类RedisAutoConfiguration.java

//排除redis自动配置注解
@EnableAutoConfiguration(exclude = RedisAutoConfiguration.class)

查看其源码: 
image

spring boot在Spring Data Redis提供了两个模板:

  • RedisTemplate
  • StringRedisTemplate

RedisTemplate会使用JdkSerializationRedisSerializer处理数据,这意味着key和value都会通过Java进行序列化。 
StringRedisTemplate默认会使用StringRedisSerializer处理数据。

要是操作字符串的话,用StringRedisTemplate就可以满足。但要是想要存储一个对象Object,我们就需要使用RedisTemplate,并对key采用String序列化方式,对value采用json序列化方式,这时候就需要对redisTemplate自定义配置,项目源码片段:

/**
     * 实例化 RedisTemplate 对象
     *
     * @return RedisTemplate
     */
    @Bean
    public RedisTemplate<String, Object> functionDomainRedisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        initDomainRedisTemplate(redisTemplate);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * 设置数据存入 redis 的序列化方式
         * </br>redisTemplate 序列化默认使用的jdkSerializeable, 存储二进制字节码, 导致key会出现乱码,所以自定义
         * 序列化类
     *
     * @param redisTemplate
     * @param factory
     */
    private void initDomainRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        // 使用Jackson2JsonRedisSerialize 替换默认序列化
        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);
        //// string结构的数据,设置value的序列化规则和 key的序列化规则
        //StringRedisSerializer解决key中乱码问题。//Long类型不可以会出现异常信息;
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //value乱码问题:Jackson2JsonRedisSerializer
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);

        //设置Hash结构的key和value的序列化方式
        //redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);
        //redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
    }

RedisTempalte类API

方  法 子API接口 描  述
opsForValue() ValueOperations<K, V> 操作具有简单值的条目
opsForList() ListOperations<K, V> 操作具有list值的条目
opsForSet() SetOperations<K, V> 操作具有set值的条目
opsForZSet() ZSetOperations<K, V> 操作具有ZSet值(排序的set)的条目
opsForHash() HashOperations<K, HK, HV> 操作具有hash值的条目
boundValueOps(K) BoundValueOperations<K,V> 以绑定指定key的方式,操作具有简单值的条目
boundListOps(K) BoundListOperations<K,V> 以绑定指定key的方式,操作具有list值的条目
boundSetOps(K) BoundSetOperations<K,V> 以绑定指定key的方式,操作具有set值的条目
boundZSet(K) BoundZSetOperations<K,V> 以绑定指定key的方式,操作具有ZSet值(排序的set)的条目
boundHashOps(K) BoundHashOperations<K,V> 以绑定指定key的方式,操作具有hash值的条目

spring boot缓存管理

spring boot集成redis进行数据缓存功能;有两种实现: 
1,通过在代码中调用redis API实现数据的CRUD; 
【参考RedisUtils工具类,该工具类支持redis的其他业务场景】 
2,通过在方法上添加缓存注解实现; 
【重点介绍,只支持redis作为缓存管理时使用】

Spring 提供了很多缓存管理器,例如:

  • SimpleCacheManager
  • EhCacheCacheManager
  • CaffeineCacheManager
  • GuavaCacheManager
  • CompositeCacheManager
  • RedisCacheManager Spring Data提供的缓存管理器:RedisCacheManager

在Spring Boot中通过@EnableCaching注解自动化配置合适的缓存管理器(CacheManager),默认情况下Spring Boot根据下面的顺序自动检测缓存提供者:

  • Generic
  • JCache (JSR-107)
  • EhCache 2.x
  • Hazelcast
  • Infinispan
  • Redis
  • Guava
  • Simple

因为之前已经配置了RedisTemplate了,Spring Boot就无法自动给RedisCacheManager设置RedisTemplate了,所以要自己配置CacheManager。


1, 修改RedisConfig配置类,添加@EnableCaching注解,并继承CachingConfigurerSupport,重写CacheManager 方法:

/**
 * 实例化 CacheManager 对象,指定使用RedisCacheManager作缓存管理
 *
 * @return CacheManager
 */
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
    RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
    // 设置缓存过期时间(单位:秒),60秒
    rcm.setDefaultExpiration(120);
    return rcm;
}

Spring提供了如下注解来声明缓存规则:

  • @Cacheable triggers cache population
  • @cacheevict triggers cache eviction
  • @cacheput updates the cache without interfering with the method execution
  • @caching regroups multiple cache operations to be applied on a method
  • @cacheconfig shares some common cache-related settings at class-level
注 解 描 述
@Cacheable 表明Spring在调用方法之前,首先应该在缓存中查找方法的返回值。如果这个值能够找到,就会返回缓存的值。否则的话,这个方法就会被调用,返回值会放到缓存之中
@cacheput 表明Spring应该将方法的返回值放到缓存中。在方法的调用前并不会 检查缓存,方法始终都会被调用
@cacheevict 表明Spring应该在缓存中清除一个或多个条目
@caching 这是一个分组的注解,能够同时应用多个其他的缓存注解
@cacheconfig 可以在类层级配置一些共用的缓存配置

@Cacheable和@cacheput有一些共有的属性:

属  性 类  型 描  述
value String[] 要使用的缓存名称
condition String SpEL表达式,如果得到的值是false的话,不会将缓存应用到方法调用上
key String SpEL表达式,用来计算自定义的缓存key
unless String SpEL表达式,如果得到的值是true的话,返回值不会放到缓存之中

2, 通过注解@Cacheable,对数据进行缓存处理:
代码片段:

/**
     * 通过缓存注解,添加数据到redis中
     * </br>实现数据缓存!
     * @param cat 对象
     */
    @Cacheable
    @RequestMapping(value = "/getCat/{catId}", method = RequestMethod.GET)
    @ResponseBody
    public Cat add(@PathVariable("catId") int catId){
        return this.catService.getCat(catId);
    }

注意:Cat对象必须实现implements Serializable接口! 
启动访问:http://127.0.0.1:8066/redis/getCat/1

java.lang.IllegalStateException: No cache could be resolved for 'Builder[public com.wyait.redis.pojo.Cat com.wyait.redis.controller.RedisCacheController.add(int)] caches=[] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'' using resolver 'org.springframework.cache.interceptor.SimpleCacheResolver@5e67a11d'. At least one cache should be provided per cache operation.
    at org.springframework.cache.interceptor.CacheAspectSupport.getCaches(CacheAspectSupport.java:244) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE]

这个错误,是由于@Cacheable注解没有指定缓存名称导致的。加上value值,再试:

@Cacheable(value = "catCache")

测试访问成功!多次访问,走redis缓存。


3, redis缓存key生成策略

键的生成策略有两种,一种是默认策略,一种是自定义策略。

  • ----------------------------默认策略:

    If no params are given, return SimpleKey.EMPTY.
    If only one param is given, return that instance.
    If more the one param is given, return a SimpleKey containing all parameters.

    默认的key是通过KeyGenerator生成的,其默认策略如下: 
    1.如果方法没有参数,则使用0作为key; 
    2.如果只有一个参数的话则使用该参数作为key; 
    3.如果参数多于一个则使用所有参数的hashcode作为key;

  • ----------------------------自定义策略: 
    自定义策略是指我们通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用参数以及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。

之前在redisTemplate里设置了template.setKeySerializer(new StringRedisSerializer()),需要key是string类型。也可以使用SpEL表达式生成Key, 
(SpEL表达式:http://itmyhome.com/spring/expressions.html) 
返回结果需要是string类型(比如#root.methodName就是,#root.method不是String),通用办法是重写keyGenerator定制Key默认生成策略(按照缓存名称+id方式生成key,同时确保更新操作的时候,操作的是同一条数据),也可以在使用缓存注解时指定key:

/**
     * 指定key的生成策略
     * @return KeyGenerator
     */
    @Bean public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override public Object generate(Object target, Method method,
                    Object... params) {
                StringBuilder sb = new StringBuilder();
                String[] value = new String[1];
                // sb.append(target.getClass().getName());
                // sb.append(":" + method.getName());
                Cacheable cacheable = method.getAnnotation(Cacheable.class);
                if (cacheable != null) {
                    value = cacheable.value();
                }
                CachePut cachePut = method.getAnnotation(CachePut.class);
                if (cachePut != null) {
                    value = cachePut.value();
                }
                CacheEvict cacheEvict = method.getAnnotation(CacheEvict.class);
                if (cacheEvict != null) {
                    value = cacheEvict.value();
                }
                sb.append(value[0]);
                //获取参数值
                for (Object obj : params) {
                    sb.append(":" + obj.toString());
                }
                return sb.toString();
            }
        };
    }

注意: 
1,在使用缓存注解时,也可以指定统一规则的key 
(比如:@Cacheable(value = "catCache", key = "#root.caches[0].name + ':' + #id")); 
就可以不走KeyGenerator默认规则;同样可以实现,更新和查询都是同一个key的数据; 
2,使用注解进行数据缓存,指定数据过期时间需要百度普及下!


4, 更新缓存数据

更新与删除Redis缓存需要用到@cacheput和@cacheevict。必须保证keyGenerator生成同一个key,否则更新的不是同一条的数据;

/**
     * 更新redis中的缓存数据
     *</br> #root. 是spEL表达式
     * </br>如果参数是个对象,就通过“#对象.变量”获取到对应的key中需要的值;比如:#cat.id
     * @param id 主键
     */
    @CachePut(value = "catCache", key = "#root.caches[0].name + ':' + #id")
    @RequestMapping(value = "/updateCat", method = RequestMethod.POST)
    @ResponseBody
    public Cat update(@RequestParam int id){
        System.out.println("==========请求参数:"+id);
        return this.catService.updateCat(id);
    }


本文转自 wyait 51CTO博客,原文链接:http://blog.51cto.com/wyait/2048478,如需转载请自行联系原作者

网友评论

登录后评论
0/500
评论
技术小阿哥
+ 关注