缓存使用模式
背景:本文是对团队培训的节选
Cache Aside Pattern
微软的解释 read more
public User get(long uid){
if (uid<0) {//failfast
return null;
}
String key = "u_" + uid;
User ucache= userCache.get(key);
//cache miss
if (ucache == null) {
ucache=mysqlDAO.get(key);
userCache.set(ucache);
}
return ucache;
}
public void save(User user) {
if (user == null) {//failfast
return;
}
String key = "u_" + user.getId();
User user_succ = mysqlDAO.save(user);
userCache.delete(key);
}
/**
* 错误使用方式
* @param user
*/
public void saveWrong(User user) {
if (user == null) {//failfast
return;
}
String key = "u_" + user.getId();
userCache.delete(key);
mysqlDAO.save(user);
}
public void delete(User user) {
if (user == null) {//failfast
return;
}
String key = "u_" + user.getId();
mysqlDAO.delete(user);
userCache.delete(key);
}
/**
* 错误处理方式
* @param user
*/
public void deleteWrong(User user) {
if (user == null) {//failfast
return;
}
String key = "u_" + user.getId();
userCache.delete(key);
mysqlDAO.delete(user);
}
备注:更像是Read Through + Write invalidate 的混合模式
Read/Write Through
Oracle解释:read more
public User get(long uid){
if (uid<0) {//failfast
return null;
}
String key = "u_" + uid;
User ucache= userCacheStorage.get(key);
return ucache;
}
public void set(User user) {
if (user == null) {//failfast
return;
}
String key = "u_" + user.getId();
userCacheStorage.set(user);
}
UserCacheStorage 示例
/**
* 问题?
* @param user
*/
public void set(User user){
System.out.println("put into the cache:"+user.toString());
mysqlDAO.save(user);
userCache.set(user);
}
/**
* 问题?
* @param key
*/
public User get(String key){
System.out.println("put into the cache:"+key);
User ucache=userCache.get(key);
//cache miss
if (ucache == null) {
ucache = mysqlDAO.get(key);
userCache.set(ucache);
}
return ucache;
}
Write Behind
Oracle解释:read more
public void set(User user) {
if (user == null) {//failfast
return;
}
String key = "u_" + user.getId();
userCacheStorage.setAsyn(user);
}
UserCacheStorage
public void setAsyn(User user){
System.out.println("put into the cache:"+user.toString());
userCache.set(user);
try {
userDAO.save(user);
} catch (InterruptedException e) {
}
}
Refresh ahead
- 预测热点数据并自动从数据库刷新缓存,永远不会阻塞读取,适合小型数据集
- 比如我们的优惠券
- 这里不进行code演示
Local Cached
前面四种是Remote cache访问模式
这里介绍本地cache,目的是减少网络带宽,访问更快
private LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
.maximumSize(10)
.expireAfterAccess(5, TimeUnit.SECONDS).recordStats()
.build(
new CacheLoader<String, Integer>() {
public Integer load(String key) throws Exception {
return loadKeyfromMC(key);
}
});
public Integer loadKeyfromMC(String key){
System.out.println("get from mc:"+key);
return 0;
}
public void put(String key, Integer value) {
cache.put(key,value);
}
/**
* get no reload
* @param key
* @return
*/
public Integer get(String key) {
return cache.getIfPresent(key);
}
/**
* get with reload
* @param key
* @return
*/
public Integer getWithReload(String key) {
return cache.getUnchecked(key);
}
/**
* 统计方法
*/
public void stats(){
CacheStats cacheStats=cache.stats();
System.out.println("hit count:"+cacheStats.hitCount());
System.out.println("hit rate:"+cacheStats.hitRate());
System.out.println("miss count:"+cacheStats.missCount());
System.out.println("miss rate:"+cacheStats.missRate());
}
缓存和数据库一致性问题
-
这是一个tradeoff问题
-
分布式环境下,一致性需要更多的成本,分布式事务/补偿
-
优先选择Cache-Aside模式,降低不一致的概率
-
这个模式的问题
-
Read Through + Write invalidate并不能解决Read Through 不一致的问题
- 需要增加CAS + TTL 来辅助解决,如memcached cas
-