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

news2024/11/6 11:34:00

文章目录

  • Mybatis缓存设计
  • 缓存KEY的设计
    • CacheKey类主体
    • CacheKey组成
    • CacheKey如何保证缓存key的唯一性

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类主体

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

  // 创建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方法进行计算

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对象相等,则两次查询操作的条件必定是完全一致的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2234251.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

面试题:JVM(二)

1. 面试题 简述 Java 类加载机制?&#xff08;百度&#xff09; JVM类加载机制 &#xff08;滴滴&#xff09; JVM中类加载机制&#xff0c;类加载过程&#xff0c;什么是双亲委派模型&#xff1f; &#xff08;腾讯&#xff09; JVM的类加载机制是什么&#xff1f; &#x…

数据库的使用02:SQLServer的连接字符串、备份、还原、SQL监视相关设置

目录 一、连接字符串 【本地连接字符串】 【远程连接字符串】 二、备份 三、还原 &#xff08;1&#xff09;还原数据库-bak、btn文件 &#xff08;2&#xff09;附加数据库mdf文件 四、SQL监视器的使用 一、连接字符串 【本地连接字符串】 server DESKTOP-FTH2P3S; Da…

【2024工业图像异常检测文献】UCAD: 使用对比学习提示的无监督连续异常检测方法

Unsupervised Continual Anomaly Detection with Contrastively-learned Prompt 1、Background 无监督异常检测&#xff08;UAD&#xff09;专注于在没有先验知识或标记实例的情况下识别数据中的不寻常模式或异常值&#xff0c;仅依赖于“正常”数据的内在分布&#xff08;Cha…

【算法】Floyd多源最短路径算法

目录 一、概念 二、思路 三、代码 一、概念 在前面的学习中&#xff0c;我们已经接触了Dijkstra、Bellman-Ford等单源最短路径算法。但首先我们要知道何为单源最短路径&#xff0c;何为多源最短路径 单源最短路径&#xff1a;从图中选取一点&#xff0c;求这个点到图中其他…

[C++]——哈希(附源码)

目录 ​编辑 ​编辑 一、前言 二、正文 2.1 unorder系列关联式容器 2.1.1 unordered_map 2.1.1.1 unorderer_map的介绍 ①unordered_map的构造 ②unordered_map的容量 ③unordered_map的迭代器 ④unordered_map的元素访问 ⑤unordered_map的查询 ⑥unordered_map的修改操…

使用Ubuntu快速部署MinIO对象存储

想拥有自己的私有云存储&#xff0c;安全可靠又高效&#xff1f;MinIO是你的理想选择&#xff01;这篇文章将手把手教你如何在Ubuntu 22.04服务器上部署MinIO&#xff0c;并使用Nginx反向代理和Let’s Encrypt证书进行安全加固。 即使你是新手&#xff0c;也能轻松完成&#xf…

Maven 下载配置 详解 我的学习笔记

Maven 下载配置 详解 我的学习笔记 一、Maven 简介二、maven安装配置三、maven基本使用四、idea配置mavenidea配置maven环境maven坐标idea创建maven项目配置Maven-Helper插件 五、依赖管理 一、Maven 简介 Apache Maven 是一个项目管理和构建工具&#xff0c;它基于项目对象模型…

一文带你了解,全国职业院校技能大赛老年护理与保健赛项如何备赛

老年护理与保健&#xff0c;作为2023年全国职业院校技能大赛的新增赛项&#xff0c;紧密贴合党的二十大精神&#xff0c;致力于加速健康与养老产业的蓬勃发展&#xff0c;并深化医养康养结合的服务模式。此赛项不仅承载着立德树人的教育使命&#xff0c;更通过竞赛的引领作用&a…

STM32ZET6-USART使用

一、原理说明 STM32自带通讯接口 通讯目的 通信方式&#xff1a; 全双工&#xff1a;通信时可以双方同时通信。 半双工&#xff1a;通信时同一时间只能一个设备发送数据&#xff0c;其他设备接收。 单工&#xff1a;只能一个设备发送到另一个设备&#xff0c;例如USART只有…

电话语音机器人,是由哪些功能构成?

电话语音机器人是自动电话销售、筛选意向客户的&#xff0c;只要录入好行业话术&#xff0c;导入要拨打的手机号&#xff0c;机器人就可以上岗工作了。 电话语音机器人组成部分&#xff1a; 1、语音识别器&#xff0c;主要作用&#xff1a;识别客户讲话内容&#xff0c;从而做…

理解 WordPress | 第二篇:结构化分析

WordPress 专题致力于从 0 到 1 搞懂、用熟这种可视化建站工具。 第一阶段主要是理解。 第二阶段开始实践个人博客、企业官网、独立站的建设。 如果感兴趣&#xff0c;点个关注吧&#xff0c;防止迷路。 WordPress 的内容和功能结构可以按照层级来划分&#xff0c;这种层次化的…

vue3项目history模式部署404处理,使用 historyApiFallback 中间件支持单页面应用路由

vue3项目history模式部署404处理&#xff0c;使用 historyApiFallback 中间件支持单页面应用路由 在现代的 web 开发中&#xff0c;单页面应用&#xff08;SPA&#xff09;变得越来越流行。这类应用通常依赖于客户端路由来提供流畅的用户体验&#xff0c;但在服务器端&#xf…

计算机毕业设计Hadoop+PySpark深度学习游戏推荐系统 游戏可视化 游戏数据分析 游戏爬虫 Scrapy 机器学习 人工智能 大数据毕设

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

PHP电商供应链ERP管理系统小程序源码

&#x1f680;电商供应链大揭秘&#xff01;ERP管理系统如何重塑你的商业版图✨ &#x1f50d; 什么是电商供应链ERP管理系统&#xff1f; 电商供应链ERP管理系统是一款基于FastAdminThinkPHP开发的系统。该系统可满足电商企业管理自身进销存&#xff0c;帮助中小型电商企业管…

全参微调与LoRA的区别,及7种LoRA变种方法解析

随着LLM的发展和应用&#xff0c;在LLM的预训练模型基础上做微调&#xff0c;使其适用于自己的业务场景的研究越来越多。与全参数SFT相比LoRA是在冻结LLM本身参数的基础上&#xff0c;在旁路增加两个可学习的矩阵&#xff0c;用于训练和学习&#xff0c;最后推理是LLM输出和可学…

ubuntu工具 -- ubuntu服务器临时没有网络,急需联网下载东西怎么办? 使用手机提供网络

问题 ubuntu服务器配置经常遇到临时需要网络下载文件需求, 通过有线连接又来不及 解决方法 使用手机usb为ubuntu服务器提供网络 先在ubuntu上运行 ifconfig 查看当前的网络接口, 一会看看多了哪个网口 1. 手机端操作 先使用usb数据线将手机连接到服务器上 打开手机的usb共享…

一文快速预览经典深度学习模型(一)——CNN、RNN、LSTM、Transformer、ViT

Hi&#xff0c;大家好&#xff0c;我是半亩花海。本文主要简要并通俗地介绍了几种经典的深度学习模型&#xff0c;如CNN、RNN、LSTM、Transformer、ViT&#xff08;Vision Transformer&#xff09;等&#xff0c;便于大家初探深度学习的相关知识&#xff0c;并更好地理解深度学…

【D3.js in Action 3 精译_038】4.2 D3 折线图的绘制方法及曲线插值处理

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可…

数据结构(8.7_2)——败者树

多路平衡归并带来的问题 什么是败者树 败者树的构造 败者树的使用 败者树在多路平衡归并中的应用 败者树的实现思路 总结

Web Broker(Web服务应用程序)入门教程(1)

1、介绍 Web Broker 组件&#xff08;位于工具面板的“Internet”选项卡中&#xff09;可以帮助您创建与特定统一资源标识符&#xff08;URI&#xff09;相关联的事件处理程序。当处理完成后&#xff0c;您可以通过编程方式构建 HTML 或 XML 文档&#xff0c;并将它们传输给客…