干翻Mybatis源码系列之第七篇:Mybatis提供的集成缓存方案

简介: 干翻Mybatis源码系列之第七篇:Mybatis提供的集成缓存方案


第一章:Mybatis Orm的缓存

Mybatis定义了一个对象缓存,是Mybatis对缓存的封装,为了屏蔽实现的差异,这被定义成了一个接口Interface,这样的话,Mybatis的缓存基本上是存储于JVM内存中的。

一:Cache源码

public interface Cache {
  String getId(); //每一个MapStatement都会有一个Cache,需要有一个编号。     
  void putObject(Object key, Object value); // 向缓存中添加
  Object getObject(Object key); // 从缓存中获取数据
  Object removeObject(Object key);//从缓存中删除数据
  void clear();//清空
  int getSize();//容量
  ReadWriteLock getReadWriteLock();//读写锁是解决并发解决的。
}

Cache整个的存储结构,很类似于Java中Map。他使用的是JVM的内存。类似于键值对的这种结构。

第二章:自写Cache实现类

一:Map做容器

实现方式1:存多个数据,并且存键值对的数据。基本上容器就是Map了。

public class MyMybatisCache implements Cache {
    private Map<Object,Object> internalCache  = new HashMap();
    @Override
    public String getId() {
        return getClass().getName() ;
    }
    @Override
    public void putObject(Object key, Object value) {
          internalCache.put(key,value);
    }
    @Override
    public Object getObject(Object key) {
        return internalCache.get(key);
    }
    @Override
    public Object removeObject(Object key) {
        return internalCache.remove(key);
    }
    @Override
    public void clear() {
        internalCache.clear();
    }
    @Override
    public int getSize() {
        return internalCache.size();
    }
    @Override
    public ReadWriteLock getReadWriteLock() {
        return new ReentrantReadWriteLock();
    }
}

二:Jedis做容器

实现方式2:存多个数据,并且存键值对的数据。这里使用Redis也是可以的。

public class MyMybatisCache2 implements Cache {
    @Override
    public String getId() {
        return null;
    }
    @Override
    public void putObject(Object key, Object value) {
        // redis java client  ---> jedis
         //Jedis jedis = JedisUtils.openJedis();
        //key ---> json  value--->json
        //jedis.set(String,String);
        //key --- byte[] value --- byte[]
        //jedis.set(byte[],byte[]);
        byte[] k = SerializationUtils.serialize((Serializable) key);
        byte[] v = SerializationUtils.serialize((Serializable) value);
        //jedis.set(k, v);
    }
    @Override
    public Object getObject(Object key) {
        if (key != null) {
            byte[] k = SerializationUtils.serialize((Serializable) key);
            //Jedis jedis = JedisUtils.openJedis();
           // byte[] bytes = jedis.get(k);
//            if(bytes !=null){
//                Object value = SerializationUtils.deserialize(bytes);
//                return value;
//            }
        }
        return null;
    }
    @Override
    public Object removeObject(Object key) {
        return null;
    }
    @Override
    public void clear() {
    }
    @Override
    public int getSize() {
        return 0;
    }
    @Override
    public ReadWriteLock getReadWriteLock() {
        return null;
    }
}

org.apache.commons下的commons-lang3这个包是阿帕奇对于Java.lang包下的功能的增强(String、toString、equals、hashCode、Serializable…)

第三章:Mybatis的Cache实现类

以下是Mybatis的Cache实现类的截图:

Mybatis对于Cache的实现类大致位于两个包:org.apache.ibatis.cache.decorators,org.apache.ibatis.cache.impl

核心的Cache实现位于Impl包下:PerpetualCache,剩下的都是基于装饰器模式,(装饰器的核心目的是为了他的目标增加功能)所以这些装饰器都不是核心,这里的核心就是PerpetualCache。

一:核心PerpetualCache

public class PerpetualCache implements Cache {
  private final String id;
  private Map<Object, Object> cache = new HashMap<Object, Object>();
  public PerpetualCache(String id) {
    this.id = id;
  }
  @Override
  public String getId() {
    return id;
  }
  @Override
  public int getSize() {
    return cache.size();
  }
  @Override
  public void putObject(Object key, Object value) {
    cache.put(key, value);
  }
  @Override
  public Object getObject(Object key) {
    return cache.get(key);
  }
  @Override
  public Object removeObject(Object key) {
    return cache.remove(key);
  }
  @Override
  public void clear() {
    cache.clear();
  }
  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }
  @Override
  public boolean equals(Object o) {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    if (this == o) {
      return true;
    }
    if (!(o instanceof Cache)) {
      return false;
    }
    Cache otherCache = (Cache) o;
    return getId().equals(otherCache.getId());
  }
  @Override
  public int hashCode() {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    return getId().hashCode();
  }
}

底层使用HashMap来完成的,这个已经可以完成一个Cache基本的存储功能了。再次基础上想让Cache功能更强大,我们才用了装饰器的这样的Cache。

二:装饰器Cache

核心目标:为了让PerpetualCache的功能更加强大。

1:FifoCache和LruCache

增强了换出功能,换出功能最为核心的两种算法就是:Fifo先入先出,Lru最小使用。

Lru是默认的装饰器。

public class FifoCache implements Cache {
  private final Cache delegate;
  private final Deque<Object> keyList;
  private int size;
  public FifoCache(Cache delegate) {
    this.delegate = delegate;
    this.keyList = new LinkedList<Object>();
    this.size = 1024;
  }
  @Override
  public String getId() {
    return delegate.getId();
  }
  @Override
  public int getSize() {
    return delegate.getSize();
  }
  public void setSize(int size) {
    this.size = size;
  }
  @Override
  public void putObject(Object key, Object value) {
    cycleKeyList(key);
    delegate.putObject(key, value);
  }
  @Override
  public Object getObject(Object key) {
    return delegate.getObject(key);
  }
  @Override
  public Object removeObject(Object key) {
    return delegate.removeObject(key);
  }
  @Override
  public void clear() {
    delegate.clear();
    keyList.clear();
  }
  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }
  private void cycleKeyList(Object key) {
    keyList.addLast(key);
    if (keyList.size() > size) {
      Object oldestKey = keyList.removeFirst();
      delegate.removeObject(oldestKey);
    }
  }
}
public class LruCache implements Cache {
  private final Cache delegate;
  private Map<Object, Object> keyMap;
  private Object eldestKey;
  public LruCache(Cache delegate) {
    this.delegate = delegate;
    setSize(1024);
  }
  @Override
  public String getId() {
    return delegate.getId();
  }
  @Override
  public int getSize() {
    return delegate.getSize();
  }
  public void setSize(final int size) {
    keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
      private static final long serialVersionUID = 4267176411845948333L;
      @Override
      protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
        boolean tooBig = size() > size;
        if (tooBig) {
          eldestKey = eldest.getKey();
        }
        return tooBig;
      }
    };
  }
  @Override
  public void putObject(Object key, Object value) {
    delegate.putObject(key, value);
    cycleKeyList(key);
  }
  @Override
  public Object getObject(Object key) {
    keyMap.get(key); //touch
    return delegate.getObject(key);
  }
  @Override
  public Object removeObject(Object key) {
    return delegate.removeObject(key);
  }
  @Override
  public void clear() {
    delegate.clear();
    keyMap.clear();
  }
  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }
  private void cycleKeyList(Object key) {
    keyMap.put(key, key);
    if (eldestKey != null) {
      delegate.removeObject(eldestKey);
      eldestKey = null;
    }
  }
}

如何体现装饰器设计模式的:

@Test
    public void test2() {
      //原始核心Cache只有基本功能
        PerpetualCache perpetualCache = new PerpetualCache("sunshuai");
        //套一层之后,引入了换出功能
        LruCache lruCache = new LruCache(perpetualCache);
        //再次套一层之后,引入了日志功能。
        LoggingCache loggingCache = new LoggingCache(lruCache);
    }

本质上就是一个套娃的过程,所有的装饰器和他的目标继承都是这个吊样。

2:LoggingCache

简单说明:增加日志打印功能

3:BlockingCache

简单说明:增加同一时间只会有一个线程去缓存中找某个key的值。

4:ScheduleCache

简单说明:定期刷新缓存,一段时间之后就自动清空缓存,这里边可以设置一个时间间隔,大于时间间隔之后就直接清空缓存。尽量解决脏数据的问题。

5:SerializableCache

简单说明:自动完成key,value的序列化和反序列化的过程。

6:TransactionalCache

简单说明:加上这个装饰器之后,只有在事务操作成功时,才会对应数据放到缓存中。

7:SoftCache和WeakCache

简单说明:这两个基本上就是纯概念性的东西,根部用不到

弱引用:xxxxxx

软引用:Tomcat在设计HttpSession的时候使用的是软引用。

设计模式的区分:装饰器模式和代理设计模式:

装饰器设计模式:

Mybatis当中大量增加了装饰器设计模式,目标是为了核心目标拓展功能,使用套娃的方式,一层一层的增加拓展工功能。

代理设计模式:

为目标原始目标增加额外功能。

如何去区分和权衡呢?

装饰器和代理设计模式,他们的原始功能是不一样的,装饰器设计模式拓展的功能是他的本职工作,比方说Mybatis当中的装饰器模式,增强的是就是他的本质核心原始功能;而代理设计模式增加的是额外功能,跟他们本质原始功能是有根本区别的,比如说是对于Service增加了事务的功能,事务和我们原始Service的功能是不一致的,这是二者最本质的区别。

装饰器设计模式能套娃,代理设计模式不行,这样的描述对么?

一般代理很少套娃,但是不代表他不能套娃。代理也是可以套娃的,JDK的动态代理就可以实现。这里可以补充一下代码。

代理能做到无中生有

当你给我一个接口的时候,我只能通过动态代理这种方式帮你自动的创建接口的实现类,这件事装饰器做不了。最典型的就是SqlSesission中的getMapper();

GOF4人帮只提供了23中设计模式,除了这几种之后,还有委托设计模式…他们不属于23中经典的范畴。这些个设计模式他有这种开发中有这些特点:

1:Java开发中用不全23中设计模式。有些设计模式不适用于微服务和web开发

2:23中设计模式有很多很类似的东西,很像,这样的话,我们就需要区分,比如说我们的代理和装饰器。甚至,我们很多时候会把代理和装饰器和适配器做区分。

3:Mybatis当中还有工厂、建造者等等设计模式。

相关文章
|
4月前
|
存储 缓存 NoSQL
mybatisplus一二级缓存
MyBatis-Plus 继承并优化了 MyBatis 的一级与二级缓存机制。一级缓存默认开启,作用于 SqlSession,适用于单次会话内的重复查询;二级缓存需手动开启,跨 SqlSession 共享,适合提升多用户并发性能。支持集成 Redis 等外部存储,增强缓存能力。
|
1月前
|
缓存 并行计算 监控
vLLM 性能优化实战:批处理、量化与缓存配置方案
本文深入解析vLLM高性能部署实践,揭秘如何通过continuous batching、PagedAttention与前缀缓存提升吞吐;详解批处理、量化、并发参数调优,助力实现高TPS与低延迟平衡,真正发挥vLLM生产级潜力。
428 0
vLLM 性能优化实战:批处理、量化与缓存配置方案
|
3月前
|
缓存 运维 安全
WordPress安全加速:Cloudflare + Nginx缓存优化方案
本文介绍如何通过Cloudflare与Nginx优化WordPress网站性能,涵盖静态资源长期缓存、动态页面智能缓存及敏感路径保护,提升加载速度并保障后台安全。适用于使用Cloudflare与Nginx环境的WordPress站点。
177 0
|
6月前
|
缓存 Java 数据库连接
Mybatis一级缓存详解
Mybatis一级缓存为开发者提供跨数据库操作的一致性保证,有效减轻数据库负担,提高系统性能。在使用过程中,需要结合实际业务场景选择性地启用一级缓存,以充分发挥其优势。同时,开发者需注意其局限性,并做好事务和并发控制,以确保系统的稳定性和数据的一致性。
233 20
|
7月前
|
SQL 存储 Java
Mybatis源码解析:详述初始化过程
以上就是MyBatis的初始化过程,这个过程主要包括SqlSessionFactory的创建、配置文件的解析和加载、映射文件的加载、SqlSession的创建、SQL的执行和SqlSession的关闭。这个过程涉及到了MyBatis的核心类和接口,包括SqlSessionFactory、SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、Configuration、SqlSession和Executor等。通过这个过程,我们可以看出MyBatis的灵活性和强大性,它可以很好地支持定制化SQL、存储过程以及高级映射,同时也避免了几
140 20
|
8月前
|
缓存 Java 数据库连接
Mybatis一级缓存、二级缓存详讲
本文介绍了MyBatis中的查询缓存机制,包括一级缓存和二级缓存。一级缓存基于同一个SqlSession对象,重复查询相同数据时可直接从缓存中获取,减少数据库访问。执行`commit`操作会清空SqlSession缓存。二级缓存作用于同一namespace下的Mapper对象,支持数据共享,需手动开启并实现序列化接口。二级缓存通过将数据存储到硬盘文件中实现持久化,为优化性能,通常在关闭Session时批量写入缓存。文章还说明了缓存的使用场景及注意事项。
302 7
Mybatis一级缓存、二级缓存详讲
|
10月前
|
Java 关系型数据库 数据库连接
简单易懂的 MyBatis 分库分表方案
本文介绍了一种基于 MyBatis 框架的数据库分库分表方案——shardino。不同于复杂插件方式,该方案通过客户端代码包装实现简便易懂的操作,显式处理分库分表逻辑,确保开发者清晰了解数据分布。项目地址:[https://github.com/pyloque/shardino](https://github.com/pyloque/shardino)。方案中,帖子表按 userId 字段 hash 分为 64 张表,平均分配到多个主从库中,配置文件管理 MySQL 组对象,支持读写分离和权重随机选择从库。代码示例展示了如何计算 partition number 并进行具体操作。
330 22
简单易懂的 MyBatis 分库分表方案
|
8月前
|
消息中间件 缓存 NoSQL
缓存与数据库的一致性方案,Redis与Mysql一致性方案,大厂P8的终极方案(图解+秒懂+史上最全)
缓存与数据库的一致性方案,Redis与Mysql一致性方案,大厂P8的终极方案(图解+秒懂+史上最全)
|
9月前
|
缓存 Java 数据库连接
十、MyBatis的缓存
十、MyBatis的缓存
196 6
|
10月前
|
存储 缓存 Java
Java中的分布式缓存与Memcached集成实战
通过在Java项目中集成Memcached,可以显著提升系统的性能和响应速度。合理的缓存策略、分布式架构设计和异常处理机制是实现高效缓存的关键。希望本文提供的实战示例和优化建议能够帮助开发者更好地应用Memcached,实现高性能的分布式缓存解决方案。
205 9