摘要:完善缓存击穿问题问题描述,分布式场景中,有时候存在高并发访问,比如秒杀活动。在高并发访问下请求全部怼到数据库,可能导致数据库挂掉,这就是缓存击穿。缓存击穿解决方案已经能解决日常情况,但还是有一定提升的空间的。
“做人、做程序,变则通,不变只能一直死循环下去” ————尼古斯拉Docker安装官方Redis
参考文章: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> ping PONG 127.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缓存功能核心逻辑代码
@Service public 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; } }
首次请求:
二次请求、多次请求:
Tips:
有时会报redis连接超时connect timed out,把链接超时时间改大一点就好了,建议200以上,一个小bug,不作过多阐述。完善缓存击穿问题
问题描述,分布式场景中,有时候存在高并发访问,比如秒杀活动。或是有黑客每次故意查询一个在缓存内必然不存在的数据,导致每次请求都要去存储层去查询,这样缓存就失去了意义。在高并发访问下请求全部怼到数据库,可能导致数据库挂掉,这就是缓存击穿。
场景如下图所示:
方法一:使用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); Listlist=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); Listlist=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%情况,但还是有一定提升的空间的。加油吧,骚年。
最后,写这样一篇文章真的好费时间,有缘人记得点赞哦,笔芯!
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/27689.html
摘要:分布式锁的作用在单机环境下,有个秒杀商品的活动,在短时间内,服务器压力和流量会陡然上升。分布式集群业务业务场景下,每台服务器是独立存在的。这里就用到了分布式锁这里简单介绍一下,以的事务机制来延生。 Redis 分布式锁的作用 在单机环境下,有个秒杀商品的活动,在短时间内,服务器压力和流量会陡然上升。这个就会存在并发的问题。想要解决并发需要解决以下问题 1、提高系统吞吐率也就是qps 每...
阅读 540·2021-08-31 09:45
阅读 1647·2021-08-11 11:19
阅读 883·2019-08-30 15:55
阅读 821·2019-08-30 10:52
阅读 2845·2019-08-29 13:11
阅读 2924·2019-08-23 17:08
阅读 2832·2019-08-23 15:11
阅读 3066·2019-08-23 14:33