记一次缓存穿透问题

1. 场景描述

公司产品的一次抽奖活动,在压测过程中把数开始了一俩轮没有问题,就在测第三轮的时候响应速度异常的慢数据库的链接也满了(当时是单机测试),当时感觉很奇怪明明加了缓存(用的是memcached)为什么会干到数据库中了当时首先想到的是memcached挂了经过排查不是,难道时缓存穿透了?有事一顿测试排查,果然,有个key的时间恰好在并发进来的时候过期了导致大量的请求干到了数据里。既然找到问题了那么接下来就是写码的时候了。

2.代码

缓存穿透前的代码

1.  public boolean drawReward(String goodsId) {
2.      CacheClient client = CacheFactory.getInstance();
3.      String key = String.format(CacheConstants.KEY_GOODS_ENTITY,goodsId);
4.      String Cache = client.get(key);
5.      List<Goods> goodsList = null;
6.      if (Cache == null || Cache.trim().equals("")) {
7.          goodsList = baseMapper.getGoodsList(goodsId);
10          client.set(key, goodsList,CacheConstants.GOODS_TLL);
13.     } else {
14.         goodsList = (List<Goods>) DataHelper.readList(Cache, Goods.class);
15.     }
16.     return goodsList;
17. }

诈一下看这段缓存使用的并没有什么问题,但是在高并发下这段缓存存在着致命的危机,致命位置在于代码第7行dao层的数据读取当并发进来的时候GOODS_TLL有效期刚刚截止,或者后台更新了物品数据(无良产品欺诈用户不可取)那么第6行代码不是线程安全的无法并发处理那么需要怎么加锁呢?

改进

1.  public boolean drawReward(String goodsId) {
2.      CacheClient client = CacheFactory.getInstance();
3.      String key = String.format(CacheConstants.KEY_GOODS_ENTITY,goodsId);
4.      String Cache = client.get(key);
5.      List<Goods> goodsList = null;
6.      if (Cache == null || Cache.trim().equals("")) {
7.          //保险机制
8.          synchronized (this) {
9.              Cache = client.get(key);
10.             if (!StringUtils.isEmpty(Cache)) {
11.                 goodsList = (List<Goods>) DataHelper.readList(Cache, Goods.class);
12                  return goodsList;
13              }
14.             goodsList=baseMapper.getGoodsList(goodsId);
15.             client.set(key, DataHelper.writeList(goodsList),CacheConstants.GOODS_TLL);
16          }
17.     } else {
18.         goodsList = (List<Goods>) DataHelper.readList(Cache, Goods.class);
19.     }
20.     return goodsList;
21. }

改进后的数据读取加上了同步锁保证了每次数据都能击中缓存,经过测试TPS达到8000程序稳稳的运行

猜你喜欢

转载自blog.csdn.net/BeiShangBuZaiLai/article/details/81567989