【MyBatis源码】CacheKey缓存键的原理分析

文章目录

Mybatis缓存设计

MyBatis 每秒过滤众多数据库查询操作,这对 MyBatis 缓存键的设计提出了很高的要求。MyBatis缓存键要满足以下几点。

无碰撞:必须保证两条不同的查询请求生成的键不一致,这是最重要也是必须满足的要求。否则会引发查询操作命中错误的缓存,并返回错误的结果。

高效比较:每次缓存查询操作都可能会引发键之间的多次比较,因此该操作必须是高效的。

高效生成:每次缓存查询和写入操作前都需要生成缓存的键,因此该操作也必须是高效的。

在编程中,我们常使用数值、字符串等简单类型作为键,然而,这类键容易产生碰撞。为了防止碰撞的发生,需要将键的生成机制设计得非常复杂,这又降低了键的比较效率和生成效率。因此,准确度和效率之间往往是相互制约的。

为了解决以上问题,MyBatis设计了一个 CacheKey类作为缓存键。整个 CacheKey设计得并不复杂,但又非常精巧。

设计图解释:

【1】 Mybatis缓存的使用和我们一般使用缓存方式相同,使用一个内存缓存(map)作为本地容器。对于查询请求优先查询本地缓存,如果有直接返回,没有查询数据库,并将数据库查询结果写入到缓存中。

【2】 Mybatis使用CacheKey类作为缓存(map)的key,重写了其hashcode和equal方法。

【3】 CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); Mybatis主要根据mapper信息,参数值,分页信息,SQL信息设计缓存key

缓存KEY的设计

CacheKey类主体

java 复制代码
public class CacheKey implements Cloneable, Serializable {
   /**
   * 乘数,用来计算hashcode时使用
   */
  private final int multiplier;

  /**
   * 哈希值,整个CacheKey的哈希值
   */
  private int hashcode;

  /**
   * 求和校验码
   */
  private long checksum;

  /**
   * 更新次数,整个CacheKey的更新次数
   */
  private int count;
  /**
   * 更新历史
   */
  private List<Object> updateList;

  public void update(Object object) {
    int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);

    count++;
    checksum += baseHashCode;
    baseHashCode *= count;

    hashcode = multiplier * hashcode + baseHashCode;

    updateList.add(object);
  }

  public void updateAll(Object[] objects) {
    for (Object o : objects) {
      update(o);
    }
  }

  @Override
  public boolean equals(Object object) {
    if (this == object) {
      return true;
    }
    if (!(object instanceof CacheKey)) {
      return false;
    }

    final CacheKey cacheKey = (CacheKey) object;

    if (hashcode != cacheKey.hashcode) {
      return false;
    }
    if (checksum != cacheKey.checksum) {
      return false;
    }
    if (count != cacheKey.count) {
      return false;
    }

    for (int i = 0; i < updateList.size(); i++) {
      Object thisObject = updateList.get(i);
      Object thatObject = cacheKey.updateList.get(i);
      if (!ArrayUtil.equals(thisObject, thatObject)) {
        return false;
      }
    }
    return true;
  }

  @Override
  public int hashCode() {
    return hashcode;
  }
}

CacheKey组成

org.apache.ibatis.executor.BaseExecutor#createCacheKey

java 复制代码
  // 创建CacheKey对象
    CacheKey cacheKey = new CacheKey();
    // mapper-id
    cacheKey.update(ms.getId());
    // 分页参数
    cacheKey.update(rowBounds.getOffset());
    cacheKey.update(rowBounds.getLimit());
    // 执行的SQL(带有占位符的)
    cacheKey.update(boundSql.getSql());
    // 执行SQL参数value值
    cacheKey.update(value);
    // 环境配置id
    cacheKey.update(configuration.getEnvironment().getId());

CacheKey主要由5部分组成:

【1】 mapper接口对应的statementId

【2】 分页参数

【3】 执行SQL

【4】 传入参数的值

【5】 当前环境的ID

CacheKey如何保证缓存key的唯一性

CacheKey首先类设计了多个重要属性,这些属性为结合传入的参数信息进行组合计算以提高缓存key的唯一性,并能够以较高的性能进行比较计算。

其中hashcode的计算主要通过update方法进行计算

java 复制代码
public void update(Object object) {
    int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);

    count++;
    checksum += baseHashCode;
    baseHashCode *= count;

    hashcode = multiplier * hashcode + baseHashCode;

    updateList.add(object);
  }

在比较 CacheKey对象是否相等时,会先进行类型判断,然后进行 hashcode、checksum、count的比较,只要有一项不相同则表明两个对象不同。以上操作都比较简单,能在很短的时间内完成。如果上面的各项属性完全一致,则会详细比较两个CacheKey 对象的变动历史 updateList,这一步操作相对复杂,但是能保证绝对不会出现碰撞问题。

【CacheKey生成的结果示例】

2042432675:5771996351:user.selectById:0:2147483647:select * from t_user where id = ? and name = ?:1:null:development

MyBatis 还准备了一个 NullCacheKey,该类用来充当一个空键使用。在缓存查询中,如果发现某个 CacheKey信息不全,则会返回 NullCacheKey对象,类似于返回一个null值。但是 NullCacheKey毕竟是 CacheKey的子类,在接下来的处理中不会引发空指针异常。这种设计方式也非常值得我们借鉴。

MyBatis生成的 CacheKey 对象中包含了这次查询的所有信息,包括查询语句的 id、查询的翻页限制、数据总量、完整的 SQL语句,这些信息一致就保证了两次查询的一致。结合 CacheKey的 equals方法,我们知道只要通过 equals方法判断两个CacheKey对象相等,则两次查询操作的条件必定是完全一致的。

相关推荐
wuyu112516 分钟前
Qt字符编码
数据库·mysql·mybatis
昂子的博客3 小时前
通过mybatis和mybatis plus 实现用户注册功能和基础的增删改查
java·开发语言·mybatis
烂漫心空4 小时前
Spring Boot 整合 MyBatis
java·spring boot·maven·mybatis
听潮阁18 小时前
【SpringCloud详细教程】-01-一文了解微服务
开发语言·spring boot·spring cloud·servlet·java-ee·mybatis
w_t_y_y20 小时前
Mybatis中的缓存
java·缓存·mybatis
毕业设计制作和分享1 天前
ssm校园二手交易管理系统+vue
开发语言·前端·javascript·vue.js·mybatis·课程设计
阳小江1 天前
Mybatis查询数据库,返回List集合,集合元素也是List。
mybatis
计算机毕设指导61 天前
基于SpringBoot的植物园管理小程序【附源码】
java·vue.js·spring boot·mysql·tomcat·intellij-idea·mybatis
Kika写代码1 天前
【基于轻量型架构的WEB开发】课程 作业2 mybatis关联查询、缓存、注解
前端·架构·mybatis
天狗食日L1 天前
MyBatis 与 MyBatis-Plus
java·spring boot·mybatis