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程序稳稳的运行
赏
使用支付宝打赏
使用微信打赏
若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏
扫描二维码,分享此文章