文档章节

Mybatis源码研究7:缓存的设计和实现

FansUnion
 FansUnion
发布于 2015/05/03 01:27
字数 1155
阅读 10
收藏 0

一、包概述(org.apache.ibatis.cache)

 本包包含了Mybatis框架的缓存接口的定义和实现。
 
 本包只引用了Mybatis的io包的Resources,不依赖于任何第三方库。
 
 Mybatis的其它包大量引用了本包中的类和接口,即严重依赖于本包。
 
 
 二、类和接口概述
 
 缓存框架按照 Key-Value方式存储,Key的生成采取规则为:[hashcode:checksum:mappedStementId:offset:limit:executeSql:queryParams]。
 
 
 Cache接口定义了缓存接口。


 CacheKey定义了缓存的Key。
 PerpetualCache直接实现了Cache接口。
 
 FifoCache,LoggingCache,LruCache,ScheduledCache,SerializedCache,SoftCache,SynchronizedCache,
 TransactionalCache,WeakCache 采用装饰模式实现Cache接口。
 
 采用装饰模式,一个个包装起来,形成一个链,典型的就是SynchronizedCache->LoggingCache->SerializedCache->LruCache->PerpetualCache,通过链起来达到功能增加。
 
 CacheException定义了缓存异常。
 
 三、缓存接口的定义
 

public interface Cache {
 // 缓存实现类的id
 String getId();

 // 缓存的对象的个数
 int getSize();

 // 放入一个缓存对象
 void putObject(Object key, Object value);

 // 获得一个缓存对象
 Object getObject(Object key);

 // 删除一个缓存对象
 Object removeObject(Object key);

 // 清空缓存对象
 void clear();

 // 获取读写锁
 ReadWriteLock getReadWriteLock();

}

 


 四、缓存Key的设计
 
   一般缓存框架的数据结构基本上都是 Key-Value方式存储。 MyBatis对于其Key的生成采取规则为:

[hashcode:checksum:mappedStementId :offset:limit:executeSql:queryParams]。

 

(待深入研究和完善)

  
 五、缓存实现类和包装类
 

实现类:PerpetualCache, 永久缓存,一旦存入就一直保持,内部就是一个HashMap,所有方法基本就是直接调用HashMap的方法。

内部维护一个Map数据结构,private Map<Object, Object> cache = new HashMap<Object, Object>();
 

包装类:

 

FifoCache:先进先出缓存,内部就是一个链表,将链表开头元素(最老)移除。

LoggingCache:日志缓存,添加功能:取缓存时打印命中率。

LruCache:最近最少使用缓存,核心就是覆盖 LinkedHashMap.removeEldestEntry方法,返回true或false告诉 LinkedHashMap要不要删除此最老键值。
LinkedHashMap内部其实就是每次访问或者插入一个元素都会把元素放到链表末尾,这样不经常访问的键值肯定就在链表开头啦。

ScheduledCache:定时调度缓存, 目的是每一小时清空一下缓存。

SerializedCache:序列化缓存,用途是先将对象序列化成2进制,再缓存向缓存中 put或get数据时的序列化及反序列化处理。、

SoftCache:软引用缓存,核心是SoftReference。

SynchronizedCache:同步缓存,防止多线程问题。
核心: 加读写锁,     ReadWriteLock.readLock().lock()/unlock() ,ReadWriteLock.writeLock().lock()/unlock()

对于 Lock机制来说,其分为 Read 和 Write 锁,其 Read 锁允许多个线程同时持有,而 Write 锁,一次能被一个线程持有,如果当 Write 锁没有释放,其它需要 Write的线程只能等待其释放才能去持有。

TransactionalCache:

事务缓存,一次性存入多个缓存,移除多个缓存 。
 
  我们可以看到在TransactionalCache类里也维护着两个HashMap:
  entriesToAddOnCommit和entriesToRemoveOnCommit。
  
  当在TransactionalCacheManager中调用putObject和removeObject方法的时候并不是马上就把对象存放到缓存或者从缓存中删除  ,而是先把这个对象放到这两个HashMap之中的一个里,然后当执行commit方法时再真正地把对象存放到缓存或者从缓存中删除。
  
  现在我们应该可以明白为TransactionalCacheManager和TransactionalCache这两个类要加上事务的前缀了,因为commit方法是一个原子操作,一次会操作多个对象,要么一起成功,要么就一起失败。

 

 WeakCache:弱引用缓存,核心是WeakReference。
 
 六、缓存实现的问题和解决方案(待深入研究和完善)
 

问题:

1.作为缓存中对象的key是它的CacheKey对象。

不得不说这是一个失败的设计,key值的类型是String类型就已经足够了,完全没有必要用对象类型来做key值的类型。

因为内存空间是有限的,要在有限的空间中尽可能地存放更多的内容,就需要key值在保证唯一性的情况下空间占的越小越好。

 

2.myBatis的读写锁有写饥渴问题等,这些问题都会给性能造成影响。
   
    所以还是不建议在生产环境中使用iBatis或者myBatis自带的二级缓存,只使用他们的ORM功能,而二级缓存还是交给Memcached等其它缓存框架来实现吧。


 memcache:http://baike.baidu.com/view/1193094.htm
 
 oscache:http://baike.baidu.com/view/1835163.htm
 
 ehcache:http://baike.baidu.com/view/1866754.htm

© 著作权归作者所有

FansUnion
粉丝 60
博文 858
码字总数 825464
作品 0
丰台
高级程序员
私信 提问

暂无文章

数据库

数据库架构 数据库架构可以分为存储文件系统和程序实例两大块,而程序实例根据不同的功能又可以分为如下小模块。 1550644570798 索引模块 常见的问题有: 为什么要使用索引 什么样的信息能成...

一只小青蛙
今天
4
0
PHP常用经典算法实现

<? //-------------------- // 基本数据结构算法 //-------------------- //二分查找(数组里查找某个元素) function bin_sch($array, $low, $high, $k){ if ( $low <= $high){ $mid = int......

半缘修道半缘君丶
昨天
5
0
GIL 已经被杀死了么?

本文原创并首发于公众号【Python猫】,未经授权,请勿转载。 原文地址:https://mp.weixin.qq.com/s/8KvQemz0SWq2hw-2aBPv2Q 花下猫语: Python 中最广为人诟病的一点,大概就是它的 GIL 了。...

豌豆花下猫
昨天
5
0
git commit message form

commit message一般包括3部分:Header、Body、Footer。 <type>(<scope>):<subject>blank line<body>blank line<footer> header是必需的,body、footer可以省略。 header中type、subject......

ninjaFrog
昨天
5
0
聊聊Elasticsearch的CircuitBreakerService

序 本文主要研究一下Elasticsearch的CircuitBreakerService CircuitBreakerService elasticsearch-7.0.1/server/src/main/java/org/elasticsearch/indices/breaker/CircuitBreakerService.ja......

go4it
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部