缓存初探

本文深入探讨了多种缓存技术,包括Guava缓存的配置与使用、Ehcache的特性及参数设置、分布式缓存Redis与Memcached的区别,以及J2cache的双层缓存策略。还介绍了Redis分布式锁的实现方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

缓存分享


本地缓存

  • guava
private  LoadingCache<String, Optional<List<GMSDSShareOrder>>> shareOrderCahce = CacheBuilder.newBuilder()
            .expireAfterWrite(60, TimeUnit.MINUTES).maximumSize(500).recordStats()
            .refreshAfterWrite(50, TimeUnit.MINUTES)
            .build(new CacheLoader<String, Optional<List<GMSDSShareOrder>>>() {
                @Override
                public Optional<List<GMSDSShareOrder>> load(String key) throws Exception {
                    String[] params = key.split("_");
                    int pageNum = Integer.parseInt(params[1]);
                    int pageSize = Integer.parseInt(params[2]);
                    return Optional.fromNullable(gmdsAwardActivityService.getAllShareOrders(pageNum, pageSize));
                }
    });

maximumSize:是缓存的条目数不是占用内存的大小
expireAfterWrite:当缓存项在指定的时间段内没有更新就会被回收
refreshAfterWrite:当缓存项上一次更新操作之后的多久会被刷新
这样他俩组合使用就可以避免 refreshAfterWrite长时间不更新取到旧值,因为guava没有单独的线程定时清理和加载功能,都是依赖于查询请求。
guava的集合工具类也是比较好用的

  • ehcache 比较少用
    overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
    memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。

分布式缓存

  • redis
    开发人员的默认首选方案,功能多样性,支持二进制格式,六种数据类型,持久化。
  • memcached
    对小型静态数据进行缓存如html页面,本身内存管理机制不像redis那么复杂,处理元数据消耗的内存资源相对更少。
  • wlist、wtable
  • hbase等
    Hbase数据模型
    hbase数据结构
    Hbase架构
    这里写图片描述
    Put流程
    这里写图片描述

J2cache缓存
第一级缓存使用 Ehcache,第二级缓存使用 Redis 。由于大量的缓存读取会导致 L2 的网络成为整个系统的瓶颈,因此 L1 的目标是降低对 L2 的读取次数。该缓存框架主要用于集群环境中。单机也可使用,用于避免应用重启导致的 Ehcache 缓存数据丢失。
这个缓存框架只是给大家扩展一下思路,不一定要这么用,这个框架还有 很多不足的地方。


redis分布式锁
集群环境多个节点设置redis,并发量高时导致从redis读取到脏数据,原理用一个状态值表示锁,对锁的占用和释放通过状态值来标识。 Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系。redis的SETNX命令可以方便的实现分布式锁。

public synchronized boolean lock() throws InterruptedException {
        int timeout = timeoutMsecs;
        while (timeout >= 0) {
            long expires = System.currentTimeMillis() + expireMsecs + 1;
            String expiresStr = String.valueOf(expires); //锁到期时间
            if (this.setNX(lockKey, expiresStr)) {
                // lock acquired
                locked = true;
                return true;
            }

            String currentValueStr = this.get(lockKey); //redis里的时间
            if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
                //判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的
                // lock is expired

                String oldValueStr = this.getSet(lockKey, expiresStr);
                //获取上一个锁到期时间,并设置现在的锁到期时间,
                //只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的
                if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
                    //防止误删(覆盖,因为key是相同的)了他人的锁——这里达不到效果,这里值会被覆盖,但是因为什么相差了很少的时间,所以可以接受

                    //[分布式的情况下]:如过这个时候,多个线程恰好都到了这里,但是只有一个线程的设置值和当前值相同,他才有权利获取锁
                    // lock acquired
                    locked = true;
                    return true;
                }
            }
            timeout -= DEFAULT_ACQUIRY_RESOLUTION_MILLIS;

            /*
                延迟100 毫秒,  这里使用随机时间可能会好一点,可以防止饥饿进程的出现,即,当同时到达多个进程,
                只会有一个进程获得锁,其他的都用同样的频率进行尝试,后面有来了一些进行,也以同样的频率申请锁,这将可能导致前面来的锁得不到满足.
                使用随机的等待时间可以一定程度上保证公平性
             */
            Thread.sleep(DEFAULT_ACQUIRY_RESOLUTION_MILLIS);

        }
        return false;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值