Cache Best Practices via Hugh Yang

General Guideline

  • 避免重复造轮子。实现一个sophisticated的缓存并不是容易的事情,尽量使用well-tested, open source的缓存产品
  • Key-value的Map不是真正的缓存,除了用于存放数量有限的immutable数据,并不能代替缓存
  • 避免在open source的缓存外面再加一层包装。如果有包装的需要,必须经过核心开发人员review
  • 避免使用二级缓存。如果有使用二级缓存的需要,必须经过核心开发人员review
  • 数据一致性问题。在使用缓存时,必须考虑到在后端数据源变化的情况下,缓存数据的一致性处理

脏数据处理策略

经常会遇到读取到缓存的脏数据导致线上故障。很多情况下都会产生脏数据,例如数据库数据变更,下游service数据结构变化等。这时在开发时需要考虑全面,如果不够确定可以请核心开发人员帮忙代码review。下面是预防此类问题的常用策略:

  • 改变cache key。这样即使缓存中有脏数据,也不会影响到新代码
  • 版本控制,版本号做为cache key的一部分,避免新旧数据相互影响
  • 上线前刷新缓存,从缓存中清除脏数据
  • 在读取缓存数据时,代码加入适当验证机制,确保读到脏数据时能够graceful degradation

缓存击穿处理策略

后台线程

启动专门的后台线程,负责更新缓存,其它线程只做读取操作。这样无论并发量多大,一个application instance最多只有一个线程会回源,避免了缓存突然失效时瞬间出现大量回源请求。对于有多个application instances的应用,例如mapi目前有400个application instances,最大仍有可能有400个回源请求,这种情况可以加入随机offset,避免所有application instacnes在同一时间回源。示例代码如下:

private class ApiKeyUpdater implements Runnable {
    private final Logger logger = LoggerFactory.getLogger(ApiKeyUpdater.class);

    private Random rand = new Random();
    @Override
    public void run() {
        while (true) {
            try {
                if (stopped) {
                    logger.info("Termination signal received, exit...");
                    break;
                }

                long sleepTime = UPDATE_INTERVAL + rand.nextInt(UPDATE_OFFSET);
                Thread.sleep(sleepTime);

                refreshApiKeyMap();
            }
            catch (InterruptedException e) {
                logger.info("Interruption signal received, exit...");
                break;
            }
            catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        }
    }
}

当然后台线程带来的一个问题是,如果很多地方都有类似的配置需求,不可能所有地方都启动一个后台线程。对于这种情况,最好引入统一的配置中心实现。

Memcached vs Redis

Memcached

Pros

  • 简单,就是一个key-value storage
  • 一般来说,单台instance所能支持的并发量更大

Cons

  • 缺乏数据持久化支持
  • 缺乏replication支持

Redis

Pros

  • 多种数据类型支持
  • 多种集合操作,在需要对集合做intersection,union,complement的时候很方便
  • 数据持久化支持

Cons

  • Redis基于单线程,一般来说单台instance所能支持的并发量不如memcached

EhCache vs Guava Cache

EhCache

Pros

  • 成熟,功能很完善
  • easy to scale,支持JVM缓存,off-heap缓存和分布式缓存。虽然off-heap和分布式缓存需要商业license
  • 很多open source framework,例如Spring,Hibernate都提供了集成支持

Cons

  • 相对来说,有点重

Guava Cache

Pro

  • 简单,缓存功能强于ConcurrentMap

Cons

  • Scalability较差,只是一个local memory缓存

评论已关闭。Comments are turned off for this article.