连接池
HikariCP
HikariCP来自于日语,意为:光。 它本身作为 SpringBoot 2.X 默认的连接池。
"Fast"源于深度优化和大量小改进
- 字节码级别优化(很多方法通过 JavaAssist 生成)
- 大量小改进:用 FastStatementList 代替 ArrayList、无锁集合 ConcurrentBag 、 代理类的优化(用 invokestatic 代替 invokevirtual)
SpringBoot 中的配置
- 默认使用 HikariCP
- application.properties 配置格式 spring.datasource.hikari.*
- 如果是 SpringBoot 1.x 需要在 maven 中移除默认的 tomcat-jdbc 依赖,并且引入 HikariCP 的 Maven 依赖,并且在
application.properties
配置文件中指定默认的连接池类型spring.datasource.type=com.zaxxer.hikari.HikariDataSource
# 暴露Actuator
management.endpoints.web.exposure.include=*
# 如果你的终端支持ANSI,设置彩色输出会让日志更具可读性
# DETECT:会检查终端是否支持ANSI,是的话就采用彩色输出(推荐项)
# ALWAYS:总是使用ANSI-colored格式输出,若终端不支持的时候,会有很多干扰信息,不推荐
spring.output.ansi.enabled=ALWAYS
# 通用配置
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.username=sa
spring.datasource.password=
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver #(可选),因为springboot会根据spring.datasource.url 自动判断,选择合适的驱动。
# 连接池配置
spring.datasource.hikari.maximumPoolSize=5
spring.datasource.hikari.minimumIdle=5
spring.datasource.hikari.idleTimeout=600000
spring.datasource.hikari.connectionTimeout=30000
spring.datasource.hikari.maxLifetime=1800000
常用配置说明
name | description | default_value |
---|---|---|
maximumPoolSize | 此属性控制允许池达到的最大大小,包括空闲和使用中的连接。基本上,此值将确定到数据库后端的最大实际连接数。合理的值最好由您的执行环境确定。当池达到此大小并且没有空闲连接可用时,对 getConnection()的调用将 connectionTimeout 在超时之前最多阻塞毫秒。 | 10 |
minimumIdle | 此属性控制 HikariCP 尝试在池中维护的最小空闲连接数。如果空闲连接下降到该值以下,并且池中的总连接数少于 maximumPoolSize ,则 HikariCP 将尽最大努力快速而有效地添加其他连接。但是,为了获得最佳性能和对峰值需求的响应能力,我们建议不要设置此值,而应让 HikariCP 充当固定大小的连接池。 默认值:与 maximumPoolSize 相同 | 10 |
maximumPoolSize | ||
idleTimeout | 连接允许在池中闲置的最长时间 | 600000(10 分钟) |
connectionTimeout | 此属性控制客户端(即您)等待来自池的连接的最大毫秒数。如果超过此时间而没有可用的连接,则会抛出 SQLException。可接受的最低连接超时为 250 ms。 | 30000(30 秒) |
maxLifetime | 用来设置一个 connection 在连接池中的存活时间 | 1800000,即 30 分钟。如果设置为 0,表示存活时间无限大。如果不等于 0 且小于 30 秒则会被重置回 30 分钟。 |
更多配置 | 官网 |
Alibaba Druid
“Druid 连接池是阿 ⾥巴巴开源的数据库连接池项 ⽬。Druid 连接池为监控而生,内置强 ⼤的监控功能,监控特性不影响性能。功能强 ⼤,能防 SQL 注 ⼊,内置 Logging 能诊断 Hack 应用行为。”
WiKi
- 详细的监控
- ExceptionSorter,针对主流数据的返回码都有支持
- SQL 防注入
- 内置加密配置
- 众多扩展点,方便进行定制
SpringBoot 方式
引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>geektime.spring.data</groupId>
<artifactId>druid-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>druid-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<!-- 需要手动排除默认的连接池 HikariCP -->
<exclusions>
<exclusion>
<artifactId>HikariCP</artifactId>
<groupId>com.zaxxer</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 引入 druid-spring-boot-starter -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
spring.output.ansi.enabled=ALWAYS
spring.datasource.url=jdbc:h2:mem:foo
spring.datasource.username=sa
spring.datasource.password=n/z7PyA5cvcXvs8px8FVmBVpaRyNsvJb3X7YfS38DJrIg25EbZaZGvH4aHcnc97Om0islpCAPc3MqsGvsrxVJw==
spring.datasource.druid.initial-size=5
spring.datasource.druid.max-active=5
spring.datasource.druid.min-idle=5
# Filter配置
spring.datasource.druid.filters=conn,config,stat,slf4j
# 密码加密
spring.datasource.druid.connection-properties=config.decrypt=true;config.decrypt.key=${public-key}
spring.datasource.druid.filter.config.enabled=true
spring.datasource.druid.test-on-borrow=true
spring.datasource.druid.test-on-return=true
spring.datasource.druid.test-while-idle=true
# druid-spring-boot-starter 默认就会开启统计功能的filter 所以注释掉了
#spring.datasource.druid.filter.stat.enabled=true
# 开启慢sql日志 (默认是监控那些执行超过3秒的sql语句)
spring.datasource.druid.filter.stat.log-slow-sql=true
# 默认抓取超过100毫秒的sql语句
spring.datasource.druid.filter.stat.slow-sql-millis=100
public-key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALS8ng1XvgHrdOgm4pxrnUdt3sXtu/E8My9KzX8sXlz+mXRZQCop7NVQLne25pXHtZoDYuMh3bzoGj6v5HvvAQ8CAwEAAQ==
启动类
package geektime.spring.data.druiddemo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
@SpringBootApplication
@Slf4j
public class DruidDemoApplication implements CommandLineRunner {
@Autowired
private DataSource dataSource;
@Autowired
private JdbcTemplate jdbcTemplate;
public static void main(String[] args) {
SpringApplication.run(DruidDemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
log.info(dataSource.toString());
}
}
控制台输出 发现 Connections 中包含 5 个连接
CreateTime:"2020-01-07 13:33:11",
ActiveCount:0,
PoolingCount:5,
CreateCount:5,
DestroyCount:0,
CloseCount:0,
ConnectCount:0,
Connections:[
{ID:1409712092, ConnectTime:"2020-01-07 13:33:11", UseCount:0, LastActiveTime:"2020-01-07 13:33:11"},
{ID:1083759991, ConnectTime:"2020-01-07 13:33:11", UseCount:0, LastActiveTime:"2020-01-07 13:33:11"},
{ID:1135523296, ConnectTime:"2020-01-07 13:33:11", UseCount:0, LastActiveTime:"2020-01-07 13:33:11"},
{ID:36127072, ConnectTime:"2020-01-07 13:33:11", UseCount:0, LastActiveTime:"2020-01-07 13:33:11"},
{ID:1698182759, ConnectTime:"2020-01-07 13:33:11", UseCount:0, LastActiveTime:"2020-01-07 13:33:11"}
]
}
# 如果有慢sql会显示
2020-01-08 17:36:04.426 ERROR 2004 --- [ Thread-3] c.alibaba.druid.filter.stat.StatFilter : slow sql 209 millis. select id from foo where id = 1 for update[]
Druid Filter
- ⽤于定制连接池操作的各种环节
- 可以继承 FilterEventAdapter 以便 ⽅便地实现 Filter
package geektime.spring.data.druiddemo;
import com.alibaba.druid.filter.FilterChain;
import com.alibaba.druid.filter.FilterEventAdapter;
import com.alibaba.druid.proxy.jdbc.ConnectionProxy;
import lombok.extern.slf4j.Slf4j;
import java.util.Properties;
@Slf4j
public class ConnectionLogFilter extends FilterEventAdapter {
/**
* 复写的方法名字 代表 扩展点所在的位置
*/
@Override
public void connection_connectBefore(FilterChain chain, Properties info) {
log.info("BEFORE CONNECTION!");
}
@Override
public void connection_connectAfter(ConnectionProxy connection) {
log.info("AFTER CONNECTION!");
}
}
- 修改 META-INF/druid-fifilter.properties 增加 Filter 配置
druid.filters.conn=geektime.spring.data.druiddemo.ConnectionLogFilter
Spring 方式
引入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.0</version>
</dependency>
application.yml
server:
port: 8091
druid:
allow:
ip: 127.0.0.1
login:
user_name: root
password: root
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.31.201:3306/shop?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
initial-size: 1
min-idle: 1
max-idle: 5
max-wait-millis: 60000
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: true
testOnReturn: false
maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
DruidConfig
package com.xdclass.couponapp.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DruidConfig {
@Value("${druid.login.user_name}")
private String userName;
@Value("${druid.login.password}")
private String password;
@Bean(name = "default_datadatasource")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource(){
return new DruidDataSource();
}
@Bean
public ServletRegistrationBean druidServlet() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
servletRegistrationBean.setServlet(new StatViewServlet());
servletRegistrationBean.addUrlMappings("/druid/*");
Map<String, String> initParameters = new HashMap<>(3);
// 用户名
initParameters.put("loginUsername", userName);
// 密码
initParameters.put("loginPassword", password);
// 禁用HTML页面上的“Reset All”功能
initParameters.put("resetEnable", "false");
servletRegistrationBean.setInitParameters(initParameters);
return servletRegistrationBean;
}
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WebStatFilter());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
}
Druid 监控台
# http://localhost:8091/druid
root
root
Durid使用注意事项
- 没有特殊情况,不要在生产环境打开监控的Servlet,负载情况以日志输出为基准。
- 没有连接泄漏可能的情况下,不要开启removeAbandoned。(大量和使用ORM框架的情况下。)
- testXxx 的使用需要注意,性能开销比较大。
- 务必配置合理的超时时间。