本地缓存

在分布式系统中,分布式缓存如redis,memcache使用比较多。实际上,本地缓存在很多场景下也有使用的必要。
本文主要介绍google的工具包guava中的缓存工具的使用。


本地缓存应用场景:
(1)对性能有非常高的要求
(2)不经常变化或者存在热词
(3)可以接受数据的非实时性

本地缓存可以通过几种方式来实现:ConcurrentHashMap,Guava cache或Ehcached。这里说说Guava cache的使用。
其特性如下:

  • 使用LRU缓存过期机制
  • 并发处理能力:类似于ConcurrentHashMap,是线程安全的,采用了分段锁机制。
  • 更新锁定:在CacheLoader的load方法中加以控制,对同一个key,只让一个请求去读源并设置到缓存中,其他请求阻塞等待(防止缓存击穿)。
  • 集成数据源:get方法从缓存中读取不到时可以从数据源中读取数据并回填到缓存中。
  • 监控缓存加载/命中情况

下面直接看代码:

public class LocalCache {
    public static void main(String[] args) throws Exception {
        LoadingCache<ReqArg, String> loadingCache = CacheBuilder.newBuilder()
                .recordStats().maximumSize(1000).expireAfterWrite(1, TimeUnit.MINUTES)
                .build(new CacheLoader<ReqArg, String>() {
                    @Override
                    public String load(ReqArg key) throws Exception {
                        return key.getKey();
                    }
                });

        Thread checkupThread = new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(1000);
                    System.out.println(loadingCache.stats());
                } catch (InterruptedException e) {
                    break;
                }
            }
        });
        checkupThread.setDaemon(true);
        checkupThread.start();
        
        for (int i = 0; i < 20; i++) {
            String key = String.valueOf(i);
            ReqArg reqArg = new ReqArg(key, key);
            System.out.println(loadingCache.get(reqArg));
        }
        for (int i = 0; i < 10; i++) {
            String key = String.valueOf(i);
            ReqArg reqArg = new ReqArg(key, key);
            System.out.println(loadingCache.get(reqArg));
        }

        Thread.sleep(10 * 60 * 3600);
    }

    static class ReqArg {
        String key;
        String id;

        public ReqArg() {
        }

        public ReqArg(String key, String id) {
            this.key = key;
            this.id = id;
        }

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        @Override
        public int hashCode() {
            return new HashCodeBuilder()
                    .append(key)
                    .append(id)
                    .toHashCode();
        }

        @Override
        public boolean equals(Object obj) {
            ReqArg args = (ReqArg) obj;
            return new EqualsBuilder()
                    .append(this.key, args.key)
                    .append(this.id, args.id)
                    .isEquals();
        }
//        public boolean equals(Object o) {
//            return EqualsBuilder.reflectionEquals(this, o);
//
//        }
//
//        public int hashCode() {
//            return HashCodeBuilder.reflectionHashCode(this);
//        }
    }
}

运行上述代码,输出结果如下:

0
1
...
CacheStats{hitCount=10, missCount=20, loadSuccessCount=20, loadExceptionCount=0, totalLoadTime=2012339, evictionCount=0}
CacheStats{hitCount=10, missCount=20, loadSuccessCount=20, loadExceptionCount=0, totalLoadTime=2012339, evictionCount=0}
...

上面的数字部分是两个for循环输出的结果。下面的缓存命中统计情况是线程checkupThread输出的。其含义如下:
缓存命中10次,未命中20次,从数据源加载数据20次

在第一个for循环中,此时缓存是空的,每次都需要从数据源读取数据(load方法),并加载到缓存中。20次全部未命中。
在第二个for循环中,所有的数据都已经被缓存了,所以直接从缓存中读取。10次全部命中缓存。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值