在Druid预编译SQL时,会检查是否开启poolPreparedStatements参数缓存预编译SQL,这些预编译的SQL存放在哪里?在什么时候进行存放?

1.如何开启poolPreparedStatements(PSCache)功能

需要注意的是,maxPoolPreparedStatementPerConnectionSize的加载顺序在poolPreparedStatements之后,如果将maxPoolPreparedStatementPerConnectionSize设置为负数,则poolPreparedStatements无法生效。

  • druid.poolPreparedStatements配置项设置为true,此时maxPoolPreparedStatementPerConnectionSize默认为10。
  • druid.maxPoolPreparedStatementPerConnectionSize参数值设置为>0的整数,也会开启此功能

2.预编译的SQL缓存在哪里?

通过分析预编译SQL部分,可以发现这些内容被存放在连接持有者的statementPool属性中。

public final class DruidConnectionHolder {protected PreparedStatementPool statementPool;/*** 获得语句池*/public PreparedStatementPool getStatementPool() {//如果语句池为空,则新建语句池,否则返回当前所持有的if (statementPool == null) {statementPool = new PreparedStatementPool(this);}return statementPool;}
}

也就是说,预编译的SQL,会被存放在当前连接的持有者中,这些预编译的内容不会被其他连接所共享。

3.预编译的SQL是什么时候被添加进缓存的?

由于预编译SQL时,没有将语句缓存到语句池中,推测是在语句关闭时进行处理。

 @Overridepublic void close() throws SQLException {//如果当前语句已经被关闭(内部状态判断),则不进行处理if (isClosed()) {return;}//判断当前连接是否被关闭(同样通过内部状态进行判断,此时如果为true,该连接处于等待回收或正在回收)boolean connectionClosed = this.conn.isClosed();// Reset the defaults//如果当前开启了PSCache,并且连接没有被关闭,重置数值到默认if (pooled && !connectionClosed) {try {if (defaultMaxFieldSize != currentMaxFieldSize) {stmt.setMaxFieldSize(defaultMaxFieldSize);currentMaxFieldSize = defaultMaxFieldSize;}if (defaultMaxRows != currentMaxRows) {stmt.setMaxRows(defaultMaxRows);currentMaxRows = defaultMaxRows;}if (defaultQueryTimeout != currentQueryTimeout) {stmt.setQueryTimeout(defaultQueryTimeout);currentQueryTimeout = defaultQueryTimeout;}if (defaultFetchDirection != currentFetchDirection) {stmt.setFetchDirection(defaultFetchDirection);currentFetchDirection = defaultFetchDirection;}if (defaultFetchSize != currentFetchSize) {stmt.setFetchSize(defaultFetchSize);currentFetchSize = defaultFetchSize;}} catch (Exception e) {this.conn.handleException(e, null);}}//由连接对象对预编译语句进行处理conn.closePoolableStatement(this);}
/*** 关闭预编译语句*/
public void closePoolableStatement(DruidPooledPreparedStatement stmt) throws SQLException {//获得原始的预编译语句对象PreparedStatement rawStatement = stmt.getRawPreparedStatement();//获得当前连接的持有者final DruidConnectionHolder holder = this.holder;//如果当前连接不再被持有,则不处理if (holder == null) {return;}//判断是否开启了缓存预编译语句if (stmt.isPooled()) {try {//清空预编译语句中所有的参数rawStatement.clearParameters();} catch (SQLException ex) {//处理异常this.handleException(ex, null);//判断当前连接是否被放弃,如果放弃不继续处理if (rawStatement.getConnection().isClosed()) {return;}LOG.error("clear parameter error", ex);}try {//清除所有的批处理rawStatement.clearBatch();} catch (SQLException ex) {this.handleException(ex, null);if (rawStatement.getConnection().isClosed()) {return;}LOG.error("clear batch error", ex);}}//获得当前语句的持有者PreparedStatementHolder stmtHolder = stmt.getPreparedStatementHolder();//释放当前语句,使其可以被再次获取stmtHolder.decrementInUseCount();//如果开启了PSCache,并且当前语句没有发生过异常if (stmt.isPooled() && holder.isPoolPreparedStatements() && stmt.exceptionCount == 0) {//置入语句池holder.getStatementPool().put(stmtHolder);//清空其返回集合stmt.clearResultSet();//取消对这个语句的跟踪holder.removeTrace(stmt);//记录当前语句的的查询峰值(监控用)stmtHolder.setFetchRowPeak(stmt.getFetchRowPeak());//软关闭当前语句stmt.setClosed(true); // soft set close} else if (stmt.isPooled() && holder.isPoolPreparedStatements()) {// the PreparedStatement threw an exception//进入此分支时,则当前语句抛出过异常//清除所有的返回集合stmt.clearResultSet();//删除跟踪holder.removeTrace(stmt);//从语句池中删除这条语句并关闭,因为这条语句不再健康holder.getStatementPool().remove(stmtHolder);} else {try {//Connection behind the statement may be in invalid state, which will throw a SQLException.//In this case, the exception is desired to be properly handled to remove the unusable connection from the pool.//真正关闭当前预编译过的语句stmt.closeInternal();} catch (SQLException ex) {this.handleException(ex, null);throw ex;} finally {//增加计数(关闭了多少预编译语句)holder.getDataSource().incrementClosedPreparedStatementCount();}}
}

经过查阅代码,发现其确实是在语句关闭时进行处理,对一条被预编译过的语句有以下三种处理方式

  1. 开启PSCache并且这条语句没有抛出过异常时,将其添加进缓存池
  2. 开启PSCache但是这条语句发生过异常,从缓存池中移除并关闭
  3. 没有开启PSCache,直接关闭这条SQL

4.为什么官方文档中不推荐在MySQL中开启PSCache

在查询这方面问题时,关于MySQL不同版本有不同的说法,究其原因是因为在MySQL某一版本之前不支持PSCache。关于poolPreparedStatements问题。 #1256
是否要在线上开启可以参考Druid监控中查看PSCache命中数据来决定。

2022-05-12 Druid源码阅读——poolPreparedStatements是如何控制缓存游标的?相关推荐

  1. Alibaba Druid 源码阅读(二) 数据库连接池实现初步探索

    Alibaba Druid 源码阅读(二) 数据库连接池实现初步探索 简介 在上篇文章中,了解了连接池的应用场景和本地运行了示例,本篇文章中,我们尝试来探索下Alibaba Druid数据库连接池的整 ...

  2. Alibaba Druid 源码阅读(五)数据库连接池 连接关闭探索

    Alibaba Druid 源码阅读(五)数据库连接池 连接关闭探索 简介 在上文中探索了数据库连接池的获取,下面接着初步来探索下数据库连接的关闭,看看其中具体执行了那些操作 连接关闭 下面的具体的代 ...

  3. Alibaba Druid 源码阅读(四) 数据库连接池中连接获取探索

    Alibaba Druid 源码阅读(四) 数据库连接池中连接获取探索 简介 上文中分析了数据库连接池的初始化部分,接下来我们来看看获取连接部分的代码 数据库连接池中连接获取 下面的相关的代码,在代码 ...

  4. Alibaba Druid 源码阅读(三) 数据库连接池初始化探索

    Alibaba Druid 源码阅读(三) 数据库连接池初始化探索 简介 上文中探索了Alibaba Druid的连接池初始化和获取连接的关键代码,接下来详细看看初始化部分 数据库连接池初始化 对整个 ...

  5. Alibaba Druid 源码阅读(一) 数据库连接池初步

    Alibaba Druid 源码阅读(一) 数据库连接池初步 简介 本文将初步探索数据库连接池的应用场景,为后面的源码分析做些准备 数据库连接池的应用场景 在没有连接池之前,在使用中,需要访问数据库时 ...

  6. Druid源码阅读3-DruidDataSource连接池的基本原理

    DruidDataSource数据库连接池的的本质,实际上是一个利用ReentrentLock和两个Condition组成的生产者和消费者模型. 1.DruidDataSource中的锁 在Druid ...

  7. ExoPlayer 源码阅读小记--HLS播放带缓存加载M38U文件过程

    基于ExoPlayer 2.17.1源码分析,基本是一边看一边写的流水账,记录下防止以后忘了: 第一步createMediaSource创建HlsMediaSource对象时同时会实例化出HlsPla ...

  8. 12 哈希表相关类——Live555源码阅读(一)基本组件类

    12 哈希表相关类--Live555源码阅读(一)基本组件类 这是Live555源码阅读的第一部分,包括了时间类,延时队列类,处理程序描述类,哈希表类这四个大类. 本文由乌合之众 lym瞎编,欢迎转载 ...

  9. 12.源码阅读(app启动流程-android api 26)

    activity的启动流程之前已经通过源码了解了,那么app的启动流程是怎样的,从我们按下app的图标,到应用启动起来显示出画面,中间都经历了什么? 安卓是基于java的,所以和java有一定的相似性 ...

最新文章

  1. web本地存储-IndexedDB
  2. XPath学习:轴(13)——namespace
  3. tomcat的JK和JK2
  4. 有人说Julia比Python好,还给出了5个理由
  5. Linux Kernel中的同步机制的介绍
  6. 初学PX4之飞控算法
  7. JeewxBoot微信管家平台源码v1.3
  8. 通过使用阿里云+vuepress快速搭建静态个人博客网页页面
  9. Eigen教程(5)之块操作
  10. 【数据结构笔记13】C实现:判别是否是同一颗二叉搜索树(BST)
  11. 使 32 位程序使用大于 2GB 的内存
  12. atitit.DD dragdrop拖拽文件到界面功能 html5 web 跟个java swing c#.net c++ 的总结
  13. snipaste如何滚动截图_一文解决几乎所有截图需求:我多年来用过的优秀截图软件和插件推荐...
  14. python广州地图_广东省客户数量地图展示,如何通过python实现?
  15. 计算机更换硬盘键盘鼠标不好使,安装win7时鼠标键盘不能用怎么办?(完美解决方法)...
  16. 微信小游戏开发指南(一)什么是微信小游戏
  17. url中出现“%22”等如何处理?如何判断url中是否有“%22等”?如何获取当前网址?传入多个参数在url上? encodeURL和(js)
  18. Nginx系列教材 (五)- 和Tomcat进行负载均衡
  19. 线上配镜新方式:眼镜直通车竞品分析报告
  20. 摄像头模组介绍和技术指标

热门文章

  1. 淘宝店铺层级每个月更新么?如何提高淘宝店铺层级?
  2. android 全屏倒计时,全屏计时器app
  3. halcon学习笔记-车牌号识别
  4. QQ农场外挂开发实践
  5. 【小样本基础】Meta-Learning 元学习流程:图解MAML代码
  6. Ubuntu系统安装时grub修复操作
  7. 2020年国考申论热点:治理“老剧翻拍”乱象
  8. 视频教程-WebGL 可视化3D绘图框架:Three.js 零基础上手实战-其他
  9. 名帖59 褚遂良 小楷《千字文》
  10. linux下进入recovery mode 的单用户模式