1.1 简介
REmote DIctionary Server(Redis)
是一个由Salvatore Sanfilippo
写的key-value
存储系统。
Redis
是一个开源的使用ANSI C
语言编写、遵守BSD
协议、支持网络、可基于内存亦可持久化的日志型、Key-Value
数据库,并提供多种语言的API
。
它通常被称为数据结构服务器,因为值(value)
可以是 字符串(String)
, 哈希(Map)
, 列表(list)
, 集合(sets)
和 有序集合(sorted sets)
等类型。
reids
的优点
- 异常快 -
Redis
非常快,每秒可执行大约110000
次的设置(SET)
操作,每秒大约可执行81000
次的读取/获取(GET)
操作。 - 支持丰富的数据类型 -
Redis
支持开发人员常用的大多数数据类型,例如列表,集合,排序集和散列等等。这使得Redis
很容易被用来解决各种问题,因为我们知道哪些问题可以更好使用地哪些数据类型来处理解决。 - 操作具有原子性 - 所有
Redis
操作都是原子操作,这确保如果两个客户端并发访问,Redis
服务器能接收更新的值。 - 多实用工具 -
Redis
是一个多实用工具,可用于多种用例,如:缓存,消息队列(Redis
本地支持发布/订阅),应用程序中的任何短期数据,例如,web
应用程序中的会话,网页命中计数等。
1.2Redis
安装
1.2.1Linux
安装
本课程是在vmvare
虚拟机中来安装的redis (centos 7)
,学习的时候如果有自己的阿里云服务器,也可以在阿里云中来安装redis
,都可以。只要能ping
的通云主机或者虚拟机的ip
,然后在虚拟机或者云主机中放行对应的端口(或者关掉防火墙)即可访问redis
。下面来介绍一下redis
的安装过程:
1.2.1.1 安装gcc
编译
因为后面安装redis
的时候需要编译,所以事先得先安装gcc
编译。阿里云主机已经默认安装了gcc
,如果是自己安装的虚拟机,那么需要先安装一下gcc
:
yum install gcc-c++
1.2.1.2 下载redis
有两种方式下载安装包,一种是去官网上下载(https://redis.io)
,然后将安装包拷到centos
中,另种方法是直接使用wget
来下载:
wget http://download.redis.io/releases/redis-3.2.8.tar.gz
如果没有安装过wget
,可以通过如下命令安装:
yum install wget
1.2.1.3 解压安装
解压安装包:
tar –vzxf redis-3.2.8.tar.gz
然后将解压的文件夹redis-3.2.8
放到/usr/local/
下,一般安装软件都放在/usr/local
下。然后进入/usr/local/redis-3.2.8/
文件夹下,执行make
命令即可完成安装。
【注】如果make
失败,可以尝试如下命令:
make MALLOC=libc
make install
1.2.1.4 修改配置文件
安装成功之后,需要修改一下配置文件,包括允许接入的ip
,允许后台执行,设置密码等等。
打开redis
配置文件:vi redis.conf
在命令模式下输入/bind
来查找bind
配置,按n
来查找下一个,找到配置后,将bind
配置成0.0.0.0
,允许任意服务器来访问redis
,即:
bind0.0.0.0
使用同样的方法,将daemonize
改成yes
(默认为no
),允许redis
在后台执行。
将requirepass
注释打开,并设置密码为123456
(密码自己设置)。
1.2.1.5 启动redis
在redis-3.2.8
目录下,指定刚刚修改好的配置文件redis.conf
来启动redis
:
redis-server./redis.conf
再启动redis
客户端:
redis-cli
由于我们设置了密码,在启动客户端之后,输入auth 123456
即可登录进入客户端。
然后我们来测试一下,往redis
中插入一个数据:
set name CSDN
然后来获取name
get name
如果正常获取到CSDN
,则说明没有问题。
1.2.2Windows
安装
下载地址:https://github.com/MSOpenTech/redis/releases
Redis
支持32
位和64
位。这个需要根据你系统平台的实际情况选择,这里我们下载Redis-x64-xxx.zip
压缩包到C
盘,解压后,将文件夹重新命名为redis
。
打开一个cmd
窗口 使用cd
命令切换目录到C:\redis
运行redis-server.exe redis.windows.conf
如果想方便的话,可以把redis
的路径加到系统的环境变量里,这样就省得再输路径了,后面的那个redis.windows.conf
可以省略,如果省略,会启用默认的。输入之后,会显示如下界面:
1.3 集成redis
Spring Boot
对redis
的支持已经非常完善了,丰富的api
已经足够我们日常的开发。
有两个redis
模板:RedisTemplate
和StringRedisTemplate
。RedisTemplate
提供给我们操作对象,操作对象的时候,我们通常是以json
格式存储,但在存储的时候,会使用Redis
默认的内部序列化器;导致我们存进里面的是乱码之类的东西。当然了,我们可以自己定义序列化。StringRedisTemplate
主要给我们提供字符串操作,我们可以将实体类等转成json
字符串即可,在取出来后,也可以转成相应的对象,所以很有必要导入fastjson
的原因。
1.3.1 添加依赖
- 如果你的
spring boot
的版本号是1.4.0
到1.5.0
之间,添加redis
的jar
包的时候 添加成spring-boot-starter-data-redis
和spring-boot-starter-redis
都是都可以的。 - 但是如果你的
spring boot
的版本号 是1.4.0
以前 也就是1.3.8
版本以前,添加redis
的jar
包 就必须是spring-boot-starter-redis
的jar
包。 - 如果你的
spring boot
的版本号在1.5.0
以后的,添加redis
的jar
包就必须是spring-boot-starter-data-redis
。
1.3.2SpringBoot 1.4
整合redis
1.3.2.1 依赖
采用的是spring-boot-starter-redis
,也可以添加成spring-boot-starter-data-redis
<!--集成redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-redis</artifactId><version>1.4.1.RELEASE</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.3</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency>
1.3.2.2 添加配置
spring.redis.host=127.0.0.1
#Redis服务器连接端口
spring.redis.port=6379
#Redis服务器连接密码(默认为空)
spring.redis.password=
#连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
#连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
#连接池中的最大空闲连接
spring.redis.pool.max-idle=8
#连接池中的最小空闲连接
spring.redis.pool.min-idle=0
#连接超时时间(毫秒)
spring.redis.timeout=30000
1.3.2.3 配置类RedisConfig
@RefreshScope
动态配置刷新的我们要配置一个@RefreshScope
在类上才可以实现对象属性的的动态更新。
我们来总结下@RefreshScope
实现流程
1、需要动态刷新的类标注
@RefreshScope
注解
2、@RefreshScope
注解标注了@Scope
注解,并默认了ScopedProxyMode.TARGET_CLASS;
属性,此属性的功能就是在创建一个代理,在每次调用的时候都用它来调用GenericScope get
方法来获取对象
3、如属性发生变更会调用ContextRefresher refresh() -》RefreshScope refreshAll()
进行缓存清理方法调用,并发送刷新事件通知 -》GenericScope
真正的 清理方法destroy()
实现清理缓存
4、在下一次使用对象的时候,会调用GenericScope get(String name, ObjectFactory<?> objectFactory)
方法创建一个新的对象,并存入缓存中,此时新对象因为Spring
的装配机制就是新的属性了。
@Configuration@EnableCaching@RefreshScopepublicclassRedisConfigextendsCachingConfigurerSupport{@Value("${spring.redis.host}")privateString host;@Value("${spring.redis.port}")privateint port;@Value("${spring.redis.timeout}")privateint timeout;@Value("${spring.redis.password}")privateString password;@Value("${spring.redis.pool.max-active}")privateint maxActive;@Value("${spring.redis.pool.max-wait}")privateint maxWait;@Value("${spring.redis.pool.max-idle}")privateint maxIdle;@Value("${spring.redis.pool.min-idle}")privateint minIdle;@RefreshScope@BeanpublicKeyGeneratorwiselyKeyGenerator(){returnnewKeyGenerator(){@OverridepublicObjectgenerate(Object target,Method method,Object... params){StringBuilder sb=newStringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());for(Object obj: params){
sb.append(obj.toString());}return sb.toString();}};}@RefreshScope@BeanpublicJedisConnectionFactoryredisConnectionFactory(){JedisConnectionFactory factory=newJedisConnectionFactory();
factory.setHostName(host);
factory.setPort(port);
factory.setTimeout(timeout);//设置连接超时时间
factory.setPassword(password);
factory.getPoolConfig().setMaxIdle(maxIdle);
factory.getPoolConfig().setMinIdle(minIdle);
factory.getPoolConfig().setMaxTotal(maxActive);
factory.getPoolConfig().setMaxWaitMillis(maxWait);return factory;}@RefreshScope@BeanpublicCacheManagercacheManager(RedisTemplate redisTemplate){RedisCacheManager cacheManager=newRedisCacheManager(redisTemplate);// Number of seconds before expiration. Defaults to unlimited (0)
cacheManager.setDefaultExpiration(10);//设置key-value超时时间return cacheManager;}@RefreshScope@BeanpublicRedisTemplate<String,String>redisTemplate(RedisConnectionFactory factory){StringRedisTemplate template=newStringRedisTemplate(factory);setSerializer(template);//设置序列化工具,这样ReportBean不需要实现Serializable接口
template.afterPropertiesSet();return template;}@RefreshScopeprivatevoidsetSerializer(StringRedisTemplate template){Jackson2JsonRedisSerializer jackson2JsonRedisSerializer=newJackson2JsonRedisSerializer(Object.class);ObjectMapper om=newObjectMapper();
om.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setValueSerializer(jackson2JsonRedisSerializer);}}
1.3.2.4RedisUtils
类
importjava.io.Serializable;importjava.util.List;importjava.util.Set;importjava.util.concurrent.TimeUnit;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.core.HashOperations;importorg.springframework.data.redis.core.ListOperations;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.data.redis.core.SetOperations;importorg.springframework.data.redis.core.ValueOperations;importorg.springframework.data.redis.core.ZSetOperations;importorg.springframework.stereotype.Service;@ServicepublicclassRedisUtils{@AutowiredprivateRedisTemplate redisTemplate;/**
* 写入缓存
* @param key
* @param value
* @return
*/publicbooleanset(finalString key,Object value){boolean result=false;try{ValueOperations<Serializable,Object> operations= redisTemplate.opsForValue();
operations.set(key, value);
result=true;}catch(Exception e){
e.printStackTrace();}return result;}/**
* 写入缓存设置时效时间
* @param key
* @param value
* @return
*/publicbooleanset(finalString key,Object value,Long expireTime,TimeUnit timeUnit){boolean result=false;try{ValueOperations<Serializable,Object> operations= redisTemplate.opsForValue();
operations.set(key, value);
redisTemplate.expire(key, expireTime, timeUnit);
result=true;}catch(Exception e){
e.printStackTrace();}return result;}/**
* 批量删除对应的value
* @param keys
*/publicvoidremove(finalString... keys){for(String key: keys){remove(key);}}/**
* 批量删除key
* @param pattern
*/publicvoidremovePattern(finalString pattern){Set<Serializable> keys= redisTemplate.keys(pattern);if(keys.size()>0){
redisTemplate.delete(keys);}}/**
* 删除对应的value
* @param key
*/publicvoidremove(finalString key){if(exists(key)){
redisTemplate.delete(key);}}/**
* 判断缓存中是否有对应的value
* @param key
* @return
*/publicbooleanexists(finalString key){return redisTemplate.hasKey(key);}/**
* 读取缓存
* @param key
* @return
*/publicObjectget(finalString key){Object result=null;ValueOperations<Serializable,Object> operations= redisTemplate.opsForValue();
result= operations.get(key);return result;}/**
* 哈希 添加
* @param key
* @param hashKey
* @param value
*/publicvoidhmSet(String key,Object hashKey,Object value){HashOperations<String,Object,Object> hash= redisTemplate.opsForHash();
hash.put(key,hashKey,value);}/**
* 哈希获取数据
* @param key
* @param hashKey
* @return
*/publicObjecthmGet(String key,Object hashKey){HashOperations<String,Object,Object> hash= redisTemplate.opsForHash();return hash.get(key,hashKey);}/**
* 列表添加
* @param k
* @param v
*/publicvoidlPush(String k,Object v){ListOperations<String,Object> list= redisTemplate.opsForList();
list.rightPush(k,v);}/**
* 列表获取
* @param k
* @param l
* @param l1
* @return
*/publicList<Object>lRange(String k,long l,long l1){ListOperations<String,Object> list= redisTemplate.opsForList();return list.range(k,l,l1);}/**
* 集合添加
* @param key
* @param value
*/publicvoidadd(String key,Object value){SetOperations<String,Object> set= redisTemplate.opsForSet();
set.add(key,value);}/**
* 集合获取
* @param key
* @return
*/publicSet<Object>setMembers(String key){SetOperations<String,Object> set= redisTemplate.opsForSet();return set.members(key);}/**
* 有序集合添加
* @param key
* @param value
* @param scoure
*/publicvoidzAdd(String key,Object value,double scoure){ZSetOperations<String,Object> zset= redisTemplate.opsForZSet();
zset.add(key,value,scoure);}/**
* 有序集合获取
* @param key
* @param scoure
* @param scoure1
* @return
*/publicSet<Object>rangeByScore(String key,double scoure,double scoure1){ZSetOperations<String,Object> zset= redisTemplate.opsForZSet();return zset.rangeByScore(key, scoure, scoure1);}
1.3.2.5 测试controller
@RestControllerpublicclassSpringBootController{publicstaticfinalLogger log=LoggerFactory.getLogger(SpringBootController.class);@AutowiredTestService testService;@AutowiredprivateRedisUtils redisUtils;@RequestMapping(value="/hello/{id}")publicStringhello(@PathVariable(value="id")String id){//查询缓存中是否存在boolean hasKey= redisUtils.exists(id);String str="";if(hasKey){//获取缓存Object object= redisUtils.get(id);
log.info("从缓存获取的数据"+ object);
str= object.toString();}else{//从数据库中获取信息
log.info("从数据库中获取数据");
str