SpringBoot集成DruidDataSource实现监控 SQL 性能

SpringBoot集成DruidDataSource实现监控 SQL 性能_第1张图片

一、快速入门

1.1 基本概念

  我们都使用过连接池,比如C3P0、DBCP、hikari、Druid,虽然 HikariCP 的速度稍快,但 Druid 能够提供强大的监控和扩展功能。Druid DataSource 是阿里巴巴开发的号称为监控而生的数据库连接池,它不仅可以获取数据库连接,还把这些数据库连接管理了起来,也就是所谓的数据库连接池。在功能、性能、扩展性方面,都超过其他数据库连接池,可以说是 Java 语言中最好的数据库连接池。
  Spring Boot 2.0 以上默认使用 Hikari 数据源,而Druid已经在阿里巴巴部署了超过600个应用,经过好几年生产环境大规模部署的严苛考验!Druid 可以很好的监控 DB 池连接和 SQL 的执行情况,天生就是针对监控而生的 DB 连接池。

  • Github项目地址 https://github.com/alibaba/druid
  • 文档 https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98
  • 下载 http://repo1.maven.org/maven2/com/alibaba/druid/
  • 监控DEMO http://120.26.192.168/druid/index.html

1.2 如何使用 DruidDataSource

首先看一下怎么使用 DruidDataSource。

// 配置一个 DruidDataSource 实例 bean 交由 Spring 容器管理
@Configuration
public class DataSourceConfig {
    @Bean
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        dataSource.setUrl("jdbc:mysql://localhost:3306/transaction?useSSL=false");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        //连接池最小空闲的连接数
        dataSource.setMinIdle(5);
        //连接池最大活跃的连接数
        dataSource.setMaxActive(20);
        //初始化连接池时创建的连接数
        dataSource.setInitialSize(10);
        return dataSource;
    }
}
 
//测试类
public class DataSourceMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(DataSourceConfig.class);
        //获取 dataSource 实例
        DataSource dataSource = (DataSource) applicationContext.getBean("dataSource");
        try {
            //通过数据源获取数据库连接
            Connection connection = dataSource.getConnection();
            Statement statement = connection.createStatement();
            statement.execute("update AccountInfo set balance = balance + 1 where id = 1");
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

  从上可以看出,通过数据源访问跟 jdbc 方式相比,省略了实例化数据库连接驱动 Driver 驱动这一步,此外调用 getConnection() 获取连接的时候,并没有传用户名和密码,说明 dataSource 把这些配置信息都管理起来了。总结来说,DruidDataSource 管理了数据库连接的一些配置信息,还帮助我们创建了连接驱动 Driver,剩下的逻辑就跟 jdbc 一毛一样了。

1.3 通用配置

DruidDataSource大部分属性都是参考DBCP的,个别配置的语意有所区别。如果你原来就是使用DBCP,迁移是十分方便的。

com.alibaba.druid.pool.DruidDataSource

配置 缺省值 说明
name 如果存在多个数据源,监控的时候可以通过名字来区分开来
url 连接数据库的url,不同数据库不一样
username 连接数据库的用户名
password 连接数据库的密码。
如果不希望密码直接写在配置文件中,可以使用ConfigFilter
driverClassName 这一项可配可不配,根据url自动识别
maxActive 8 最大连接池数量
initialSize 0 初始化时建立连接的个数
minIdle 最小连接池数量
maxWait 获取连接时最大等待时间,单位毫秒。
filters 监控统计拦截的filters
timeBetweenEvictionRunsMillis 间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
minEvictableIdleTimeMillis 一个连接在池中最小生存的时间,单位是毫秒
maxEvictableIdleTimeMillis 一个连接在池中最大生存的时间,单位是毫秒
validationQuery 用来检测连接是否有效的sql。
如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用
validationQueryTimeout 检测连接是否有效的超时时间,单位:秒
testWhileIdle 如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效
testOnBorrow true 申请连接时执行validationQuery检测连接是否有效,配置后会降低性能
testOnReturn false 归还连接时执行validationQuery检测连接是否有效,配置后会降低性能
keepAlive false 连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作。
poolPreparedStatements false 是否缓存preparedStatement
maxOpenPreparedStatements -1 是否要启用PSCache。当大于0时,启用。
asyncInit false
connectionInitSqls 物理连接初始化的时候执行的sql

1.4 内置Filter的别名

在META-INF/druid-filter.properties文件中配置Filter的别名。

Filter类名 别名
default com.alibaba.druid.filter.stat.StatFilter
stat com.alibaba.druid.filter.stat.StatFilter
mergeStat com.alibaba.druid.filter.stat.MergeStatFilter
encoding com.alibaba.druid.filter.encoding.EncodingConvertFilter
log4j com.alibaba.druid.filter.logging.Log4jFilter
log4j2 com.alibaba.druid.filter.logging.Log4j2Filter
slf4j com.alibaba.druid.filter.logging.Slf4jLogFilter
commonlogging com.alibaba.druid.filter.logging.CommonsLogFilter
wall com.alibaba.druid.wall.WallFilter

二、相关配置

2.1 添加依赖

首先我们需要在应用的 pom.xml 文件中添加上 Druid 数据源依赖,可以从 Maven 仓库官网 中获取。



<dependency>
	<groupId>com.alibabagroupId>
	<artifactId>druid-spring-boot-starterartifactId>
	<version>1.1.10version>
dependency>



<dependency>
	<groupId>mysqlgroupId>
	<artifactId>mysql-connector-javaartifactId>
	<scope>runtimescope>
dependency>


<dependency>
	<groupId>org.springframework.bootgroupId>
	<artifactId>spring-boot-starter-log4j2artifactId>
dependency>


<dependency>
	<groupId>org.mybatis.spring.bootgroupId>
	<artifactId>mybatis-spring-boot-starterartifactId>
	<version>2.0.1version>
	<exclusions>
		
		<exclusion>
			<groupId>com.zaxxergroupId>
			<artifactId>HikariCPartifactId>
		exclusion>
	exclusions>
dependency>

2.2 配置数据源相关属性

2.2.1 配置Druid 数据源

  从 Spring Boot 2.0 开始,默认使用 com.zaxxer.hikari.HikariDataSource 数据源,但其中有一个十分重要的条件,如下图。

SpringBoot集成DruidDataSource实现监控 SQL 性能_第2张图片

   @ConditionalOnMissingBean(DataSource.class) 的含义是:当容器中没有 DataSource 时,Spring Boot 才会使用 HikariCP 作为其默认数据源。 也就是说,若向容器中添加 Druid 数据源类的对象时,Spring Boot 就会使用 Druid 作为其数据源,而不再使用 HikariCP。在配置文件 application.yml 中添加以下数据源配置,它们会与与 Druid 数据源中的属性进行绑定,如下所示:

spring:
  datasource:
    username: root
    password: root
    # driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=true
    platform: mysql
    type: com.alibaba.druid.pool.DruidDataSource

com.alibaba.druid.pool.DruidDataSource 基本配置参数,如下所示:

配置 说明
jdbcUrl 连接数据库的url,多数据源时使用,不同数据库不一样
url 连接数据库的url,单数据源时使用,不同数据库不一样
username 连接数据库的用户名
password 连接数据库的密码
driverClassName 这一项可配可不配,不配置时druid会根据url自动识别dbType,然后选择相应的driverClassName
connectionInitSqls 连接初始化的时候执行的sql
exceptionSorter 当数据库抛出一些不可恢复的异常时,抛弃连接

注:配置类创建 Druid 数据源对象时,应该尽量避免将数据源信息硬编码到代码中,而应该通过@ConfigurationProperties(“spring.datasource”) 注解,将 Druid 数据源对象的属性与配置文件中的以“spring.datasource”开头的配置进行绑定。

至此,我们就已经将数据源从 HikariCP 切换到了 Druid 了。

2.2.2 Druid 数据源测试

当数据源配置之后,我们需要来验证数据源类型是否为 Druid 以及是否能正常获取数据库连接、访问数据库,代码如下。

import com.alibaba.druid.pool.DruidDataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.*;

/**
 * 把今天最好的表现当作明天最新的起点..~
 * 

* Today the best performance as tomorrow the newest starter! * * @类描述: 数据源测试,测试 spring.datasource.xx 的 druid 属性配置是否正常,数据库是否能连接上等等 * @author: 独泪了无痕 * @创建时间: 2023-02-28 00:48 * @版本: V 1.0.1 * @since: JDK 1.8 */ @RunWith(SpringRunner.class) @SpringBootTest public class DataSourceTest { /** * Spring Boot 默认已经配置好了数据源,可以直接 DI 注入然后使用即可 */ @Resource private DataSource dataSource; @Test public void contextLoads() throws SQLException { System.out.println("数据源>>>>>>" + dataSource.getClass()); Connection connection = dataSource.getConnection(); System.out.println("连接>>>>>>>>>" + connection); System.out.println("连接地址>>>>>" + connection.getMetaData().getURL()); if (dataSource instanceof DruidDataSource) { DruidDataSource druidDataSource = (DruidDataSource) dataSource; System.out.println("druidDataSource 数据源最大连接数:" + druidDataSource.getMaxActive()); System.out.println("druidDataSource 数据源初始化连接数:" + druidDataSource.getInitialSize()); System.out.println("version=" + druidDataSource.getVersion()); System.out.println("name=" + druidDataSource.getName()); } connection.close(); } }

2.3 自定义 Druid 数据源

2.3.1 Druid 数据源参数配置

  如同以前 c3p0、dbcp 数据源可以设置数据源连接初始化大小、最大连接数、等待时间、最小连接数 等一样,Druid 数据源同理可以进行设置。Druid Spring Boot Starter 配置属性的名称完全遵照 Druid,可以通过 Spring Boot 配置文件来配置Druid数据库连接池和监控,如果没有配置则使用默认值,如下在 application.yml 配置相关属性:

spring:
  datasource:
    # druid 数据源专有配置,对应的是 com.alibaba.druid.pool.DruidDataSource 中的属性
    druid:
      # 数据源名称:当存在多个数据源时,设置名字可以很方便的来进行区分,默认自动生成名称,格式是:"DataSource-" + System.identityHashCode(this)
      name: druid-db1
      # 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时,默认0
      initialSize: 10
      # 最大连接池数量,默认8
      maxActive: 200
      # 最小连接池数量
      minIdle: 10
      # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁
      maxWait: 60000
      # 1)Destroy线程会检测连接的间隔时间 2)testWhileIdle的判断依据(详细看testWhileIdle属性的说明)
      timeBetweenEvictionRunsMillis: 60000
      # 设置连接最小可收回空闲时间(毫秒),默认为 1000L * 60L * 30L
      minEvictableIdleTimeMillis: 300000
      # 设置连接最大可收回空闲时间(毫秒),默认为 1000L * 60L * 60L * 7
      maxEvictableIdleTimeMillis: 900000
      # 用来检测连接是否有效的sql,求是一个查询语句。默认为null,此时testOnBorrow、testOnReturn、testWhileIdle都不会其作用
      validationQuery: SELECT 1 FROM DUAL
      # 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效,默认为true
      testWhileIdle: true
      # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能,默认false
      testOnBorrow: false
      # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能,默认false
      testOnReturn: false
      # 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle,在mysql下建议关闭。
      poolPreparedStatements: true
      # 要启用PSCache,必须配置大于0,当大于0时, poolPreparedStatements自动触发修改为true,
      # 在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
      maxOpenPreparedStatements: 20
      # 连接池中的 minIdle 数量以内的连接,空闲时间超过 minEvictableIdleTimeMillis,则会执行 keepAlive 操作
      keepAlive: true
      # 每个连接大小的最大池准备语句数
      maxPoolPreparedStatementPerConnectionSize: 20
      # 是否使用全局数据源统计,默认false
      useGlobalDataSourceStat: true
      # 连接属性
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
      # 配置监控统计的内置过滤器:
      # stat-监控统计(必须配置,否则监控不到sql)
      # wall-防御sql注入
      # log4j2-日志记录框架(值与应用中的日志框架保持一致,如 log4j、log4j、logback、slf4j)
      filters: stat,wall,log4j2

现在可以重新测试 Druid 数据源,查看配置文件中的参数是否已经生效,也可以直接 Debug 查看 DruidDataSource 的属性。

2.3.2 配置 Druid 数据源监控

  这个过滤器的作用就是统计 web 应用请求中所有的数据库信息,比如 发出的 sql 语句,sql 执行的时间、请求次数、请求的 url 地址、以及seesion 监控、数据库表的访问次数 等等。可以通过 spring.datasource.druid.filters=stat,wall,log4j ...的方式来启用相应的内置Filter,不过这些Filter都是默认配置。如果默认配置不能满足需求,可以放弃这种方式,通过配置文件来配置Filter,下面是例子。

########## JDBC基本配置 ##########

spring.datasource.username=developer
# spring.datasource.druid.username
spring.datasource.password=developer
# mysql的连接驱动
# spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# spring.datasource.druid.url
spring.datasource.url=jdbc:mysql://localhost:3306/opensource?useUnicode=true&characterEncoding=UTF-8&useSSL=true
# 数据库类型
spring.datasource.platform=mysql
# 指定数据源类型
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

########## druid数据源专有配置,对应的是com.alibaba.druid.pool.DruidDataSource中的属性
# 配置监控统计的内置过滤器:
spring.datasource.druid.filters=stat,wall,slf4j

##########自定义过滤器配置:stat、slf4j、log4j、log4j2、commons-log、wal
# 内置Filter都是默认配置,无法满足需求时,则可以自定义Filter,自定义的过滤器默认都是没有开启的

# 开启DruidDataSource的状态监控,必须配置,否则监控不到sql
spring.datasource.druid.filter.stat.enabled=true
spring.datasource.druid.filter.stat.db-type=mysql
# 开启慢sql监控,超过2s就认为是慢sql,记录到日志中
spring.datasource.druid.filter.stat.log-slow-sql=true
spring.datasource.druid.filter.stat.slow-sql-millis=2000

########## 日志监控,使用slf4j进行日志输出

spring.datasource.druid.filter.slf4j.enabled=true
spring.datasource.druid.filter.slf4j.statement-log-error-enabled=true
spring.datasource.druid.filter.slf4j.statement-create-after-log-enabled=false
spring.datasource.druid.filter.slf4j.statement-close-after-log-enabled=false
spring.datasource.druid.filter.slf4j.result-set-open-after-log-enabled=false
spring.datasource.druid.filter.slf4j.result-set-close-after-log-enabled=false

# 防火墙过滤器,防御sql注入
spring.datasource.druid.filter.wall.enabled=true
# 不允许删除数据
spring.datasource.druid.filter.wall.config.delete-allow=false

##########配置WebStatFilter,用于采集web关联监控的数据##########

# 是否启用StatFilter默认值false
spring.datasource.druid.web-stat-filter.enabled=true
# 过滤所有url
spring.datasource.druid.web-stat-filter.url-pattern=/*
# 排除一些不必要的url
spring.datasource.druid.web-stat-filter.exclusions="*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
# 开启session统计功能
spring.datasource.druid.web-stat-filter.session-stat-enable=true
# session的最大个数,默认100
spring.datasource.druid.web-stat-filter.session-stat-max-count=1000
# 使得druid能够知道当前的session的用户是谁
# spring.datasource.druid.web-stat-filter.principal-session-name=
# 如果你的user信息保存在cookie中,你可以配置principalCookieName,使得druid知道当前的user是谁
# spring.datasource.druid.web-stat-filter.principal-cookie-name=
# 配置profileEnable能够监控单个url调用的sql列表
# spring.datasource.druid.web-stat-filter.profile-enable=

########## 配置StatViewServlet(监控页面),用于展示Druid的统计信息 ##########

# 是否启用StatViewServlet(监控页面)默认值为false
spring.datasource.druid.stat-view-servlet.enabled=true
# 访问内置监控页面的路径,内置监控页面的首页是/druid/index.html
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
# 不允许清空统计数据,重新计算
spring.datasource.druid.stat-view-servlet.reset-enable=false
# 配置监控页面访问用户名
spring.datasource.druid.stat-view-servlet.loginUsername=root
# 配置监控页面访问密码
spring.datasource.druid.stat-view-servlet.loginPassword=123
# 允许访问的地址,如果allow没有配置或者为空,则允许所有访问
spring.datasource.druid.stat-view-servlet.allow=127.0.0.1
# 拒绝访问的地址,deny优先于allow,如果在deny列表中,就算在allow列表中,也会被拒绝
spring.datasource.druid.stat-view-servlet.deny=

不想使用内置的 Filters,要想使自定义 Filter 配置生效需要将对应 Filter 的 enabled 设置为 true ,Druid Spring Boot Starter 默认禁用 StatFilter,可以将其 enabled 设置为 true 来启用它。

2.3.3 Druid 内置 Filter 配置

  Druid Spring Boot Starter 对以下 Druid 内置 Filter,都提供了默认配置StatFilter、WallFilter、ConfigFilter、EncodingConvertFilter、Slf4jLogFilter、Log4jFilter、Log4j2Filter、CommonsLogFilter。我们可以通过 spring.datasource.druid.filters=stat,wall … 的方式来启用相应的内置 Filter,不过这些 Filter 使用的都是默认配置。如果默认配置不能满足我们的需求,我们还可以在配置文件使用 spring.datasource.druid.filter.* 对这些 Filter 进行配置,示例代码如下。

#  ####################################################### Druid 监控配置信息  ##########################################
spring:
  datasource:
    druid:
     # 对配置已开启的 filters 即 stat(sql 监控)  wall(防火墙)
      filter:
        #配置StatFilter (SQL监控配置)
        stat:
          enabled: true                                                               #开启 SQL 监控
          slow-sql-millis: 1000                                                       #慢查询
          log-slow-sql: true                                                          #记录慢查询 SQL
 
        #配置WallFilter (防火墙配置)
        wall:
          enabled: true                                                               #开启防火墙
          config:
            update-allow: true                                                        #允许更新操作
            drop-table-allow: false                                                   #禁止删表操作
            insert-allow:  true                                                       #允许插入操作
            delete-allow: true                                                        #删除数据操作

在配置 Druid 内置 Filter 时,需要先将对应 Filter 的 enabled 设置为 true,否则内置 Filter 的配置不会生效。

2.4 Druid 内置监控页面

  Druid 数据源具有监控的功能,并提供了一个 web 界面方便用户查看,类似安装路由器时,人家也提供了一个默认的 web 页面。首先需要设置 Druid 的后台管理页面的属性,比如登录账号、密码等。

2.4.1 开启 Druid 内置监控

  Druid 内置提供了一个名为 StatViewServlet 的 Servlet,这个 Servlet 可以开启 Druid 的内置监控页面功能, 展示 Druid 的统计信息,它的主要用途可以提供监控信息展示的 html 页面、提供监控信息的 JSON API等。StatViewServlet 是一个标准的 javax.servlet.http.HttpServlet,想要开启 Druid 的内置监控页面,我们可以在配置类中,通过 ServletRegistrationBean 将 StatViewServlet 注册到容器中,来开启 Druid 的内置监控页面。

spring:
  datasource:
    druid:
      stat-view-servlet:
        enabled: true                   # 启用StatViewServlet
        url-pattern: /druid/*           # 访问内置监控页面的路径,内置监控页面的首页是/druid/index.html
        reset-enable: false             # 不允许清空统计数据,重新计算
        loginUsername: root             # 配置监控页面访问用户
        login-password: 123             # 配置监控页面访问密码
        allow: 127.0.0.1                # 允许访问的地址,如果allow没有配置或者为空,则允许所有访问
        deny:                           # 拒绝访问的地址,deny优先于allow,如果在deny列表中,就算在allow列表中,也会被拒绝

  启动 Spring Boot 项目后,浏览器访问 http://localhost:8080/druid/login.html ,即可访问 Druid 的内置监控页面的登录页,如下图所示。
SpringBoot集成DruidDataSource实现监控 SQL 性能_第3张图片

  在登录页中,分别输入自定义的用户名和密码,用户和密码对应我们的配置文件中设置的用户名(loginUsername)和密码(loginPassword)。输入用户名密码登录进去可以看到里面有很多监控,如下所示:

SpringBoot集成DruidDataSource实现监控 SQL 性能_第4张图片

2.4.2 数据源

  数据源页面是当前DataSource配置的基本信息,上述配置的Filter可以在里面找到,如果没有配置Filter(一些信息会无法统计,例如“SQL监控”,会无法获取JDBC相关的SQL执行信息)
SpringBoot集成DruidDataSource实现监控 SQL 性能_第5张图片

2.4.3 SQL 监控

  Druid 内置提供了一个 StatFilter,通过它可以开启 Druid 的 SQL 监控功能,对 SQL 进行监控。SQL监控页面统计了所有SQL语句的执行情况,执行时间、读取行数、更新行数都有区间分布,如下图所示。

SpringBoot集成DruidDataSource实现监控 SQL 性能_第6张图片

  StatFilter 的别名是 stat,这个别名的映射配置信息保存在 druid-xxx.jar!/META-INF/druid-filter.properties 中。Druid 官方文档-配置_StatFilter 中给出了在 Spring 中配置该别名(stat)开启 Druid SQL 监控的方式,配置如下。

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
	... ...
	<property name="filters" value="stat" />
bean>

  根据以上配置我们可以看出,只要在 dataSource 的 Bean 中添加一个取值为“stat”的“filters”属性,就能开启 Druid SQL 监控。

spring:
  datasource:
    druid:
      filter:
        stat:
          enabled: true         # 开启DruidDataSource状态监控
          db-type: mysql        # 数据库的类型
          log-slow-sql: true    # 开启慢SQL记录功能
          slow-sql-millis: 2000 # 默认3000毫秒,这里超过2s,就是慢,记录到日志

2.4.4 Web 应用

  Druid 还内置提供了一个名为 WebStatFilter 的过滤器,它可以用来监控与采集 web-jdbc 关联监控的数据。根据 Druid 官方文档-配置_配置WebStatFilter,想要开启 Druid 的 Web-JDBC 关联监控,只需要将 WebStatFilter 配置在 Web 应用中的 WEB-INF/web.xml 中即可,web.xml 配置如下。

<filter>
    <filter-name>DruidWebStatFilterfilter-name>
    <filter-class>com.alibaba.druid.support.http.WebStatFilterfilter-class>
    <init-param>
        <param-name>exclusionsparam-name>
        <param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*param-value>
    init-param>
filter>
<filter-mapping>
    <filter-name>DruidWebStatFilterfilter-name>
    <url-pattern>/*url-pattern>
filter-mapping>

  Spring Boot 项目中是没有 WEB-INF/web.xml 的,但是我们可以在配置类中,通过 FilterRegistrationBean 将 WebStatFilter 注入到容器中,来开启 Druid 的 Web-JDBC 关联监控。

spring:
  datasource:
    druid:
      web-stat-filter:
        enabled: true                   # 启动 StatFilter
        url-pattern: /*                 # 过滤所有url
        exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"     # 排除一些不必要的url
        session-stat-enable: true       # 开启session统计功能
        session-stat-max-count: 1000    # session的最大个数,默认100

  浏览器访问系统的任意页面,然后再访问 Druid 的内置监控页面,切换到 Web 应用模块,可以看到 Druid 的 Web 监控已经开启,与此同时,URI 监控和 Session 监控也都被开启。

2.4.5 URL 监控

URL监控页面统计了所有Controller接口的访问以及执行情况。这里可以很清晰的看到,每个url涉及到的数据库执行的信息。

在这里插入图片描述

2.4.6 Session 监控

Session监控页面 可以看到当前的session状况,创建时间、最后活跃时间、请求次数、请求时间等详细参数。

2.4.7 Spring 监控

Spring 监控页面,利用aop 对指定接口的执行时间,jdbc数进行记录

在这里插入图片描述

访问之后 Spring 监控默认是没有数据的,Spring 监控是用于通过 aop 切面对指定类及方法进行监控,这需要导入SprngBoot的AOP的Starter,如下所示:


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-aopartifactId>
dependency>

然后在 application.yml 配置 Spring 监控 AOP 切入点,如 com.springboot.template.dao.*,配置多个英文逗号分隔,例如如下所示:

spring:
  datasource:
    druid:
      aop-patterns:"com.springboot.template.dao.*" # Spring监控AOP切入点,多个时用英文逗号分隔

SpringBoot集成DruidDataSource实现监控 SQL 性能_第7张图片

2.4.8 SQL 防火墙

  Druid 内置提供了一个 WallFilter,使用它可以开启防火墙功能,防御 SQL 注入攻击。WallFilter 的别名是 wall,这个别名映射配置信息保存在 druid-xxx.jar!/META-INF/druid-filter.properties 中。Druid 官方文档-配置 wallfilter 中给出了在 Spring 中使用该别名(wall)开启防火墙的方式,配置如下。

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
    ... ...
    <property name="filters" value="wall" />
bean>

  WallFilter 可以结合其他 Filter 一起使用,例如:

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
    ...
    <property name="filters" value="wall,stat"/>
bean>

  根据以上配置我们可以看出,只要在 dataSource 的 Bean 中添加一个取值为“wall”的“filters”属性,就能开启 Druid 的防火墙功能,因此我们只需要在配置类中为 dataSource 的 filters 属性再添加一个“wall”即可(多个属性值之间使用逗号“,”隔开),代码如下。

spring:
  datasource:
    druid:
      filters: stat,wall,slf4j

  访问 Druid 的内置监控页面,切换到 SQL 防火墙,可以看到 Druid 防火墙已经开启。SQL防火墙页面 druid提供了黑白名单的访问,可以清楚的看到sql防护情况。

2.4.9 JSON API

JSONAPI 页面 通过api的形式访问Druid的监控接口,api接口返回Json形式数据。

三、自定义DruidDataSource

3.1 去广告

  访问监控页面的时候,可能会在页面底部(footer)看到内置的广告,如下所示。这是因为引入的druid的 druid-x.y.z.jar 包中的 common.js(里面有一段js代码是给页面的footer追加广告的)。如果想去掉,有两种方式:
SpringBoot集成DruidDataSource实现监控 SQL 性能_第8张图片

  • 直接手动注释这段代码

      如果是使用Maven,直接到本地仓库中,查找这个 druid-x.y.z.jar 包,然后用压缩工具打开,找到 support/http/resources/js/common.js,然后注释掉里面的代码 或者修改为自己的页脚内容。

    // this.buildFooter();
    

  • 使用过滤器过滤

    注册一个过滤器,过滤common.js的请求,使用正则表达式替换相关的广告内容,如下代码所示:

    @Configuration
    @ConditionalOnWebApplication
    @AutoConfigureAfter(DruidDataSourceAutoConfigure.class)
    @ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled",
    havingValue = "true", matchIfMissing = true)
    public class RemoveDruidAdConfig {
    
        /**
        * 除去页面底部的广告
        */
        @Bean
        public FilterRegistrationBean removeDruidAdFilterRegistrationBean(DruidStatProperties properties) {
    
            // 获取web监控页面的参数
            DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
            // 提取common.js的配置路径
            String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
            String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
    
            final String filePath = "support/http/resources/js/common.js";
    
            //创建filter进行过滤
            Filter filter = new Filter() {
                @Override
                public void init(FilterConfig filterConfig) throws ServletException {}
    
                @Override
                public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
                    chain.doFilter(request, response);
                    // 重置缓冲区,响应头不会被重置
                    response.resetBuffer();
                    // 获取common.js
                    String text = Utils.readFromResource(filePath);
                    // 正则替换banner, 除去底部的广告信息
                    text = text.replaceAll("
    "
    , ""); text = text.replaceAll("powered.*?shrek.wang", ""); response.getWriter().write(text); } @Override public void destroy() {} }; FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(filter); registrationBean.addUrlPatterns(commonJsPattern); return registrationBean; } }

两种方式都可以,建议使用的是第一种,从根源解决。

3.2 取 Druid 的监控数据

  Druid 的监控数据不仅可以在页面上查看,在 开启 StatFilter 后 ,也可以通过 DruidStatManagerFacade 类进行获取。其中 getDataSourceStatDataList 方法可以获取所有数据源的监控数据,除此之外 DruidStatManagerFacade 还提供了一些其他方法,可以按需选择使用。

@RestController
@RequestMapping(value = "/druid")
public class DruidStatController {
  /**
     * Spring Boot 默认已经配置好了数据源,程序员可以直接 DI 注入然后使用即可
     */
    @Resource
    private DataSource dataSource;
    
    /**
     * 获取 druid 数据监控信息,其中统计了所有数据源的所有详细信息。
     */
    @GetMapping("/stat")
    public List<Map<String, Object>> druidStat(){
        // 获取数据源的监控数据
        return DruidStatManagerFacade.getInstance().getDataSourceStatDataList();
    }
    
    /**
     * 使用 DruidDataSource API 获取具体数据源的指定的监控信息。
     */
    @GetMapping("/druidDataSource")
    public Map<String, Object> druidDataSource() {
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("class", dataSource.getClass());
        if (dataSource instanceof DruidDataSource) {
            DruidDataSource druidDataSource = (DruidDataSource) dataSource;
            dataMap.put("version", druidDataSource.getVersion());
            dataMap.put("name", druidDataSource.getName());
            dataMap.put("initialSize", druidDataSource.getInitialSize());
            dataMap.put("maxActive", druidDataSource.getMaxActive());
            dataMap.put("minIdle", druidDataSource.getMinIdle());
            dataMap.put("activeCount", druidDataSource.getActiveCount());
            dataMap.put("activePeak", druidDataSource.getActivePeak());
            dataMap.put("activePeakTime", druidDataSource.getActivePeakTime());
            dataMap.put("poolingCount", druidDataSource.getPoolingCount());
            dataMap.put("poolingPeak", druidDataSource.getPoolingPeak());
            dataMap.put("poolingPeakTime", druidDataSource.getPoolingPeakTime());
        }
        return dataMap;
    }
}

获取 druid 数据监控信息

参数名 描述
ActiveCount 当前连接池中活跃连接数
ActivePeak 连接池中活跃连接数峰值
ActivePeakTime 活跃连接池峰值出现的时间
BlobOpenCount Blob 打开数
ClobOpenCount Clob 打开数
CommitCount 提交数
DbType 数据库类型
DefaultAutoCommit 是否默认提交
DiscardCount 放弃/丢弃的个数
DriverClassName 驱动类
ErrorCount 错误数
ExceptionSorterClassName 异常分类器类名
ExecuteBatchCount 执行批次计数
ExecuteCount 执行总数
ExecuteQueryCount 执行查询计数
ExecuteUpdateCount 执行更新计数
FailFast 是否快速失败
FilterClassNames 过滤器类名称
Identity
InitGlobalVariants 初始全局变量
InitVariants 初始变量
InitialSize 连接池建立时创建的初始化连接数
KeepAlive 是否保持活跃
KeepAliveCheckCount 保持活动检查计数
LogDifferentThread 记录不同的线程
LogicCloseCount 产生的逻辑连接关闭总数
LogicConnectCount 产生的逻辑连接建立总数
LogicConnectErrorCount 产生的逻辑连接出错总数
LoginTimeout 数据库客户端登录超时时间
MaxActive 连接池中最大的活跃连接数
MaxEvictableIdleTimeMillis 最大可收回空闲时间(毫秒)
MaxWait 最大等待时间
MaxWaitThreadCount 最大等待线程计数
MinEvictableIdleTimeMillis 最小可执行时间Mill
MinIdle 连接池中最小的活跃连接数
Name 数据源名称
NotEmptyWaitCount 获取连接时最多等待多少次
NotEmptyWaitMillis 获取连接时最多等待多长时间,毫秒为单位
PSCacheAccessCount PSCache访问总数
PSCacheHitCount PSCache命中次数
PSCacheMissCount PSCache未命中次数
PhysicalCloseCount 产生的物理关闭总数
PhysicalConnectCount 产生的物理连接建立总数
PhysicalConnectErrorCount 产生的物理连接失败总数
PoolPreparedStatements 预编译准备语句
PoolingCount 当前连接池中的连接数
PoolingPeak 连接池中连接数的峰值
PoolingPeakTime 连接池数目峰值出现的时间
PreparedStatementClosedCount 预编译准备语句关闭计数
PreparedStatementOpenCount 准备语句打开计数
QueryTimeout 查询超时数
RecycleErrorCount 回收错误计数
RemoveAbandoned 已删除
RollbackCount 回滚数
StartTransactionCount 事务开始的个数
TestOnBorrow
TestOnReturn
TestWhileIdle
TransactionQueryTimeout 事务查询超时数
URL 数据库连接地址
UseUnfairLock 使用不公平锁定
UserName 数据库连接账号
WaitThreadCount 当前等待获取连接的线程数

3.3 使用log4j2进行日志输出

  1. log4j2.xml文件中的日志配置

    
    <configuration status="OFF">
        <appenders>
            <Console name="Console" target="SYSTEM_OUT">
                
                <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
                <PatternLayout pattern="[%d{HH:mm:ss.SSS}] %-5level %class{36} %L %M - %msg%xEx%n"/>
            Console>
            
            
            <RollingFile name="RollingFileDebug" fileName="./logs/debug.log"
                         filePattern="logs/$${date:yyyy-MM}/debug-%d{yyyy-MM-dd}-%i.log.gz">
                <Filters>
                    <ThresholdFilter level="DEBUG"/>
                    <ThresholdFilter level="INFO" onMatch="DENY" onMismatch="NEUTRAL"/>
                Filters>
                <PatternLayout
                        pattern="[%d{yyyy-MM-dd HH:mm:ss}] %-5level %class{36} %L %M - %msg%xEx%n"/>
                <Policies>
                    <SizeBasedTriggeringPolicy size="500 MB"/>
                    <TimeBasedTriggeringPolicy/>
                Policies>
            RollingFile>
            
            <RollingFile name="RollingFileInfo" fileName="./logs/info.log"
                         filePattern="logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log.gz">
                <Filters>
                    
                    <ThresholdFilter level="INFO"/>
                    <ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
                Filters>
                <PatternLayout
                        pattern="[%d{yyyy-MM-dd HH:mm:ss}] %-5level %class{36} %L %M - %msg%xEx%n"/>
                <Policies>
                    <SizeBasedTriggeringPolicy size="500 MB"/>
                    <TimeBasedTriggeringPolicy/>
                Policies>
            RollingFile>
            
            <RollingFile name="RollingFileWarn" fileName="./logs/warn.log"
                         filePattern="logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log.gz">
                <Filters>
                    <ThresholdFilter level="WARN"/>
                    <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
                Filters>
                <PatternLayout
                        pattern="[%d{yyyy-MM-dd HH:mm:ss}] %-5level %class{36} %L %M - %msg%xEx%n"/>
                <Policies>
                    <SizeBasedTriggeringPolicy size="500 MB"/>
                    <TimeBasedTriggeringPolicy/>
                Policies>
            RollingFile>
            
            <RollingFile name="RollingFileError" fileName="./logs/error.log"
                         filePattern="logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log.gz">
                <ThresholdFilter level="ERROR"/>
                <PatternLayout
                        pattern="[%d{yyyy-MM-dd HH:mm:ss}] %-5level %class{36} %L %M - %msg%xEx%n"/>
                <Policies>
                    <SizeBasedTriggeringPolicy size="500 MB"/>
                    <TimeBasedTriggeringPolicy/>
                Policies>
            RollingFile>
            
            <RollingFile name="druidSqlRollingFile" fileName="./logs/druid-sql.log"
                         filePattern="logs/$${date:yyyy-MM}/api-%d{yyyy-MM-dd}-%i.log.gz">
                <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] %-5level %L %M - %msg%xEx%n"/>
                <Policies>
                    <SizeBasedTriggeringPolicy size="500 MB"/>
                    <TimeBasedTriggeringPolicy/>
                Policies>
            RollingFile>
        appenders>
        <loggers>
            <root level="DEBUG">
                <appender-ref ref="Console"/>
                <appender-ref ref="RollingFileInfo"/>
                <appender-ref ref="RollingFileWarn"/>
                <appender-ref ref="RollingFileError"/>
                <appender-ref ref="RollingFileDebug"/>
            root>
            
            <logger name="druid.sql.Statement" level="debug" additivity="false">
                <appender-ref ref="druidSqlRollingFile"/>
            logger>
            <logger name="druid.sql.Statement" level="debug" additivity="false">
                <appender-ref ref="druidSqlRollingFile"/>
            logger>
            
            <Logger name="org.apache.catalina.startup.DigesterFactory" level="error" />
            <Logger name="org.apache.catalina.util.LifecycleBase" level="error" />
            <Logger name="org.apache.coyote.http11.Http11NioProtocol" level="warn" />
            <logger name="org.apache.sshd.common.util.SecurityUtils" level="warn"/>
            <Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="warn" />
            <Logger name="org.crsh.plugin" level="warn" />
            <logger name="org.crsh.ssh" level="warn"/>
            <Logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="error" />
            <Logger name="org.hibernate.validator.internal.util.Version" level="warn" />
            <logger name="org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration" level="warn"/>
            <logger name="org.springframework.boot.actuate.endpoint.jmx" level="warn"/>
            <logger name="org.thymeleaf" level="warn"/>
        loggers>
    configuration>
    
  2. 配置 application.properties

    # 配置日志输出
    spring.datasource.druid.filter.slf4j.enabled=true
    spring.datasource.druid.filter.slf4j.statement-create-after-log-enabled=false
    spring.datasource.druid.filter.slf4j.statement-close-after-log-enabled=false
    spring.datasource.druid.filter.slf4j.result-set-open-after-log-enabled=false
    spring.datasource.druid.filter.slf4j.result-set-close-after-log-enabled=false
    

你可能感兴趣的:(数据操作,SpringBoot,spring,boot,sql,java)