栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

2022-05-13 Druid源码阅读——PreparedStatementPool的结构

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

2022-05-13 Druid源码阅读——PreparedStatementPool的结构

1.PreparedStatementPool的数据结构

在PreparedStatementPool中,成员变量除了日志外,只有两项,一项为LRUCache类型的map,另一项为DruidAbstractDataSource的dataSource,其中map属性为已缓存语句的存储结构,dataSource为当前连接池的指向。

public class PreparedStatementPool {
    private final static Log LOG = LogFactory.getLog(PreparedStatementPool.class);
    private final LRUCache map;
    private final DruidAbstractDataSource dataSource;
}
1.1 LRUCache

LRUCache是继承自LinkedHashMap来实现的LRU缓存,其重写了LinkedHashMap中的removeEldestEntry()方法,该方法实现了对最近最少使用的语句进行回收操作。

 public class LRUCache extends LinkedHashMap {

        private static final long serialVersionUID = 1L;

        public LRUCache(int maxSize){
        	//调用父类构造方法,初始化大小为druid.maxPoolPreparedStatementPerConnectionSize参数配置项,加载因子为0.75,默认按照访问顺序进行排序
            super(maxSize, 0.75f, true);
        }
		
        
        protected boolean removeEldestEntry(Entry eldest) {
            //如果当前语句池中语句数量大于配置的最大数,则关闭并移除该语句
            boolean remove = (size() > dataSource.getMaxPoolPreparedStatementPerConnectionSize());

            if (remove) {
                closeRemovedStatement(eldest.getValue());
            }

            return remove;
        }
    }
1.2 PreparedStatementKey

在判断PreparedStatementKey是否一致时,需要所有字段一致才可以

   public static class PreparedStatementKey {
        
        protected final String     sql;
        
        protected final String     catalog;
        
        protected final MethodType methodType;
        
        public final int           resultSetType;
        
        public final int           resultSetConcurrency;
        
        public final int           resultSetHoldability;
        
        public final int           autoGeneratedKeys;
        //列映射
        private final int[]        columnIndexes;
        private final String[]     columnNames;
}
1.3 PreparedStatementHolder

以default开头的选项为默认配置,在语句执行完被回收时,将相关参数进行重置
inUseCount用来判断当前语句是否在被使用,如果被使用中则不允许被回收
pooling在开启PSCache选项时,默认为true,在进行回收时会设置为false

public final class PreparedStatementHolder {
    
    public final PreparedStatementKey key;
    
    public final PreparedStatement    statement;
    
    private int                       hitCount                 = 0;
    
    private int                       fetchRowPeak             = -1;

    private int                       defaultRowPrefetch       = -1;
    
    private int                       rowPrefetch              = -1;
    
    private boolean                   enterOracleImplicitCache = false;
    
    private int                       inUseCount               = 0;
    
    private boolean                   pooling                  = false;
}
2.PreparedStatementPool对已缓存的语句处理 2.1 从语句池中取出语句(get方法)
    
    public PreparedStatementHolder get(PreparedStatementKey key) throws SQLException {
        //从LRU缓存中获取
        PreparedStatementHolder holder = map.get(key);
        //判空
        if (holder != null) {
            //如果当前语句正在使用中并且未设置sharePreparedStatements,则不返回内容
            if (holder.isInUse() && (!dataSource.isSharePreparedStatements())) {
                return null;
            }
            //增加缓存命中计数,监控指标
            holder.incrementHitCount();
            //增加连接池中的缓存命中计数,监控指标
            dataSource.incrementCachedPreparedStatementHitCount();
            //如果是Oracle数据库,将缓存状态变更到活动
            if (holder.isEnterOracleImplicitCache()) {
                OracleUtils.exitImplicitCacheToActive(holder.statement);
            }
        } else {
            //增加链接池中未命中缓存数,监控指标
            dataSource.incrementCachedPreparedStatementMissCount();
        }

        return holder;
    }
2.2 置入OR归还语句(put方法)
    
    public void put(PreparedStatementHolder stmtHolder) throws SQLException {
        //获得实际的语句对象
        PreparedStatement stmt = stmtHolder.statement;
        //如果实际对象为空,不处理
        if (stmt == null) {
            return;
        }
        //如果是Oracle数据库并且使用了Oracle的隐式缓存
        if (dataSource.isOracle() && dataSource.isUseOracleImplicitCache()) {
            //更改当前的游标状态
            OracleUtils.enterImplicitCache(stmt);
            //设置Oracle隐式缓存状态
            stmtHolder.setEnterOracleImplicitCache(true);
        } else {
            //设置未使用Oracle隐式缓存
            stmtHolder.setEnterOracleImplicitCache(false);
        }
        //将当前语句置入语句池中
        PreparedStatementHolder oldStmtHolder = map.put(stmtHolder.key, stmtHolder);
        //如果之前的引用地址和现在的一致,则不继续处理内容(可以认为是一种归还)
        if (oldStmtHolder == stmtHolder) {
            return;
        }
        //如果有返回旧语句,则将旧语句回收并关闭
        if (oldStmtHolder != null) {
            oldStmtHolder.setPooling(false);
            closeRemovedStatement(oldStmtHolder);
        } else {
            //如果当前语句持有者没有命中过缓存,则认为是新的一条缓存,需要增加连接池中已缓存语句数
            if (stmtHolder.getHitCount() == 0) {
                dataSource.incrementCachedPreparedStatementCount();
            }
        }
        //设置当前语句在池中的状态
        stmtHolder.setPooling(true);

        if (LOG.isDebugEnabled()) {
            String message = null;
            if (stmtHolder.statement instanceof PreparedStatementProxy) {
                PreparedStatementProxy stmtProxy = (PreparedStatementProxy) stmtHolder.statement;
                if (stmtProxy instanceof CallableStatementProxy) {
                    message = "{conn-" + stmtProxy.getConnectionProxy().getId() + ", cstmt-" + stmtProxy.getId()
                              + "} enter cache";
                } else {
                    message = "{conn-" + stmtProxy.getConnectionProxy().getId() + ", pstmt-" + stmtProxy.getId()
                              + "} enter cache";
                }
            } else {
                message = "stmt enter cache";
            }

            LOG.debug(message);
        }
    }
2.3 关闭并删除语句(closeRemovedStatement)

该方法为对缓存语句的回收关闭方法,在对缓存语句弃用时会被调用

public void closeRemovedStatement(PreparedStatementHolder holder) {
    if (LOG.isDebugEnabled()) {
        String message = null;
        if (holder.statement instanceof PreparedStatementProxy) {
            PreparedStatementProxy stmtProxy = (PreparedStatementProxy) holder.statement;
            if (stmtProxy instanceof CallableStatementProxy) {
                message = "{conn-" + stmtProxy.getConnectionProxy().getId() + ", cstmt-" + stmtProxy.getId()
                          + "} exit cache";
            } else {
                message = "{conn-" + stmtProxy.getConnectionProxy().getId() + ", pstmt-" + stmtProxy.getId()
                          + "} exit cache";
            }
        } else {
            message = "stmt exit cache";
        }

        LOG.debug(message);
    }
    //将语句在池中的状态设为false,软删除
    holder.setPooling(false);
    //如果当前语句正在使用,则暂不处理
    if (holder.isInUse()) {
        return;
    }
    //如果当前开启了Oracle的隐式缓存
    if (holder.isEnterOracleImplicitCache()) {
        try {
            //Oracle特殊的关闭方式
            OracleUtils.exitImplicitCacheToClose(holder.statement);
        } catch (Exception ex) {
            LOG.error("exitImplicitCacheToClose error", ex);
        }
    }
    //调用连接池中方法进行关闭
    dataSource.closePreapredStatement(holder);
}
public void closePreapredStatement(PreparedStatementHolder stmtHolder) {
    //如果当前持有者为空,不处理
    if (stmtHolder == null) {
        return;
    }
    //增加已关闭语句缓存计数,监控指标
    closedPreparedStatementCountUpdater.incrementAndGet(this);
    //减少已缓存语句计数,监控指标
    decrementCachedPreparedStatementCount();
    //增加已删除一句计数,监控指标
    incrementCachedPreparedStatementDeleteCount();
    //使用JDBC工具类对语句进行关闭
    JdbcUtils.close(stmtHolder.statement);
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/884899.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号