From 9f3039776ec6ea59b0723d1f8f565debd6172786 Mon Sep 17 00:00:00 2001
From: zicheng <523186180@qq.com>
Date: Mon, 10 Aug 2020 10:01:55 +0800
Subject: [PATCH] 完善mybatis的二级缓存

---
 src/main/java/com/sunyo/wlpt/message/bus/service/cache/RedisCache.java | 49 ++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 40 insertions(+), 9 deletions(-)

diff --git a/src/main/java/com/sunyo/wlpt/message/bus/service/cache/RedisCache.java b/src/main/java/com/sunyo/wlpt/message/bus/service/cache/RedisCache.java
index b761984..56ea978 100644
--- a/src/main/java/com/sunyo/wlpt/message/bus/service/cache/RedisCache.java
+++ b/src/main/java/com/sunyo/wlpt/message/bus/service/cache/RedisCache.java
@@ -15,6 +15,16 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
 /**
  * @author 子诚
  * Description:自定义Redis作为mybatis二级缓存实现
+ * 问题描述:缓存穿透、缓存雪崩
+ * <p>
+ * 缓存穿透:就是说利用一些列措施,使得访问避开了缓存,直接访问数据库,使得数据库不堪重负引起的问题。比如(压测)访问数据库中不存在的数据
+ * 解决方案:读取数据库,不存在;依旧生成对应的key,放到缓存中,但是对应的value是null(mybatis的二级缓存是这样解决的)。
+ * 下次再次访问的话,就是读取缓存。
+ * <p>
+ * 缓存雪崩:是指在某一特殊时刻,缓存中的缓存全部失效,然后这一时刻又有大量的数据库访问,导致数据库不堪重负。
+ * 解决方案:根据业务的不同设置不同的缓存失效时间。
+ * 比如:这个项目,做了3个namespace的缓存,其中一个namespace,共有5个Mapper指向它。所以选择使用范围内的随机值,来做缓存失效时间
+ * <p>
  * 时间:2020/8/6 9:37
  */
 public class RedisCache implements Cache {
@@ -30,11 +40,6 @@ public class RedisCache implements Cache {
     private static final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
 
     /**
-     * 缓存对象的是失效时间,30分钟
-     */
-    private static final long CACHE_TIME_IN_MINT = 30;
-
-    /**
      * 当前放入缓存的mapper的namespace
      */
     private final String id;
@@ -67,8 +72,34 @@ public class RedisCache implements Cache {
     {
         RedisTemplate redisTemplate = getRedisTemplate();
         // 使用redis的hash类型作为缓存存储模型
-        redisTemplate.opsForHash().put(id.toString(), key.toString(), value);
-        redisTemplate.expire(id.toString(), CACHE_TIME_IN_MINT, TimeUnit.MINUTES);
+        redisTemplate.opsForHash().put(id.toString(), encryptionKey(key.toString()), value);
+
+        /**
+         * 根据业务的不同,设置不同的缓存时间,解决掉缓存雪崩
+         */
+        if (id.equals("com.sunyo.wlpt.message.bus.service.mapper.VirtualHostMapper")) {
+            // 设置缓存时间
+            redisTemplate.expire(id.toString(), random(1, 3), TimeUnit.HOURS);
+        }
+
+        if (id.equals("com.sunyo.wlpt.message.bus.service.mapper.UserMessageBindingMapper")) {
+            // 设置缓存时间
+            redisTemplate.expire(id.toString(), random(60, 100), TimeUnit.MINUTES);
+        }
+
+        if (id.equals("com.sunyo.wlpt.message.bus.service.mapper.UserInfoMapper")) {
+            // 设置缓存时间
+            redisTemplate.expire(id.toString(), random(30, 50), TimeUnit.MINUTES);
+        }
+    }
+
+    /**
+     *
+     */
+    public int random(int low, int high)
+    {
+        int num = ((int) (Math.random() * (high - low))) + low;
+        return num;
     }
 
     /**
@@ -79,7 +110,7 @@ public class RedisCache implements Cache {
     {
         RedisTemplate redisTemplate = getRedisTemplate();
         //根据key 从redis的hash类型中获取数据
-        return redisTemplate.opsForHash().get(id.toString(), key.toString());
+        return redisTemplate.opsForHash().get(id.toString(), encryptionKey(key.toString()));
     }
 
     /**
@@ -133,7 +164,7 @@ public class RedisCache implements Cache {
     /**
      * 封装一个对key进行md5处理方法
      */
-    private String getKeyToMD5(String key)
+    private String encryptionKey(String key)
     {
         return DigestUtils.md5DigestAsHex(key.getBytes());
     }
--
libgit2 0.24.0