• 利用互斥锁解决缓存击穿问题


    核心思路:相较于原来从缓存中查询不到数据后直接查询数据库而言,现在的方案是 进行查询之后,如果从缓存没有查询到数据,则进行互斥锁的获取,获取互斥锁后,判断是否获得到了锁,如果没有获得到,则休眠,过一会再进行尝试,直到获取到锁为止,才能进行查询

    如果获取到了锁的线程,再去进行查询,查询后将数据写入redis,再释放锁,返回数据,利用互斥锁就能保证只有一个线程去执行操作数据库的逻辑,防止缓存击穿

    操作锁的代码:

    核心思路就是利用redis的setnx方法来表示获取锁,该方法含义是redis中如果没有这个key,则插入成功,返回1,在stringRedisTemplate中返回true, 如果有这个key则插入失败,则返回0,在stringRedisTemplate返回false,我们可以通过true,或者是false,来表示是否有线程成功插入key,成功插入的key的线程我们认为他就是获得到锁的线程。

    1. //获取锁
    2. private boolean tryLock(String key) {
    3. Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
    4. return BooleanUtil.isTrue(flag);
    5. }
    6. //释放锁
    7. private void unlock(String key) {
    8. stringRedisTemplate.delete(key);
    9. }

     操作代码:

    1. public Shop queryWithMutex(Long id) {
    2. String key = CACHE_SHOP_KEY + id;
    3. // 1、从redis中查询商铺缓存
    4. String shopJson = stringRedisTemplate.opsForValue().get("key");
    5. // 2、判断是否存在
    6. if (StrUtil.isNotBlank(shopJson)) {
    7. // 存在,直接返回
    8. return JSONUtil.toBean(shopJson, Shop.class);
    9. }
    10. //判断命中的值是否是空值
    11. if (shopJson != null) {
    12. //返回一个错误信息
    13. return null;
    14. }
    15. // 4.实现缓存重构
    16. //4.1 获取互斥锁
    17. String lockKey = "lock:shop:" + id;
    18. Shop shop = null;
    19. try {
    20. boolean isLock = tryLock(lockKey);
    21. // 4.2 判断否获取成功
    22. if(!isLock){
    23. //4.3 失败,则休眠重试
    24. Thread.sleep(50);
    25. return queryWithMutex(id);
    26. }
    27. //4.4 成功,根据id查询数据库
    28. shop = getById(id);
    29. // 5.不存在,返回错误
    30. if(shop == null){
    31. //将空值写入redis
    32. stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL,TimeUnit.MINUTES);
    33. //返回错误信息
    34. return null;
    35. }
    36. //6.写入redis
    37. stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_NULL_TTL,TimeUnit.MINUTES);
    38. }catch (Exception e){
    39. throw new RuntimeException(e);
    40. }
    41. finally {
    42. //7.释放互斥锁
    43. unlock(lockKey);
    44. }
    45. return shop;
    46. }
  • 相关阅读:
    如何调节电脑屏幕亮度?让你的眼睛更舒适!
    旅游企业该怎么进行网络品牌推广呢?如何宣传和规划旅游商品?
    linux下基于boost/process库实现多进程管理,基于c++开发
    运行时数据区之虚拟机栈
    ERROR 2003 (HY000) Can‘t connect to MySQL server on ‘localhost3306‘ (10061)解决办法
    【面试题】面试官:请你实现一个深拷贝,那如果是正则/set/函数怎么拷贝?
    关于js_事件处理的三种方式
    基于若依框架的药品管理系统
    一文带你深入理解【Java基础】· 枚举类
    JDBC-day05(DAO及相关实现类)
  • 原文地址:https://blog.csdn.net/qq_52183856/article/details/134531366