本文是关于mybatis缓存模块设计的读后感,关于缓存的思考,关于mybatis的缓存源码详细分析在另一篇文章:https://www.cnblogs.com/gmt-hao/p/12448896.html,欢迎大家指教。
一般我们用到缓存的时候,只知道他很快,很强,还能持久,但是为什么他可以做到这些呢,有人会说这是天赋,遗传的,是的,你想的没错,确实是大佬们在构造这些的时候,赋予他这些能力,那今天我们就来剖析一下,大佬们干了啥,区区缓存就能这么厉害。
去大厂面试的时候,面试官总会喜欢问为什么,一开始,完全搞不懂我就去拧个螺丝,你问我造火箭怎么造我咋知道,后来在工作中遇到各种各样的问题,解决不了的时候,看着身边大佬们一层层点进去看源码分析问题的时候,瞬间觉得这多牛逼啊,或许一开始有动力看源码,了解为什么就是因为这个了吧(能装逼)。说回今天的主题-缓存,缓存在百度上的解释就是访问速度比一般随机存取存储器(RAM)快的一种高速存储器,其实对于我们而言就是一个访问速度特别快的数据库,一般而言是一种key-value形式存储的,因为便于查找,无论是类似redis还是我们自己程序中的,其实都是一个道理,他查找快的最大秘诀还是直接在内存中操作,但是由于现在互联网发展太快,大厂的用户量太大了,所以我们已经不能满足于此,那么我们怎么样找到对应的缓存的位置,更加是需要考量的,我们不由得想想,如果是我,那么该如何去设计一个缓存呢,或许能答出来的就是基于内存放到一个map当中读写了吧,那么mybatis作为一个优秀的orm框架,他是怎么设计缓存的呢,没准我们看了之后能够收到一点启发。
mybatis的缓存分为一级和二级,他除了缓存具备的简单读写功能之外,还额外添加了阻塞、清空策略、序列化、日志等功能并让你可以做到任意组合,够豪横吧,那么豪横的能力是怎么做到的呢,我们给他添加功能很简单,但是让我们可以随意组合,好像蛮难的哈,其实我们想一想,在java编程思想当中提到过一个比继承更加推荐的就叫组合,他其实就是不直接继承父类,而是将其引入对象中,这样既可以松耦合而且也能使用父类的功能。知道了这些我们再来看一下mybatis的代码
可以看到这些都是附加的一些功能,我们进去看看他是如何添加功能的。
可以看到,就是我们之前想到的组合,我们将对象传入,并新增一些功能,不就达到了目的吗,而将这个新增之后的作为原始对象,再使用同样的方式去添加新功能,就可以达到任意组合的方式去使用,而这种方式在设计模式中被称为装饰器模式(哈哈,设计模式好像也没那么难)。
说完附加功能和设计模式,我们再看看他的最基本的功能-读写,其实最重要的就是缓存如何命中,可以看到mybatis的缓存命中,最重要的一个点就是cachekey
上图是cachekey的四个重要的参数,重写的equals也是相当的复杂,我们明明几个字符串就可以解决的问题,为啥要设计这么复杂?这不是很消耗资源吗,做这些的目的到底是啥呢,我们相信mybatis搞这么复杂绝不是为了装逼。一般来说,map的key为字符串,而mybatis支持动态sql,因此缓存的key不能仅仅通过String来表示,所以使用cachekey来封装多个影响缓存的因素,而判断两个cachekey是否相同关键是比较两个对象的hash值是否一致。每次更新数据同时也会刷新cachekey的值。我们已经知道mybatis是如何命中缓存的,其基本数据结构还是map,其实在我们自己设计缓存的时候,map也完全足够用了,像redis中双端链表,压缩列表,集合,跳表等数据结构,一般来说除了特殊情况,我们确实是不需要用到,所以对于缓存设计,也需要考虑一些其它的因素。
大家可以看下,mybatis的缓存基础数据结构是hashmap,对于一级缓存来说,由于他的作用域是当前会话,因此不会在多线程之间进行操作,不会有线程安全的问题,但是,二级缓存是跨sqlSession的,多线程并发的情况下,hashMap是肯定无法保证线程安全,所以,mybatis在设计的时候,二级缓存会加上一个synchronized装饰器,其实现就是在方法上面加synchronized关键字,他还有其他的一些装饰器比如LRU,FIFO等处理对象达到上限的清除策略,对于缓存的持久化,也是一个比较重要的话题,mybatis不像redis,他是本地缓存,随程序启动而启动,因此,没有做缓存持久化操作,但是,我们在设计缓存的时候,确实也需要考虑到这一点是否需要。
缓存的设计确实学问很大,本人才疏学浅,只能简单聊聊自己对于这方面的理解,看了mybatis的源码,才知道设计模式为啥这么被推崇,希望以后自己能走得更远吧!!!