SpringBoot使用 druid 连接池来优化分页语句

一、前言

一个老系统随着数据量越来越大,我们察觉到部分分页语句拖慢了我们的速度。

鉴于老系统的使用方式,不打算使用pagehelper和mybatis-plus来处理,加上系统里使用得是druid连接池,考虑直接使用druid来优化。

二、老代码

老代码是使用得一个mybatis插件进行的分页,分页的核心代码如下:

// 记录统计的 sql
String countSql = "select count(0) from (" + sql+ ") tmp_count";
PreparedStatement countStmt = connection.prepareStatement(countSql);
BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql, boundSql.getParameterMappings(), parameterObject);
setParameters(countStmt, mappedStatement, countBS,parameterObject);

在原始的 sql 外面包装了一个 count sql,当然很多插件都是这样做的。

三、druid 的 PagerUtil

示例 sql(有比较复杂的坐标计算)

SELECT g.*
  , ROUND(6378.138 * 2 * ASIN(SQRT(POW(SIN((? * PI() / 180 - t.latitude * PI() / 180) / 2), 2) + COS(? * PI() / 180) * COS(t.latitude * PI() / 180) * POW(SIN((? * PI() / 180 - t.longitude * PI() / 180) / 2), 2))), 2) AS distancecd
  , t.agentname, t.agentlogo, t.compaddress
FROM t_bas_integral_goods g
  LEFT JOIN t_bas_agent t ON g.agentid = t.AGENTID
WHERE t.AGENTTYPE = '2'
  AND t.pass = '0'
  AND t.dl_type = '4'
  AND g.type = 0
ORDER BY distancecd ASC

使用 Druid 生成 count sql:

String countSql = PagerUtils.count(sql, DbType.mysql);
System.out.println(countSql);

输出:

SELECT COUNT(*)
FROM t_bas_integral_goods g
 LEFT JOIN t_bas_agent t ON g.agentid = t.AGENTID
WHERE t.AGENTTYPE = '2'
 AND t.pass = '0'
 AND t.dl_type = '4'
 AND g.type = 0

我们可以看到优化后的 count sql 变得十分简洁,坐标计算的都已经丢弃掉。 注意:PagerUtil还有limit方法用来生成limit语句,感兴趣的同学可以自行试验。

四、改造mybatis分页插件

4.1 踩坑之路

看到上面 druid PagerUtils count 的优化效果,立马开始改造起来,起初只改掉了countSql,

String countSql = PagerUtils.count(sql, dbType);
PreparedStatement countStmt = connection.prepareStatement(countSql);
BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql, boundSql.getParameterMappings(), parameterObject);
setParameters(countStmt, mappedStatement, countBS,parameterObject);

启动起来测试一番就发现报错了,因为原始 sql 中含有?变量,优化后的 sql 已经没有变量了,插件还会继续给他设置变量。 我们要怎么解决这个问题呢?

我们再回头看看pagehelper和mybatis-plus是怎么实现的!它俩都是基于jsqlparser对 sql 进行解析,然后处理。

要多加一个jsqlparser?没必要没必要,druid 的 sql 解析功能也是很强大的,我看了看PagerUtils.count方法的源码,大不了用 druid 的 sql 解析实现一遍。

看了看源码之后我陷入了沉思,有必要搞这么复杂么?有没有更好的方法?我反复 debug 发现了,DynamicSqlSource中有带#{xxx}这样的原始 sql,

那么我是否可以使用 druid 先对这种 mybatis 占位符的 sql 进行优化呢?我们来试试:

示例 sql:

select * from xxx where type = #{type} order by xx

输出:

SELECT COUNT(*)
FROM xxx
WHERE type = #{type}

完美!!! 4.2 继续踩坑

然而直接在 Mapper 上注解的 sql 还是有问题,拿不到原始的 sql,debug 发现 RawSqlSource 在构造器里就将 sql 处理成了?号挂参的形式。

@Select("select * from xxx where type = #{type} order by xx")
Object test(@Param("type") String type);

那么我只能看看能不能扩展它,我找到了它是在XMLLanguageDriver里进行初始化,这下好办了,因为我之前扩展过XMLLanguageDriver,它是可以自定义配置的。 于是我重写了RawSqlSource, 添加上了包含 mybatis 参数占位符(#{})的rawSql字段。

/**
 * 原始 sql,用于方便 druid 工具进行分页
 *
 * @author L.cm
 */
public class MicaRawSqlSource implements SqlSource {
  private final String rawSql;
  private final SqlSource sqlSource;
  public MicaRawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
    this(configuration, getSql(configuration, rootSqlNode), parameterType);
  }
  public MicaRawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> clazz = parameterType == null ? Object.class : parameterType;
    this.rawSql = sql;
    this.sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
  }
  // ... ...
}

自此全部逻辑已经走通,我们再来看看我们的PagePlugin核心代码:

// 进行分页
Configuration configuration = mappedStatement.getConfiguration();
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> parameterType = parameterObject.getClass();
Connection connection = (Connection) invocation.getArgs()[0];
// 1. 对 sql 进行判断,如果没有 ? 号,则直接处理
String boundRawSql = boundSql.getSql();
if (boundRawSql.indexOf(CharPool.QUESTION_MARK) == -1) {
  // 不包含 ? 号
  String countSql = PagerUtils.count(boundRawSql, dbType);
  SqlSource newSqlSource = sqlSourceParser.parse(countSql, parameterType, new HashMap<>());
  BoundSql newBoundSql = newSqlSource.getBoundSql(parameterObject);
  int count = getCount(connection, mappedStatement, parameterObject, newBoundSql);
  StringBuilder sqlBuilder = new StringBuilder(boundRawSql);
  Page page = getPageParam(parameterObject, sqlBuilder, count);
  String pageSql = generatePageSql(sqlBuilder.toString(), dbType, page);
  // 将分页sql语句反射回BoundSql.
  setField(boundSql, "sql", pageSql);
  return invocation.proceed();
}
// 2. 按 SqlSource 进行解析
SqlSource sqlSource = mappedStatement.getSqlSource();
// xml 中的动态 sql
int count;
if (sqlSource instanceof DynamicSqlSource) {
  SqlNode rootSqlNode = PagePlugin.getField(sqlSource, "rootSqlNode");
  DynamicContext context = new DynamicContext(configuration, parameterObject);
  rootSqlNode.apply(context);
  // 生成 count sql,带 #{xxx} 变量的 sql
  String countSql = PagerUtils.count(context.getSql(), dbType);
  SqlSource newSqlSource = sqlSourceParser.parse(countSql, parameterType, context.getBindings());
  BoundSql newBoundSql = newSqlSource.getBoundSql(parameterObject);
  count = getCount(connection, mappedStatement, parameterObject, newBoundSql);
} else if (sqlSource instanceof MicaRawSqlSource) {
  String rawSql = ((MicaRawSqlSource) sqlSource).getRawSql();
  DynamicContext context = new DynamicContext(configuration, parameterObject);
  // 生成 count sql,带 #{xxx} 变量的 sql
  String countSql = PagerUtils.count(rawSql, dbType);
  SqlSource newSqlSource = sqlSourceParser.parse(countSql, parameterType, context.getBindings());
  BoundSql newBoundSql = newSqlSource.getBoundSql(parameterObject);
  count = getCount(connection, mappedStatement, parameterObject, newBoundSql);
} else {
  throw new IllegalArgumentException("不支持的 sql 分页形式,请使用 xml 或者注解");
}

五、结论

整个老服务通过切换到 mica(深度定制)的微服务架构(演示环境仅仅在单服务低内存配置)之后速度提升效果明显,当然后面我们还会继续进行优化。

到此这篇关于SpringBoot使用 druid 连接池来优化分页语句的文章就介绍到这了,更多相关SpringBoot druid 连接池分页内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 关于数据库连接池Druid使用说明

    根据综合性能,可靠性,稳定性,扩展性,易用性等因素替换成最优的数据库连接池. Druid:druid-1.0.29 数据库 Mysql.5.6.17 替换目标:替换掉C3P0,用druid来替换 替换原因: 1.性能方面 hikariCP>druid>tomcat-jdbc>dbcp>c3p0 .hikariCP的高性能得益于最大限度的避免锁竞争. 2.druid功能最为全面,sql拦截等功能,统计数据较为全面,具有良好的扩展性. 3.综合性能,扩展性等方面,可考虑使用druid或

  • SpringBoot整合Druid数据库连接池的方法

    一,Druid是什么? Druid是Java语言中最好的数据库连接池.Druid能够提供强大的监控和扩展功能. 二, 在哪里下载druid maven中央仓库: http://central.maven.org/maven2/com/alibaba/druid/ 三, 怎么获取Druid的源码 Druid是一个开源项目,源码托管在github上,源代码仓库地址是 https://github.com/alibaba/druid.同时每次Druid发布正式版本和快照的时候,都会把源码打包,你可以从

  • 数据库阿里连接池 druid配置详解

    Java程序很大一部分要操作数据库,为了提高性能操作数据库的时候,有不得不使用数据库连接池.数据库连接池有很多选择,c3p.dhcp.proxool等,druid作为一名后起之秀,凭借其出色的性能,也逐渐印入了大家的眼帘.接下来本教程就说一下druid的简单使用. 首先从 http://repo1.maven.org/maven2/com/alibaba/druid/下载最新的jar包.如果想使用最新的源码编译,可以从 https://github.com/alibaba/druid下载源码,然

  • springboot配置druid连接池的方法示例

    Druid的简介 Druid首先是一个数据库连接池.Druid是目前最好的数据库连接池,在功能.性能.扩展性方面,都超过其他数据库连接池,包括DBCP.C3P0.BoneCP.Proxool.JBoss DataSource.Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验.Druid是阿里巴巴开发的号称为监控而生的数据库连接池! Druid的功能 1.替换DBCP和C3P0.Druid提供了一个高效.功能强大.可扩展性好的数据库连接池. 2.可以监控数据库

  • Spring Boot集成Druid数据库连接池

    1. 前言 Druid数据库连接池由阿里巴巴开源,号称是java语言中最好的数据库连接池,是为监控而生的.Druid的官方地址是:https://github.com/alibaba/druid 通过本文,我们可以看到 Spring Boot 如何配置数据源 Spring Boot 如何集成Druid数据库连接池 如何打开并访问Druid数据库连接池的监控功能 Spring Boot 使用JdbcTemplate操作数据库 2. 配置pom.xml <parent> <groupId&g

  • springboot2.0使用Hikari连接池的方法(替换druid)

    1.springboot 2.0 默认连接池就是Hikari了,所以引用parents后不用专门加依赖 2.贴我自己的配置(时间单位都是毫秒) # jdbc_config datasource spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://127.0.0.1:3306/datebook?useUnicode=true&characterEncoding=UT

  • Spring Boot使用Druid连接池的示例代码

    Druid是Java语言中最好的数据库连接池.Druid相比于其他的数据库连接池,有两大特性: 监控数据库,有利于分析线上数据库问题 更容易扩展,同时也很高效. 今天演示一下Spring Boot集成Druid. 实战 1.添加Maven依赖. Spring Boot版本使用的是1.x的,2.x的版本druid starter还不支持.不过自定义也是没问题的. <!--starter-web 方便我们查看效果--> <dependency> <groupId>org.s

  • spring boot配置druid连接池的完整步骤

    Druid的简介 Druid首先是一个数据库连接池.Druid是目前最好的数据库连接池,在功能.性能.扩展性方面,都超过其他数据库连接池,包括DBCP.C3P0.BoneCP.Proxool.JBoss DataSource.Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验.Druid是阿里巴巴开发的号称为监控而生的数据库连接池! Druid的功能 1.替换DBCP和C3P0.Druid提供了一个高效.功能强大.可扩展性好的数据库连接池. 2.可以监控数据库

  • SpringBoot使用 druid 连接池来优化分页语句

    一.前言 一个老系统随着数据量越来越大,我们察觉到部分分页语句拖慢了我们的速度. 鉴于老系统的使用方式,不打算使用pagehelper和mybatis-plus来处理,加上系统里使用得是druid连接池,考虑直接使用druid来优化. 二.老代码 老代码是使用得一个mybatis插件进行的分页,分页的核心代码如下: // 记录统计的 sql String countSql = "select count(0) from (" + sql+ ") tmp_count"

  • springboot整合druid连接池的步骤

    使用springboot默认的连接池 导入springboot data-jdbc依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> 配置文件配置连接池 spring: datasource: username: root pass

  • springboot集成druid连接池配置的方法

    在开发项目中如果数据库选型为mysql,很大概率下连接池会使用druid 这里介绍springboot集成durid springboot : 2.1.9 druid : 1.1.10 案例地址 github地址 springboot集成druid配置 需要引入的pom <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactI

  • SpringBoot集成Druid连接池连接MySQL8.0.11

    目录 1.配置依赖 1.1MySQL Connector/J依赖配置 maven 1.2Druid依赖配置 2.配置DataSource 2.1创建配置类 2.2注意事项 这篇博客简单介绍spring boot集成druid连接池的简单配置和注意事项 本次使用的是MySQL8.0.11版本 前提:一个已经搭建好的Spring Boot项目和可以使用的数据库 注意:MySQL8.0.X的配置和5.7.X有小差异,整体思路完全一样 1.配置依赖 依赖管理我们可以使用maven或者gradle MyS

  • SpringBoot集成Druid连接池进行SQL监控的问题解析

    Druid连接池是阿里巴巴开源的数据库连接池项目.Druid连接池为监控而生,内置强大的监控功能,监控特性不影响性能. Druid的监控统计功能是通过filter-chain扩展实现,采集的信息非常全面,包括SQL执行.并发.慢查.执行时间区间分布等.并且Druid内置提供了一个StatViewServlet用于展示Druid的统计信息,提高html页面展示非常完备的监控信息,可以快速诊断系统的瓶颈. 而Druid增加StatFilter之后,能采集大量统计信息,同时对性能基本没有影响.Stat

  • Springboot中加入druid连接池

    目录 1.DRUID连接池介绍 2.DRUID 的参数 3.配置依赖 4.添加文件 1.DRUID连接池介绍 Druid是阿里巴巴开发的号称为监控而生的数据库连接池,Druid是目前最好的数据库连接池.在功能.性能.扩展性方面,都超过其他数据库连接池,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况. 2.DRUID 的参数 jdbcUrl 连接数据库的url:mysql : jdbc:mysql://localhost:3306/testusername 数据库的用户名passw

  • Java Druid连接池与Apache的DBUtils使用教程

    目录 Druid连接池 连接池思想 Druid连接池使用步骤 引入相关jar包 创建database.properties配置文件 编写连接池工具类 Druid连接池测试 Apache的DBUtils使用 Apache DBUtils介绍 Apache DBUtils特征 Apache DbUtils主要组成 Apache DbUtils使用步骤 综合案例 创建product表 向表中添加数据 创建实体类Product 创建ProductDao接口 创建ProductDaoImpl实现类 创建P

  • JSP Spring中Druid连接池配置详解

    JSP Spring中Druid连接池配置 jdbc.properties url=jdbc:postgresql://***.***.***.***:****/**** username=*** password=*** applicationContext.xml中配置bean <!-- 阿里 druid 数据库连接池 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSou

随机推荐