业务逻辑直接与数据库进行交互,这样将会严重影响系统的整体性能,因此在业务逻辑与数据库之间引入一层缓存层,当需要向数据库中添加数据实体时,先将数据实体对象放入到缓存,并将实体对象放入到队列中,以进行持久化;需要删除数据实体时,既要删除缓存中的实体对象,又要删除数据库中相应的实体对象;更新实体对象道理相同;查询实体对象时,可以通过三种方式进行查询,实体的主键、实体的唯一索引、实体的普通索引,前两者获取的是一个实体对象,后一个获取的是一个实体对象的集合,包含多个实体对象。
在实现的缓存中,有下面几个需要重点注意的点:
1.需要根据唯一性索引,以及普通索引进行实体对象的查询,因此我们引入了自定义注解,@Unique和@Index
2.为了简化代码逻辑,同时不会影响到具体业务逻辑使用的便利性,缓存层实现时默认将实体对象的名称定义为数据库表中相应的数据表名称。
3.缓存层具体使用的存储结构方面,引入了google团队开发的concurrentLinkedHashMap,用于存储实体对象。这种结构符合LRU(最近最久未使用算法),因此使用这种结构可以提高效率。
4.每一个实体我们相应的定义了一个ReentrantReadWriteLock对象,不要所有实体共用一个锁对象,这样效率问题将会是一个严重的问题。对所有个实体类所对应的数据表对象可以同时进行操作,这样使不会导致一些冲突问题。
5.生产者/消费者线程模型,对于增、删、改操作会不停的往实体队列集合中添加元素,类似于生产者;启动一些新的线程从实体队列集合中获取元素,并将这些元素持久化到数据库,类似于消费者。
6.对于删除实体操作,如果试图去查询一个正在被删除的实体对象,为了保证效率,不让试图查询实体的线程一直等待,而是直接返回相应的信息,可以使用一个集合记录正在被删除的实体的信息,供查询操作使用,从而减少一个不必要的时间开销。
第1点:
查询操作很多时候不仅仅是基于主键进行的,还有可能是基于其他字段进行的查询,引入@Unique和@Index可以使我们基于其他注解进行查询操作。
程序中,可以通过反射机制获取到被这些注解标记的字段的字段名称,对于具体对象,可以基于反射机制获取到该字段所对应的具体值。
第3点:
ConcurrentLinkedHashMap是google团队提供的一个容器,它可以用来实现一个基于URL的缓存,它本身是对ConcurrentHashMap的封装。ConcurrentLinkedHashMap的采用的是Builder(构造器)创建实例,它本身也实现了ConcurrentMap接口。ConcurrentLinkedHashMap中的每个元素会维护一个weight,每增加一个元素weight就累加,直到容量满。
ConcurrentLinkedHashMap VS ConcurrentHashMap:
ConcurrentLinkedHashMap需要维护一个操作队列,操作速度慢于ConcurrentHashMap,性能上不是差很多。由于ConcurrentLinkedHashMap是基于URL的缓存,可以保证比较高的命中率。
实现缓存的几种方式:
本文中选择的是第三种方式构建底层缓存结构。