乱弹缓存

缓存崩溃

  1. 描述

    由于缓存层承载过多请求,有效的保护了存储层。但是因为缓存层由于某些原因整体不能提供服务,于是所有请求都会重新回源到存储层,存储层因调用量剧增,直接崩溃。

  2. 解决方法
    • 高可用(主备or主从架构)
    • 限流、降级

缓存穿透

  1. 描述

    缓存穿透将导致不存在的数据每次请求都要落到存储层查询,失去了保护存储层的意义。

  2. 解决方法
    • 空对象,缓存有意义的值。
    • 分散Key失效时间,缓存超时时间分散。

缓存热点

  1. 描述

    某些key是热点key,访问频率非常高。甚至服务启动就会访问频率极高。

  2. 解决方法
    • 提前预热。

缓存经验之谈

  1. 设计好的缓存数据模型,能简单模型就简单模型
  2. 能本地缓存的就本地缓存
  3. 尽量设置缓存的超时时间
  4. 尽量分散缓存的超时时间,加随机数,避免缓存穿透
  5. 要做缓存开关,流控,降级等措施,原则:不要因为缓存穿透、崩溃等问题,把整个系统搞崩了
  6. 热点数据尽量提前预热

缓存封装

  1. Cache get组件封装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

/**
* Get value of specify key
*
* @param key K
* @param expireSeconds K expire seconds
* @param cacheLoader Sor loader
* @return Value
*/
public <V> V get(final String key, final int expireSeconds, TypeReference<V> clazz, CacheLoader<V> cacheLoader) {

// remote cache switch control
if (!cacheSwitch.isRemoteCacheEnabled()) {
return cacheLoader.load();
}

// load data from cache
String value = redisOperator.get(key);
if (null != value) {
logger.debug("Hit cache key:{},value:{}", key, value);
return CacheValueParser.parseObject(value, clazz);
}

// processing cache snow-slip
Lock lock = striped.get(key);
try {
lock.lock(); // lock

value = redisOperator.get(key);
if (null != value) {
logger.debug("Hit cache key:{},value:{}", key, value);
return CacheValueParser.parseObject(value, clazz);
}

// load data from SOR
V data = cacheLoader.load();

final String remoteVal;
final int expireSec;
if (null == data) { // processing cache breakdown
remoteVal = JSON.toJSONString(NULL_STRING);
expireSec = 0 == expireSeconds ? 0 : expireSeconds / 2 + 1;
} else {
remoteVal = JSON.toJSONString(data);
expireSec = expireSeconds;
}

// write cache( sync or async is a problem )
asyncCacheExecutor.submit(new Runnable() {
@Override
public void run() {
try {
redisOperator.put(key, remoteVal, expireSec);
} catch (Exception ex) { // log , send warning mail
logger.error("Write cache key:" + key + " fail .", ex);
}
}
});

return data;
} finally {
lock.unlock();
}
}
  1. 客户端调用
1
2
3
4
5
6
7
List<LocalDeviceFeaturesVo> value = simpleCache.get(CacheKeys.CONF_LOCAL_FEATURES, 60 * 60, new TypeReference<List<LocalDeviceFeaturesVo>>() {},
new CacheLoader<List<LocalDeviceFeaturesVo>>() {
@Override
public List<LocalDeviceFeaturesVo> load() {
return fdpDao.getLocalDeviceFeatures();
}
});
支持原创技术分享,您的支持将鼓励我继续创作!