From 85e592d627b8b5c872843ee3f67e1d0f2299773e Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Mon, 26 May 2025 09:48:23 +0800 Subject: [PATCH] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E5=8F=AF=E4=BB=A5=E5=9C=A8=E9=AB=98=E5=B9=B6?= =?UTF-8?q?=E5=8F=91=E5=9C=BA=E6=99=AF=E4=B8=8B=E9=98=B2=E6=AD=A2=E8=A2=AB?= =?UTF-8?q?=E5=87=BB=E7=A9=BF=E7=9A=84Mybaties=E4=BA=8C=E7=BA=A7=E7=BC=93?= =?UTF-8?q?=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1421727635046400]增加一个可以在高并发场景下防止被击穿的Mybaties二级缓存 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1421727635046400 --- .../cache/NeatLogicConcurrentSafeCache.java | 99 ++++++++++--------- 1 file changed, 53 insertions(+), 46 deletions(-) diff --git a/src/main/java/neatlogic/framework/dao/cache/NeatLogicConcurrentSafeCache.java b/src/main/java/neatlogic/framework/dao/cache/NeatLogicConcurrentSafeCache.java index 8d8308263..448c1ef5a 100644 --- a/src/main/java/neatlogic/framework/dao/cache/NeatLogicConcurrentSafeCache.java +++ b/src/main/java/neatlogic/framework/dao/cache/NeatLogicConcurrentSafeCache.java @@ -20,27 +20,30 @@ import net.sf.ehcache.CacheManager; import net.sf.ehcache.Ehcache; import net.sf.ehcache.Element; import net.sf.ehcache.config.CacheConfiguration; -import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.ibatis.cache.Cache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; /** * 高并发场景下,防止缓存击穿 */ public class NeatLogicConcurrentSafeCache implements Cache { + + private final static Logger logger = LoggerFactory.getLogger(NeatLogicConcurrentSafeCache.class); /** * The cache manager reference. */ protected static CacheManager CACHE_MANAGER = CacheManager.create(); - private final static ConcurrentHashMap LOCAL_LOCK_MAP = new ConcurrentHashMap<>(); + private final static ConcurrentHashMap LOCAL_LOCK_MAP = new ConcurrentHashMap<>(); private static String generateLockKey(String id, Object key) { String tenant = null; @@ -99,18 +102,7 @@ public class NeatLogicConcurrentSafeCache implements Cache { */ @Override public void clear() { - Ehcache cache = getCache(); - List keys = cache.getKeys(); - if (CollectionUtils.isNotEmpty(keys)) { - for (Object key : keys) { - String lockKey = generateLockKey(getId(), key); - Semaphore lock = LOCAL_LOCK_MAP.remove(lockKey); - if (lock != null) { - lock.release(); - } - } - } - cache.removeAll(); + getCache().removeAll(); } /** @@ -126,35 +118,48 @@ public class NeatLogicConcurrentSafeCache implements Cache { */ @Override public Object getObject(Object key) { + Object obj = null; Element cachedElement = getCache().get(key); if (cachedElement != null) { - return cachedElement.getObjectValue(); + obj = cachedElement.getObjectValue(); } - String lockKey = generateLockKey(getId(), key); - Semaphore lock = LOCAL_LOCK_MAP.computeIfAbsent(lockKey, k -> new Semaphore(1)); - boolean flag = false; - try { - flag = lock.tryAcquire(5, TimeUnit.SECONDS); - // 调用putObject()方法时会在LOCAL_LOCK_MAP删除锁,会出现一种场景,这里获得锁,但LOCAL_LOCK_MAP中已经删除了该锁,必须在这里释放锁 - if (flag) { - if (lock != LOCAL_LOCK_MAP.get(lockKey)) { - lock.release(); - } + if (obj == null) { + String lockKey = generateLockKey(getId(), key); + // 1.这里是锁对象放入LOCAL_LOCK_MAP的唯一入口 + // 2.该锁对象会被第一个获取该锁的线程在putObject方法中移除LOCAL_LOCK_MAP + // 3.其他获取该锁的线程只要释放锁就行 + ReentrantLock lock = LOCAL_LOCK_MAP.computeIfAbsent(lockKey, k -> new ReentrantLock()); + boolean flag = false; + try { + flag = lock.tryLock(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + // ignore + } + cachedElement = getCache().get(key); + if (cachedElement != null) { + obj = cachedElement.getObjectValue(); } - } catch (InterruptedException e) { - // ignore - } - cachedElement = getCache().get(key); - if (cachedElement != null) { if (flag) { - if (lock == LOCAL_LOCK_MAP.get(lockKey)) { - LOCAL_LOCK_MAP.remove(lockKey); + if (obj != null) { + if (LOCAL_LOCK_MAP.containsValue(lock)) { + logger.error("NeatLogicConcurrentSafeCache.LOCAL_LOCK_MAP中的锁对象没有被正常移除"); + } + // 获取到锁后,从缓存中得到的结果不为null,不会再查询数据库,也不会调用putObject方法,所以要在这里释放该锁 + lock.unlock(); + } else { + // 获取到锁后,从缓存中得到的结果为null,有以下3种情况: + // 1.该线程是第一个获取到该锁的线程,需要去查询数据库,该线程会在putObject方法中释放该锁。 + // 2.有其他线程调用了clear方法,清空了缓存。 + // 3.该SQL语句执行报错。 + // 对应第2和3种情况,第一个获取到该锁的线程,在调用putObject()方法时会在LOCAL_LOCK_MAP删除锁,会出现一种场景,这里获得锁,但LOCAL_LOCK_MAP中已经删除了该锁,必须在这里释放锁 + ReentrantLock reentrantLock = LOCAL_LOCK_MAP.get(lockKey); + if (reentrantLock != lock) { + lock.unlock(); + } } - lock.release(); } - return cachedElement.getObjectValue(); } - return null; + return obj; } /** @@ -175,24 +180,26 @@ public class NeatLogicConcurrentSafeCache implements Cache { public void putObject(Object key, Object value) { getCache().put(new Element(key, value)); String lockKey = generateLockKey(getId(), key); - Semaphore lock = LOCAL_LOCK_MAP.remove(lockKey); - if (lock != null) { - lock.release(); + ReentrantLock lock = LOCAL_LOCK_MAP.get(lockKey); + if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) { + LOCAL_LOCK_MAP.remove(lockKey, lock); + lock.unlock(); } } /** + * 对应二级缓存,该方法不会被调用 * {@inheritDoc} */ @Override public Object removeObject(Object key) { - String lockKey = generateLockKey(getId(), key); - Semaphore lock = LOCAL_LOCK_MAP.remove(lockKey); - if (lock != null) { - lock.release(); + Object obj = null; + Ehcache ehcache = getCache(); + Element cachedElement = ehcache.get(key); + if (cachedElement != null) { + obj = cachedElement.getObjectValue(); + ehcache.remove(key); } - Object obj = getObject(key); - getCache().remove(key); return obj; } @@ -244,7 +251,7 @@ public class NeatLogicConcurrentSafeCache implements Cache { public static List getAllLockKeyList() { List resultList = new ArrayList<>(); - for (Map.Entry entry : LOCAL_LOCK_MAP.entrySet()) { + for (Map.Entry entry : LOCAL_LOCK_MAP.entrySet()) { resultList.add(entry.getKey()); } return resultList; -- Gitee