spring boot项目19:RDBMS连接池

JAVA 8

Spring Boot 2.5.3

MySQL 5.7.21

Druid 1.2.6(2021-5-5发布)

---

 

目录

1、序章

2、使用默认HikariCP数据库

操作数据库

3、使用Druid

访问/druid/*端点

参考文档

 

1、序章

数据库连接池 使应用可以重复使用一个已经建立的数据库连接,从而提高操作数据库的性能。

在Java开发中,常见的数据库连接池如下:来自博客园

C3P0

DBCP

BoneCP

HikariCP

Druid

本文展示在Spring Boot应用中使用下面2种数据库连接池:

1)HikariCP、2)Druid

其中,HikariCP是S.B.应用默认的数据库连接池。

 

在S.B.官文的 4.11. Working with SQL Databases 中,提到支持的连接池如下:

HikariCP、 Tomcat pooling DataSource、Commons DBCP2、Oracle UCP。

首选HikariCP 是处于 性能和并发性(performance and concurrency) 的考虑。

Spring Boot uses the following algorithm for choosing a specific implementation:
1. We prefer HikariCP for its performance and concurrency. If HikariCP is available, we always
choose it.
2. Otherwise, if the Tomcat pooling DataSource is available, we use it.
3. Otherwise, if Commons DBCP2 is available, we use it.
4. If none of HikariCP, Tomcat, and DBCP2 are available and if Oracle UCP is available, we use it.

 

注,本文操作的目标数据库为 MySQL 5.7.21来自博客园

 

2、使用默认HikariCP数据库

新建Web项目 dbpool-hello,端口9000,使用Spring Data JPA操作数据库。

项目依赖:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-test</artifactId>
	<scope>test</scope>
</dependency>

<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
</dependency>
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<scope>runtime</scope>
</dependency>

 

项目配置——MySQL数据库连接池部分,spring.datasource.*

# MySQL on Ubuntu
spring.datasource.url=jdbc:mysql://mylinux:3306/db_example?serverTimezone=Asia/Shanghai
spring.datasource.username=springuser
spring.datasource.password=ThePassword
#spring.datasource.driver-class-name =com.mysql.jdbc.Driver # This is deprecated
spring.datasource.driver-class-name =com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
# 打开使用过程中执行的SQL语句
spring.jpa.show-sql=true

 

启动项目,可以看到Spring容器中有很多Bean有“DataSource”字符串:来自博客园

name=org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari, bean=org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari@7bebcd65
name=dataSource, bean=HikariDataSource (HikariPool-1)
name=org.springframework.boot.autoconfigure.jdbc.DataSourceJmxConfiguration$Hikari, bean=org.springframework.boot.autoconfigure.jdbc.DataSourceJmxConfiguration$Hikari@1e00bfe2
name=org.springframework.boot.autoconfigure.jdbc.DataSourceJmxConfiguration, bean=org.springframework.boot.autoconfigure.jdbc.DataSourceJmxConfiguration@4702e7a5
name=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$PooledDataSourceConfiguration, bean=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$PooledDataSourceConfiguration@6a2d867d
name=org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration$HikariPoolDataSourceMetadataProviderConfiguration, bean=org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration$HikariPoolDataSourceMetadataProviderConfiguration@e322ec9
name=hikariPoolDataSourceMetadataProvider, bean=org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration$HikariPoolDataSourceMetadataProviderConfiguration$$Lambda$623/777970377@4d0753c9
name=org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration, bean=org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration@73bb1337
name=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, bean=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration@685f5d0d
name=spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties, bean=org.springframework.boot.autoconfigure.jdbc.DataSourceProperties@21c7208d

 

启动日志中,还有下面两行:表明建立了HikariDataSource对象

com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.

 

除了生产了 dataSource Bean,操作RDB时还生成了jdbcTemplate Bean,它会使用了 dataSource Bean。

 

其中,名为dataSource 的Bean的类型为 HikariDataSource (HikariPool-1)。

添加dataSource Bean检查接口:

/ds/check/get 接口
@RestController
@RequestMapping(value="/ds/check")
public class CheckDatasource {

	private static Consumer<Object> cs = System.out::println;

	@Autowired
	private DataSource datasource;
	
	@GetMapping("/get")
	public Boolean get() {
		cs.accept("datasource=" + datasource);
		cs.accept("datasource.class=" + datasource.getClass());
		try {
			cs.accept("datasource.getLoginTimeout=" + datasource.getLoginTimeout());
		} catch (SQLException e) {
			cs.accept("getLoginTimeout() SQLException e=" + e.getClass());
		}
		try {
			Connection conn = datasource.getConnection();
			cs.accept("conn=" + conn);
			cs.accept("conn.Catalog=" + conn.getCatalog());
			cs.accept("conn.NetworkTimeout=" + conn.getNetworkTimeout());
			cs.accept("conn.Schema=" + conn.getSchema());
			cs.accept("conn.TransactionIsolation=" + conn.getTransactionIsolation());
			cs.accept("conn.ClientInfo=" + conn.getClientInfo());
			DatabaseMetaData md = conn.getMetaData();
			cs.accept("conn.md=" + md);
			cs.accept("conn.md.DriverName=" + md.getDriverName());
			cs.accept("conn.md.DriverVersion=" + md.getDriverVersion());
			cs.accept("conn.md.DatabaseProductName=" + md.getDatabaseProductName());
			cs.accept("conn.md.DatabaseProductVersion=" + md.getDatabaseProductVersion());
		} catch (SQLException e) {
			cs.accept("datasource.getConnection() SQLException e=" + e.getClass());
		}
		
		return true;
	}
	
}

 

调用 /ds/check/get 接口,返回true。检查应用日志:接口输出下面的内容。来自博客园

datasource=HikariDataSource (HikariPool-1)
datasource.class=class com.zaxxer.hikari.HikariDataSource
datasource.getLoginTimeout=30
conn=HikariProxyConnection@2078797209 wrapping com.mysql.cj.jdbc.ConnectionImpl@6636c130
conn.Catalog=db_example
conn.NetworkTimeout=0
conn.Schema=null
conn.TransactionIsolation=4
conn.ClientInfo={}
conn.md=HikariProxyDatabaseMetaData@1524225169 wrapping com.mysql.cj.jdbc.DatabaseMetaData@5038dad0
conn.md.DriverName=MySQL Connector/J
conn.md.DriverVersion=mysql-connector-java-8.0.26 (Revision: 9aae1e450989d62c06616c1dcda3e404ef84df70)
conn.md.DatabaseProductName=MySQL
conn.md.DatabaseProductVersion=5.7.21-1ubuntu1

 

操作数据库

// 实体类 Country.java
@Entity
@Data
public class Country {
    // ...省略
}

// DAO层 CountryDAO.java
public interface CountryDAO extends CrudRepository<Country, Long> {
    // 暂无内容
}

// 省略了服务层Service

// 接口层 CountryApi.java
@RestController
@RequestMapping(value="/api/country")
@Slf4j
public class CountryApi {

	@Autowired
	private CountryDAO countryDao;
    
    // ...省略
}

 

本项目源码

 

新增接口:

1)增加Country POST /api/country/add

2)根据ID查找 GET /api/country/getById

 

启动项目,此时,数据库建立好了数据表:来自博客园

 

测试:

调用 POST /api/country/add,添加成功。

调用 GET /api/country/getById,查找成功。

数据库中数据:

country表数据
mysql> select * from country;
+----+---------+---------------------+-----------------------+--------------------------------+---------------------+
| id | area    | create_time         | name_cn               | name_en                        | update_time         |
+----+---------+---------------------+-----------------------+--------------------------------+---------------------+
|  1 | 9600000 | 2021-09-28 18:13:24 | 中华人民共和国        | the People's Republic of China | 2021-09-28 18:13:24 |
|  2 | 9600000 | 2021-09-28 18:18:00 | 中华人民共和国        | the People's Republic of China | 2021-09-28 18:18:00 |
+----+---------+---------------------+-----------------------+--------------------------------+---------------------+
2 rows in set (0.01 sec)

 

使用MySQL的 show processlist; 命令,可以看到应用已和数据库建立了 多个连接(HikariCP 的  默认连接数是 10):查询时状态都为 sleep

10个连接是否够用?怎么配置HikariCP连接池?请看S.B.官文,其中,连接池配置以 “spring.datasource.”开头,HikariCP的特别配置 以“spring.datasource.hikari.”开头。

比如,修改下面的配置,可以变更默认连接数:

# 最小空闲线程数(默认为 10)
spring.datasource.hikari.minimum-idle=5

效果:

3、使用Druid

S.B.官文没有介绍怎么用Druid。来自博客园

Druid官网也没找到介绍。

最后在 https://mvnrepository.com/ 找到下面两个依赖包:

既然是S.B.使用,那就使用第二个 Druid Spring Boot Starter。

 

添加依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.6</version>
</dependency>

包结构如下:

使用Druid,需要做什么配置吗?

先启动项目看看。

启动成功。

启动日志中没有前面HikariCP的任何内容了!

下面两句显示启动了Druid连接池:

c.a.d.s.b.a.DruidDataSourceAutoConfigure : Init DruidDataSource
com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited

dataSource Bean被替换了?调用 /ds/check/get 检查。结果如下:

Druid连接池信息
datasource={
	CreateTime:"2021-09-28 21:37:07",
	ActiveCount:0,
	PoolingCount:1,
	CreateCount:1,
	DestroyCount:0,
	CloseCount:2,
	ConnectCount:2,
	Connections:[
		{ID:1695012953, ConnectTime:"2021-09-28 21:37:08", UseCount:2, LastActiveTime:"2021-09-28 21:37:09"}
	]
}
datasource.class=class com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceWrapper
datasource.getLoginTimeout=0
2021-09-28 21:40:34.004  WARN 24124 --- [nio-9000-exec-1] c.a.druid.pool.DruidAbstractDataSource   : discard long time none received connection. , jdbcUrl : jdbc:mysql://mylinux:3306/db_example?serverTimezone=Asia/Shanghai, version : 1.2.5, lastPacketReceivedIdleMillis : 204760
conn=com.mysql.cj.jdbc.ConnectionImpl@3e87a374
conn.Catalog=db_example
conn.NetworkTimeout=0
conn.Schema=null
conn.TransactionIsolation=4
conn.ClientInfo={}
conn.md=com.mysql.cj.jdbc.DatabaseMetaData@156d104d
conn.md.DriverName=MySQL Connector/J
conn.md.DriverVersion=mysql-connector-java-8.0.26 (Revision: 9aae1e450989d62c06616c1dcda3e404ef84df70)
conn.md.DatabaseProductName=MySQL
conn.md.DatabaseProductVersion=5.7.21-1ubuntu1

dataSource Bean已经变为 DruidDataSourceWrapper对象了。来自博客园

 

检查MySQL的连接:默认只有一个连接

怎么修改Druid的配置呢?请看Druid的GitHub文档,参考文档2 也有一些介绍。

 

测试:

调用 POST /api/country/add,添加成功。

调用 GET /api/country/getById,查找成功。

 

访问/druid/*端点

参考文档2 提到,HikariCP的性能 要好于 Druid(作者为测试),但是,Druid 的功能更全面(sql拦截、统计数据等(TODO))。

根据参考文档2,编写下面的程序实现:

@Configuration
public class DruidConfig {

	@Bean
	public ServletRegistrationBean statViewServlet( ) {
		ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(
				new StatViewServlet(), "/druid/*");
		
		servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
		
		servletRegistrationBean.addInitParameter("loginUsername", "root");
		servletRegistrationBean.addInitParameter("loginPassword", "root");
		
		servletRegistrationBean.addInitParameter("resetEnable", "false");
		
		return servletRegistrationBean;
	}
	
}

 

再次,启动应用,访问 http://localhost:9000/druid ,提示登录,登录后进入页面:

这个页面更多的功能,Druid更多的使用,还需再探索。

》》》全文完《《《来自博客园

 

HikariCP、Druid的性能怎么测试、比较?

HikariCP的优势是什么?

Druid的优势是什么?

必须用Druid替代HikariCP?

连接数如何设置才合理?

在多个应用链接MySQL时,MySQL服务器允许的连接数不够了会怎样?来自博客园

在dataSource Bean的信息(get函数)中,没有发现 连接timeout 等参数,哪里找?还是说,没有?

继续探索...

 

参考文档

1、数据库连接池

2、常见的几种数据库连接池的配置和使用

3、GitHub druid-spring-boot-starter项目

这里有更完善的Druid使用、配置介绍。

第一手资料

4、

posted @ 2021-09-29 06:34  快乐的凡人721  阅读(258)  评论(0编辑  收藏  举报