博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Redis实现广告缓存、并完善缓存击穿
阅读量:6325 次
发布时间:2019-06-22

本文共 4519 字,大约阅读时间需要 15 分钟。

“做人、做程序,变则通,不变只能一直死循环下去” ————尼古斯拉

Docker安装官方Redis

参考文章:

拉取最新版的redis镜像:docker pull redis:latest

启动容器并带密码:

docker run --name redis-test -p 6379:6379 -d --restart=always redis:latest redis-server --appendonly yes --requirepass "your passwd"

查看容器、注意看id: docker ps

查看进程: ps -ef|grep redis

进入容器执行redis客户端:

docker exec -it a126ec987cfe redis-cli -h yourhost -p 6379 -a 'your passwd'127.0.0.1:6379> pingPONG127.0.0.1:6379> info...

能ping通即可正常使用,这个例子比起原先方式,省去了编译、配置、开机启动服务一大堆麻烦。docker就是好,docker就是棒,docker顶呱呱。

广告缓存功能的实现

添加依赖

org.springframework.boot
spring-boot-starter-data-redis
2.1.3.RELEASE

添加配置

# Redis服务器地址spring.redis.host=yourhost# Redis服务器连接端口spring.redis.port=6379# Redis服务器连接密码(默认为空)spring.redis.password=xxxx# Redis数据库索引(默认为0)spring.redis.database=0# 连接池最大连接数(使用负值表示没有限制)spring.redis.jedis.pool.max-active=8# 连接池最大阻塞等待时间(使用负值表示没有限制)spring.redis.jedis.pool.max-wait=-1# 连接池中的最大空闲连接spring.redis.jedis.pool.max-idle=8# 连接池中的最小空闲连接spring.redis.jedis.pool.min-idle=0# 连接超时时间(毫秒)spring.redis.timeout=0

缓存功能核心逻辑代码

@Servicepublic class ContentServiceImpl implements ContentService {    @Autowired    private ContentMapper contentMapper;    @Autowired    private StringRedisTemplate redis;    @Override    public Object contentList(Long cid) throws Exception {        String test= (String) redis.opsForHash().get("ContentList", cid.toString());        if(test==null) {        System.out.println("缓存未命中,执行SQL");        ContentExample example=new ContentExample();        example.createCriteria().andCategoryIdEqualTo(cid);        List
list=contentMapper.selectByExample(example); test=new ObjectMapper().writeValueAsString(list); redis.opsForHash().put("ContentList", cid.toString(), test); return test; } System.out.println("缓存命中,直接使用"); return test; }}

功能测试、调错

首次请求:

clipboard.png

二次请求、多次请求:

clipboard.png

Tips:

有时会报redis连接超时connect timed out,把链接超时时间改大一点就好了,建议200以上,一个小bug,不作过多阐述。

完善缓存击穿问题

问题描述,分布式场景中,有时候存在高并发访问,比如秒杀活动。或是有黑客每次故意查询一个在缓存内必然不存在的数据,导致每次请求都要去存储层去查询,这样缓存就失去了意义。在高并发访问下请求全部怼到数据库,可能导致数据库挂掉,这就是缓存击穿。

场景如下图所示:

clipboard.png

方法一:使用synchronized

public Object contentList(Long cid) throws Exception {        String test= null;        synchronized (redis) {            test=(String) redis.opsForHash().get("ContentList", cid.toString());        }                    if(test==null) {        System.out.println("缓存未命中,执行SQL");        ContentExample example=new ContentExample();        example.createCriteria().andCategoryIdEqualTo(cid);        List
list=contentMapper.selectByExample(example); test=new ObjectMapper().writeValueAsString(list); redis.opsForHash().put("ContentList", cid.toString(), test); return test; } System.out.println("缓存命中,直接使用"); return test;}

方法二:使用互斥锁

@Override        public Object contentList(Long cid) throws Exception {                String test= (String) redis.opsForHash().get("ContentList", cid.toString());                if(test==null) {            System.out.println("缓存未命中,执行SQL");            if(redis.opsForValue().setIfAbsent("key", "value")){                 redis.expire("key", 30000, TimeUnit.MILLISECONDS);                ContentExample example=new ContentExample();                example.createCriteria().andCategoryIdEqualTo(cid);                List
list=contentMapper.selectByExample(example); test=new ObjectMapper().writeValueAsString(list); redis.opsForHash().put("ContentList", cid.toString(), test); redis.delete("key"); return test; } } System.out.println("缓存命中,直接使用"); return test; }

方法二原理就第一个请求进来执行redis.opsForValue().setIfAbsent("key", "value")没有值为true才执行业务逻辑。如果没有执行完业务逻辑、delete("key")。第二个请求就会查到有值为flase,那是进不去的,相当于被锁住了。

使用redis.expire("key", 30000, TimeUnit.MILLISECONDS)为了防止调用setIfAbsent方法之后线程挂掉了,没有执行到delete("key")这一步。这样的话,如果没有给锁设置过期的时间,默认会永不过期。那么这个锁就会一直存在,想查库也查不到了。

评价:这两个解决方案已经能应对日程大部分情况。方案一一存在一定性能损耗,方案二在极端情况下有死锁的可能性,那么还是使用方案二吧。

完善数据同步的问题

问题描述:如果广告内容改变了,即数据库内容已经改变的,但请求还是从原来的缓存里拿数据,这显然是不对的,所以我们更改数据库时要把缓存清一清。

public Object contentSave(Content content) {        Date date =new Date();        content.setCreated(date);        content.setUpdated(date);        contentMapper.insert(content);        redis.delete("ContentList");        return Result.of(200, "添加成功");    }

总结

这个小案例碰到了不少问题,值得一提的是在云上安装redis安装了好几次都不对,最后改用docker才安成了,做程序还是得学会灵活变通呀。缓存击穿解决方案已经能解决日常99.9%情况,但还是有一定提升的空间的。加油吧,骚年。

最后,写这样一篇文章真的好费时间,有缘人记得点赞哦,笔芯!

转载地址:http://jovaa.baihongyu.com/

你可能感兴趣的文章
ajax 设置Access-Control-Allow-Origin实现跨域访问
查看>>
Flask框架
查看>>
对Spring 框架 AOP(面向切面)的理解
查看>>
vue的监听键盘事件的快捷方法
查看>>
git hooks 远程部署
查看>>
Spark 1.0.0 部署Hadoop 2.2.0上
查看>>
Html5 Svg Chapter 3
查看>>
《Windows Internals》(1)
查看>>
《Windows Internal》(2)
查看>>
前端学习之好书推荐
查看>>
KVM/QEMU简介
查看>>
LOJ 2555 「CTSC2018」混合果汁——主席树
查看>>
洛谷4059找爸爸(Code+第一次月赛)
查看>>
Hadoop综合大作业
查看>>
SilverLight商业应用程序开发---学习笔记(2)
查看>>
linux之vim配置
查看>>
docker安装centos后没有ifconfig命令解决办法
查看>>
PAT1105 Spiral Matrix(模拟)
查看>>
Ajax:进度条
查看>>
蚂蚁感冒
查看>>