缓存分享
本地缓存
- 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架构
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;
}