Spring整合Redis做数据缓存(Windows环境)
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Spring整合Redis做数据缓存(Windows环境),小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含11517字,纯文字阅读大概需要17分钟。
内容图文
当我们一个项目的数据量很大的时候,就需要做一些缓存机制来减轻数据库的压力,提升应用程序的性能,对于java项目来说,最常用的缓存组件有Redis、Ehcache和Memcached。
Ehcache是用java开发的缓存组件,和java结合良好,直接在jvm虚拟机中运行,不需要额外安装什么东西,效率也很高;但是由于和java结合的太紧密了,导致缓存共享麻烦,分布式集群应用不方便,所以比较适合单个部署的应用。
Redis需要额外单独安装,是通过socket访问到缓存服务,效率比Ehcache低,但比数据库要快很多很多,而且处理集群和分布式缓存方便,有成熟的方案,比较适合分布式集群部署的项目;也有很多的应用将Ehcache和Redis结合使用,做成二级缓存。
至于Memcached嘛和Redis很类似,功能方面嘛理论上来说没有Redis强大(但对于我们来说也完全足够了),因此我们这里先不讲,后面如果有时间在写一篇关于Memcached的文章;由于我们后面会涉及到Tomcat的集群部署,所以这里就先讲讲Redis的应用,好为后面的文章打个基础~~下面正式开始!
代码URL:http://git.oschina.net/tian5017/UserDemoRedis
一、Redis环境准备
Redis有中文官方网站,地址为http://www.redis.cn/
Redis没有官方的Windows版本,但是微软开源技术团队(Microsoft Open Tech group)开发和维护着一个 Win64 的版本,下载地址为
https://github.com/MicrosoftArchive/redis/releases
截止文章完成之时,Redis的最新win64版本为3.2.100,我们这里就是使用的此版本;下载安装好之后,打开安装目录,如下
上图红框中标出的redis-cli.exe就是我们用来操作Redis的客户端,在此安装目录下打开命令行窗口,输入redis-cli.exe回车,如下
可以看到已经连接到了Redis,地址是127.0.0.1(本机),默认的端口为6379,至于操作Redis的命令,大家可以自己百度,常用的也就那么几个,很简单,我们来简单演示下
Redis是采用key-value键值对来存储数据的,使用命令 set "haha" "123456",就表示将值(value)"123456"赋给键(key)"haha",再用 get "haha",就可以查看到值,好了关于Redis的别的东西,大家自己去玩,我们继续我们的正题。
二、Spring集成
1、我们都知道,要在java中使用一个组件或者框架什么的,第一步都是加载它的jar包,java中操作Redis的jar包叫做jedis,这里我们还是利用上一篇文章《Spring+Mybatis+SpringMVC整合》所建立的UserDemo(项目连接:https://git.oschina.net/tian5017/UserDemo)
2、利用Maven来加载jedis的jar包,在UserDemo中的pom.xml的dependencies中添加如下代码
<!-- redis相关 --> < dependency > < groupId >redis.clients</groupId><artifactId>jedis</artifactId><version>2.8.1</version></dependency><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId><version>1.7.10.RELEASE</version></dependency>
除了jedis之外,还需要另外一个jar包spring-data-redis,这是让Spring管理Redis用的;我们先来改造我们的代码,试试往Redis里面存点数据,再来查询。
3、在/resources/下新建redis.properties文件
配置项如下
##Redis连接信息配置 #redis地址 cache.redis.host=127.0.0.1 #redis端口号 cache.redis.port=6379 #redis密码 cache.redis.password= #redis使用的数据库(Redis内置18个数据库,编号为0-17,默认使用0) cache.redis.db=0 #redis链接超时时间 cache.redis.timeout=2000 #redis链接池中最大空闲数 cache.redis.maxIdle=5 #redis链接池中最大连接数 cache.redis.maxActive=20 #建立连接最长等待时间 cache.redis.maxWait=1000
4、在/resources/springConfig/下新建applicationContext-redis.xml文件作为Spring集成Redis的配置文件
配置代码如下
<? xml version="1.0" encoding="UTF-8" ?> <!-- 缓存redis相关配置 --> < beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx ="http://www.springframework.org/schema/tx" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd" > < bean id ="jedisPoolConfig" class ="redis.clients.jedis.JedisPoolConfig" > < property name ="maxTotal" value ="${cache.redis.maxActive}" /> < property name ="maxIdle" value ="${cache.redis.maxIdle}" /> < property name ="maxWaitMillis" value ="${cache.redis.maxWait}" /> < property name ="testOnBorrow" value ="${cache.redis.testOnBorrow}" /> </ bean > < bean id ="redisConnectionFactory" class ="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" > < property name ="usePool" value ="true" /> < property name ="hostName" value ="${cache.redis.host}" /> < property name ="port" value ="${cache.redis.port}" /> < property name ="password" value ="${cache.redis.password}" /> < property name ="timeout" value ="${cache.redis.timeout}" /> < property name ="database" value ="${cache.redis.db}" /> < constructor-arg index ="0" ref ="jedisPoolConfig" /> </ bean > < bean id ="stringRedisSerializer" class ="org.springframework.data.redis.serializer.StringRedisSerializer" /> < bean id ="redisCache" class ="org.springframework.data.redis.core.RedisTemplate" > < property name ="connectionFactory" ref ="redisConnectionFactory" /> < property name ="keySerializer" ref ="stringRedisSerializer" /> < property name ="valueSerializer" ref ="stringRedisSerializer" /> < property name ="hashKeySerializer" ref ="stringRedisSerializer" /> < property name ="hashValueSerializer" ref ="stringRedisSerializer" /> </ bean > </ beans >
5、到这里,我们的准备工作已经全部完成,接下来就是在代码中使用redis了,我们修改UserServiceImpl.java
修改思路为,查询数据的时候,先查询Redis缓存,如果查到了就直接返回数据,如果没查到数据,就去数据库中查询,查到了数据先缓存进Redis再返回,代码如下:
package com.user.demo.service.impl; import com.alibaba.fastjson.JSON; import com.user.demo.dao.UserDao; import com.user.demo.entity.User; import com.user.demo.service.IUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import javax.annotation.Resource; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; /** * Service接口实现类 */ @Service public class UserServiceImpl implements IUserService { @Resource private UserDao userDao; @Autowired private RedisTemplate<String, String> redisCache; @Override public List<User> findAll() { List<User> users = new ArrayList<User>(); //先从redis缓存中获取数据,如果缓存中没有,去数据库中查询数据,查到后在写入缓存 Set<String> sets = redisCache.keys("USER*"); if(sets==null || sets.isEmpty()){ users = userDao.findAll(); if(!CollectionUtils.isEmpty(users)){ for(User user : users){ redisCache.opsForValue().set("USER"+user.getUserId(), JSON.toJSONString(user)); } } }else{ Iterator<String> it = sets.iterator(); while (it.hasNext()){ String item = it.next(); String value = redisCache.opsForValue().get(item); users.add(JSON.parseObject(value, User.class)); } } return users; } @Override @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) //事物publicvoid saveUser(User user) { userDao.saveUser(user); } }
6、接下来我们来验证缓存是否生效,编译,运行项目,如下图
打开redis-cli,输入命令KEYS * (查看所有的key),如下
可以看到我们的缓存已经加入进去了,key就是我们在UserServiceImpl.java中设置的(redisCache.opsForValue().set("USER"+user.getUserId(), JSON.toJSONString(user))),以USER开头加上userId构成,为了进一步验证有了缓存之后,查询的数据优先来源于缓存,我们在数据库中删除一条数据,就删除userId为1的那条数据
删除之后,由于我们代码中检测缓存的逻辑只是检测了能否查询到缓存,而没有检测缓存数量是否和数据库数据量一致,所以缓存中还是会存在这条数据
可以看到,userId为1的数据依然存在,说明数据是从Redis缓存中查询出来的。
三、数据同步
上面其实说明了一个问题,就是数据同步,我们很可能会出现,数据库中数据变了,但是缓存中的数据还没有变,因此和数据库中的数据不一致,所以我们需要一些策略来保证数据库的数据和Redis的数据能够保持一致,至于选用什么策略,那要具体项目具体分析,如果对数据的实时性要求很高的项目,那么就要在查询的时候,检测数据库的数据和Redis的缓存数据是否一致,如果不一致就要刷新Redis的数据,但是这样必然对性能会有很高的要求;如果项目对数据的实时性要求没有那么高,我们完全可以做一个定时任务,比如每隔10分钟或者半小时去数据库拉一次数据,再刷新到Redis缓存中,所以下面我们就来做一个定时任务,每隔10分钟去拉一次数据,然后往Redis中刷新一次。
四、定时刷新缓存
我们用Spring中的InitializingBean和DisposableBean接口(这两个接口的具体用法请自行百度,大概就是在SpringBean的生命周期中,影响bean的行为,我们这里就是影响了bean,让它去刷新缓存)来实现刷新缓存,在com.user.demo下新建包cache,在cache下面新建类UserCache.java,具体代码如下
package com.user.demo.cache; import com.alibaba.fastjson.JSON; import com.user.demo.dao.UserDao; import com.user.demo.entity.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import javax.annotation.Resource; import java.util.List; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * Redis缓存User数据并定时刷新 * 每间隔一定时间后(如10分钟)刷新一次缓存,刷新时另起一个线程,不影响执行主任务的线程 */ @Component public class UserCache implements InitializingBean, DisposableBean { privatefinal Logger log = LoggerFactory.getLogger(UserCache.class); //获取电脑的CPU核心数量,设置线程池大小为核心数的2倍(比如我的电脑为4核心,那么这里的值就为8)privatestaticfinalint CORE_NUM = Runtime.getRuntime().availableProcessors() * 2; //初始化值为redis.properties中配置的cache.redis.cacheExpire的值,表示每隔多长时间后执行任务 @Value("${cache.redis.cacheExpire}") privatelong cacheExpire; //执行定时任务的类private ScheduledThreadPoolExecutor executor = null; @Resource private RedisTemplate<String, String> redisCache; @Resource private UserDao userDao; @Override publicvoid destroy() throws Exception { executor.shutdownNow(); } @Override publicvoid afterPropertiesSet() throws Exception { executor = new ScheduledThreadPoolExecutor(CORE_NUM); RefreshCache refreshCache = new RefreshCache(); refreshCache.run(); executor.scheduleWithFixedDelay(refreshCache, cacheExpire, cacheExpire, TimeUnit.SECONDS); } //内部类,开启新线程执行缓存刷新privateclass RefreshCache implements Runnable { @Override publicvoid run() { log.info("---开始刷新用户信息缓存---"); List<User> userList = userDao.findAll(); if(!CollectionUtils.isEmpty(userList)){ for(User user : userList){ redisCache.opsForValue().set("USER" + user.getUserId(), JSON.toJSONString(user)); } } } } }
在redis.properties中加入cache.redis.cacheExpire配置项,代码如下
##Redis连接信息配置 #redis地址 cache.redis.host=127.0.0.1 #redis端口号 cache.redis.port=6379 #redis密码 cache.redis.password= #redis使用的数据库(Redis内置18个数据库,编号为0-17,默认使用0) cache.redis.db=0 #redis链接超时时间 cache.redis.timeout=2000 #redis链接池中最大空闲数 cache.redis.maxIdle=5 #redis链接池中最大连接数 cache.redis.maxActive=20 #建立连接最长等待时间 cache.redis.maxWait=1000 #定时任务执行时间间隔,单位为毫秒,这里相当于10分钟 cache.redis.cacheExpire=600
同时修改UserServiceImpl.java中的代码如下
package com.user.demo.service.impl; import com.alibaba.fastjson.JSON; import com.user.demo.dao.UserDao; import com.user.demo.entity.User; import com.user.demo.service.IUserService; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import javax.annotation.Resource; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; /** * Service接口实现类 */ @Service public class UserServiceImpl implements IUserService { @Resource private UserDao userDao; @Resource private RedisTemplate<String, String> redisCache; @Override public List<User> findAll() { List<User> result = new ArrayList<User>(); Set<String> sets = redisCache.keys("USER*"); //如果缓存有数据则从缓存中取数据,如果没有则从数据库中取数据if(!CollectionUtils.isEmpty(sets)){ Iterator<String> it = sets.iterator(); while(it.hasNext()){ String item = it.next(); String value = redisCache.opsForValue().get(item); result.add(JSON.parseObject(value, User.class)); } }else{ result = userDao.findAll(); } return result; } @Override @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) //事物publicvoid saveUser(User user) { userDao.saveUser(user); } }
此时,项目已经可以定时(每隔十分钟)刷新缓存,我们编译启动
我们新提交一个用户“三德子”,刚提交后,刷新是查不出来数据的,但是数据库是有数据的
然后等待10分钟之后,再来查看,可以看到缓存已经被自动刷新到了Redis中
OK,到这里,这篇文章就结束了,关于Spring集成Redis做数据缓存我们也讲的差不多了,其实关于Redis的应用,远远不止这么简单,我们可以很容易的搭建Redis集群,做分布式数据管理,也可以实现分布式session共享(这个我后面会有一篇文章讲到),甚至假如写数据量很大,我们也可以先缓存进Redis中,再利用多线程来将数据写入数据库中,减轻数据库的负担(因为数据库写操作是很耗费资源的)等等~~那如果大家有什么意见和建议,也欢迎留言交流;下一篇文章我会讲讲mysql的读写分离,欢迎继续关注!!
代码URL:http://git.oschina.net/tian5017/UserDemoRedis
原文:http://www.cnblogs.com/tian5017/p/7171744.html
内容总结
以上是互联网集市为您收集整理的Spring整合Redis做数据缓存(Windows环境)全部内容,希望文章能够帮你解决Spring整合Redis做数据缓存(Windows环境)所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。