Spring+SpringMVC+MyBatis+easyUI整合进阶篇(十四)Redis缓存正确的使用姿势
作者:13
GitHub:https://github.com/ZHENFENG13
版权声明:本文为原创文章,未经允许不得转载。
简介
这是一篇关于Redis使用的总结类型文章,会先简单的谈一下缓存的应用场景、缓存的使用逻辑及注意事项,然后是Redis缓存与数据库间结合以进行系统优化,当然文章的最后也会给出具体的代码实现,不至于看到文章的你一头雾水,理论要讲,项目代码也要分享,这是我写博客的基本出发点。
应用场景
Redis能做什么呢?
这是个好问题,不同的人可能会给出不同的答案,因为它的应用场景真的很多,作为一个优秀的nosql数据库可以结合其他产品做很多事情,比如:tomcat集群的session同步、与nginx和lua结合做限流工具、基于Redis的分布式锁实现、分布式系统唯一主键生成策略、秒杀场景中也会看到它、它还能够作为一个消息队列.....
Redis的应用场景很多很多,以上也只是列举了一部分而已,由于本文是围绕我的开源项目perfect-ssm来写的,所以在本文的场景就是一个缓存中间层,对于读多写少的应用场景,我们经常使用缓存来进行优化以提高系统性能。
我曾经写过一篇《一次线上Mysql数据库崩溃事故的记录》的文章,里面记录了Web请求是如何毫不留情的摧垮mysql数据库,进而导致网站应用无法正常运转。当时的情况就是数据库读请求太多,事故的主要原因也是这个,后续的解决方案也就是在项目中添加缓存层,使得热点数据得以存入缓存,不会重复的去读取mysql,将大部分请求压力转移至Redis缓存中以减轻mysql的负担。
接入缓存后的处理逻辑
请求过来后,首先判断Redis里面有没有,有数据则直接返回Redis中的数据给用户,没有则查询数据库,如果数据库中也没有则返回空或者提醒语句即可。
当然,针对不同的操作,对于Redis和mysql的操作也是不同的:
添加操作
如果是需要放入缓存的数据,那么在向mysql数据库中插入成功后,生成对应的key至,并存入Redis中。
修改操作
向mysql数据库中修改成功后,修改Redis中的数据,但是Redis并没有更新语句,所以只能先删除,再添加完成更新操作。
需要注意的是,考虑到程序对于Redis的操作可能会失败,这时mysql中的数据已经修改,但是Redis中的数据依然是上一次的数据,导致数据不一致的问题,所以是先操作Redis还是先操作mysql需要慎重考虑。
删除操作
与修改操作相同,先删除数据,再更新缓存,但是同样会有出现数据不一致问题的可能性需要注意,如果数据库中的数据删除了,但是Redis中的数据没删除,又会出现业务问题。
查询操作
首先通过Redis查询,如果缓存中已经存在数据则直接返回即可,此时就不再需要通过mysql数据库来获取数据,减少对mysql的请求,如果缓存中不存在数据,则依然通过mysql数据库查询,查询到数据后,存入Redis缓存中。
本项目中的代码是先操作mysql,再操作Redis,有概率会出现上文中提到的数据库与缓存数据不一致的情况,所以需要注意,本文的代码只做参考,用到实际项目中还是需要根据具体的业务逻辑进行合理的修改。
使用缓存的建议
缓存存储策略:
可以缓存的数据的特征基本上是以下几点:
- 热点数据
- 实时性要求不高的数据
- 业务逻辑简单的数据
至于什么数据,不同的系统、不同的项目要求肯定不同,这里不做过多讨论,只简单的说一下自己的想法,结合以上的特征总结如下:
- 1.首页数据、分类数据这些数据属于热点数据,首页数据更是热得发烫,而且这类数据一般实时性不高,不会频繁的去操作,比较适合放入缓存。
- 2.详情数据,比如文章详情、商品详情、广告详情、个人信息详情,这些数据库中单条的的数据可以以其id生成不同的key保存到Redis,操作比较简单明了,在更新或者删除的时候需要同步更新Redis中的数据,这类数据也适合放入缓存中。
- 3.列表数据不是特别推荐,除非是实时性和改变频率真的很低的情况下,因为列表往往牵涉的数据和操作很多,处理起来比较复杂,如果对实时性要求低的话、或者部分字段更新频率低的话,可以换成这部分数据。
缓存存储策略的制定说难也难,说容易也容易,主要是根据具体的业务场景合理的操作即可,以上只是做了一个简单的总结。
缓存失效策略:
失效策略一定要做好,血的教训。
- 定时删除
含义:在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除
优点:保证内存被尽快释放
缺点:
若过期key很多,删除这些key会占用很多的CPU时间,在CPU时间紧张的情况下,CPU不能把所有的时间用来做要紧的事儿,还需要去花时间删除这些key
定时器的创建耗时,若为每一个设置过期时间的key创建一个定时器(将会有大量的定时器产生),性能影响严重
- 惰性删除
含义:key过期的时候不删除,每次从数据库获取key的时候去检查是否过期,若过期则删除,返回null。
优点:删除操作只发生在从数据库取出key的时候发生,而且只删除当前key,所以对CPU时间的占用是比较少的,而且此时的删除是已经到了非做不可的地步(如果此时还不删除的话,我们就会获取到了已经过期的key了)
缺点:若大量的key在超出超时时间后,很久一段时间内,都没有被获取过,那么可能发生内存泄露(无用的垃圾占用了大量的内存)
- 定期删除
含义:每隔一段时间执行一次删除过期key操作
优点:
通过限制删除操作的时长和频率,来减少删除操作对CPU时间的占用--处理"定时删除"的缺点
定期删除过期key--处理"惰性删除"的缺点
缺点
在内存友好方面,不如"定时删除"
在CPU时间友好方面,不如"惰性删除"
难点
合理设置删除操作的执行时长(每次删除执行多长时间)和执行频率(每隔多长时间做一次删除)(这个要根据服务器运行情况来定了)
参考《Redis设计与实现》
缓存操作顺序策略:
在上文中已经讲到了操作顺序的问题,是先操作mysql呢?还是先操作Redis呢?这个需要根据自己的业务逻辑来考量,尽量选择影响较小且结合友好的方案来做。
代码实现:
这里只贴出主要的逻辑代码,想要完整实现的可以到代码仓库去取。
//添加
@Override
public int addArticle(Article article) {
if (articleDao.insertArticle(article) > 0) {
log.info("insert article success,save article to Redis");
RedisUtil.put(Constants.ARTICLE_CACHE_KEY + article.getId(), article);
return 1;
}
return 0;
}
//修改
@Override
public int updateArticle(Article article) {
if (article.getArticleTitle() == null || article.getArticleContent() == null || getTotalArticle(null) > 90 || article.getArticleContent().length() > 50000) {
return 0;
}
if (articleDao.updArticle(article) > 0) {
log.info("update article success,delete article in Redis and save again");
RedisUtil.del(Constants.ARTICLE_CACHE_KEY + article.getId());
RedisUtil.put(Constants.ARTICLE_CACHE_KEY + article.getId(), article);
return 1;
}
return 0;
}
//删除
@Override
public int deleteArticle(String id) {
RedisUtil.del(Constants.ARTICLE_CACHE_KEY + id);
return articleDao.delArticle(id);
}
//查询
@Override
public Article findById(String id) {
log.info("get article by id:" + id);
Article article = (Article) RedisUtil.get(Constants.ARTICLE_CACHE_KEY + id, Article.class);
if (article != null) {
log.info("article in Redis");
return article;
}
Article articleFromMysql = articleDao.getArticleById(id);
if (articleFromMysql != null) {
log.info("get article from mysql and save article to Redis");
RedisUtil.put(Constants.ARTICLE_CACHE_KEY + articleFromMysql.getId(), articleFromMysql);
return articleFromMysql;
}
return null;
}
结语
首发于我的个人博客,新的项目演示地址:perfect-ssm,登录账号:admin,密码:123456
如果有问题或者有一些好的创意,欢迎给我留言,也感谢向我指出项目中存在问题的朋友。
如果你想继续了解该项目可以查看整个系列文章Spring+SpringMVC+MyBatis+easyUI整合系列文章,也可以到我的GitHub仓库或者开源中国代码仓库中查看源码及项目文档。
Spring+SpringMVC+MyBatis+easyUI整合进阶篇(十四)Redis缓存正确的使用姿势的更多相关文章
- Spring+SpringMVC+MyBatis+easyUI整合进阶篇(十五)阶段总结
作者:13 GitHub:https://github.com/ZHENFENG13 版权声明:本文为原创文章,未经允许不得转载. 一 每个阶段在结尾时都会有一个阶段总结,在<SSM整合基础篇& ...
- Spring+SpringMVC+MyBatis+easyUI整合进阶篇(六)一定要RESTful吗?
作者:13 GitHub:https://github.com/ZHENFENG13 版权声明:本文为原创文章,未经允许不得转载. 写在前面的话 这个问题看起来就显得有些萌,或者说类似的问题都有些不靠 ...
- 如约而至,Java 10 正式发布! Spring+SpringMVC+MyBatis+easyUI整合进阶篇(十四)Redis缓存正确的使用姿势 努力的孩子运气不会太差,跌宕的人生定当更加精彩 优先队列详解(转载)
如约而至,Java 10 正式发布! 3 月 20 日,Oracle 宣布 Java 10 正式发布. 官方已提供下载:http://www.oracle.com/technetwork/java ...
- Spring+SpringMVC+MyBatis+easyUI整合进阶篇(十一)redis密码设置、安全设置
警惕 前一篇文章<Spring+SpringMVC+MyBatis+easyUI整合进阶篇(九)Linux下安装redis及redis的常用命令和操作>主要是一个简单的介绍,针对redis ...
- Spring+SpringMVC+MyBatis+easyUI整合优化篇(四)单元测试实例
日常啰嗦 前一篇文章<Spring+SpringMVC+MyBatis+easyUI整合优化篇(三)代码测试>讲了不为和不能两个状态,针对不为,只能自己调整心态了,而对于不能,本文会结合一 ...
- Spring+SpringMVC+MyBatis+easyUI整合进阶篇(十二)Spring集成Redis缓存
作者:13 GitHub:https://github.com/ZHENFENG13 版权声明:本文为原创文章,未经允许不得转载. 整合Redis 本来以为类似的Redis教程和整合代码应该会很多,因 ...
- Spring+SpringMVC+MyBatis+easyUI整合进阶篇(一)设计一套好的RESTful API
写在前面的话 看了一下博客目录,距离上次更新这个系列的博文已经有两个多月,并不是因为不想继续写博客,由于中间这段时间更新了几篇其他系列的文章就暂时停止了,如今已经讲述的差不多,也就继续抽时间更新< ...
- Spring+SpringMVC+MyBatis+easyUI整合进阶篇(二)RESTful API实战笔记(接口设计及Java后端实现)
写在前面的话 原计划这部分代码的更新也是上传到ssm-demo仓库中,因为如下原因并没有这么做: 有些使用了该项目的朋友建议重新创建一个仓库,因为原来仓库中的项目太多,结构多少有些乱糟糟的. 而且这次 ...
- Spring+SpringMVC+MyBatis+easyUI整合进阶篇(九)Linux下安装redis及redis的常用命令和操作
redis简介 Redis是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库. Redis与其他key-value缓存产品有以下三个特点: Redis支持数据的持久化,可以将内存 ...
随机推荐
- java IO(五):字节流、字符流的选择规律
*/ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...
- try{}catch(e){}不能捕获到异常
只能捕获到ReferenceError异常,I don't know why. try{ aa();//这是一个未被定义的方法 }catch(e){ if(e instanceof Reference ...
- 小项目:聊天室 (jQuery,PHP,MySQL)
这几天写了一个小项目,初衷是自己写一个有前端,有后端的东西练练手,所以,实际意义并不大,只是拿来试试手而已. 我对这个聊天室的构想是这样的:先建两个数据库,一个保存用户名和密码,另一个保存用户名和发送 ...
- 将centos_yum源更换为阿里云(官方文档)
http://mirrors.aliyun.com/help/centos?spm=5176.bbsr150321.0.0.d6ykiD 1.备份 mv /etc/yum.repos.d/CentOS ...
- excel中的数据导出为properties和map的方法
在做项目的过程中,经常需要处理excel数据,特别是和业务人员配合时,业务人员喜欢使用excel处理一些数据,然后交给我们技术人员进行程序处理.利用POI读取写入excel数据,是经常使用的一个情景. ...
- springboot(十九):使用Spring Boot Actuator监控应用
微服务的特点决定了功能模块的部署是分布式的,大部分功能模块都是运行在不同的机器上,彼此通过服务调用进行交互,前后台的业务流会经过很多个微服务的处理和传递,出现了异常如何快速定位是哪个环节出现了问题? ...
- html集锦
注意:此内容为复习所总结,非专业,不全,理解记录理解会有偏差. 一.HTML解释: 指的是超文本标记语言 (Hyper Text Markup Language),不是一种编程语言,而是一种标记语言 ...
- MP4大文件虚拟HLS分片技术,避免服务器大量文件碎片
MP4大文件虚拟HLS分片技术,避免点播服务器的文件碎片 本文主要介绍了通过虚拟分片技术,把MP4文件,映射为HLS协议中的一个个小的TS分片文件,实现了在不实际切分MP4文件的情况下,通过HLS协议 ...
- python布尔类型
布尔类型 python当中下面的值在作为布尔表达式时,会被解释器看作False: 1.None: 2.False: 3.任何为0的数字类型,如:0,0.0,0j: 4.任何空序列,如:'',(),[] ...
- c# 颜色RGB到HSB互相转换
/// <summary> /// 色相,饱和度,亮度转换成rgb值 /// </summary> /// <returns></returns> pu ...