🌈个人首页: 神马都会亿点点的毛毛张
在前面一篇文章中毛毛张介绍了SpringBoot中数据源与数据库连接池相关概念,今天毛毛张要分享的是关于SpringBoot整合HicariCP连接池相关知识点以及底层源码分析
文章目录
- 1 HicariCP连接池概述
- 1.1 HicariCP连接池简介
- 2 快速入门案例
- 2.1 后端代码
- 2.1.1 创建项目
- 2.1.2 导入依赖
- 方式1:手动导入 `HikariCP` 依赖
- 方式 2:自动引入依赖 - 推荐
- 本项目完整依赖
- 2.1.3 编写配置文件
- 2.1.4 初始化数据库并创建对应实体类
- 2.1.5 编写响应封装与前端展示VO
- 2.1.6 编写业务逻辑
- 2.1.7 跨域资源共享
- 2.1.8 启动类
- 2.1.9 测试
- 2.2 前端代码
- 3 配置详解
- 3.1 常见配置
- 3.2 数据源配置解析
- 3.3 连接池配置详解
- 4 底层源码解析
- 4.1 SpringBoot自动初始化配置
- 4.2 默认的数据源
- 参考文献
1 HicariCP连接池概述
- 在上面介绍过
HicariCP
是SpringBoot2.0
之后默认的数据库连接池,特点就是:简单,高效,史称最快的数据库连接池,毛毛张将从以下几个方面展开对HicariCP
连接池的介绍:- 首先简单介绍以下
HicariCP
连接池 - 然后通过一个案例来帮助大家能够快速使用
HicariCP
连接池 - 接着通过分析
SpringBoot
的底层代码来解释SringBoot
默认的数据库连接池为HicariCP
- 首先简单介绍以下
1.1 HicariCP连接池简介
- HikariCP 是一个高性能的
JDBC
数据库连接池,基于BoneCP
做了多项优化,旨在提供更高的并发性能和更低的延迟。自SpringBoot 2.x
版本后(自然也包括SpringBoot 3.x
),HikariCP
成为默认的数据库连接池,只需导入HikariCP
的JAR
包并配置相关参数,即可无缝集成并优化数据库连接池管理。 - HikariCP 的高性能优化主要体现在以下两个方面:
- FastList 替代 ArrayList
- 传统的数据库连接池大多使用
ArrayList
存储Statement
,HikariCP 自定义了FastList
来优化这一操作。FastList
的优化主要体现在两个方面:- 取消
ArrayList
的get()
方法中的范围检查(range check)。由于数据库连接池管理的 List 中的索引合法性有保证,因此不需要每次访问都进行索引合法性检查。 - 改变
ArrayList
中remove()
操作的遍历方式,采用从尾部开始遍历,而不是从头开始。由于连接池中的连接通常是逆序释放的(后获取的连接先释放),这样优化后,每次关闭连接时可以更高效地找到需要释放的资源,提升了效率。
- 取消
- 传统的数据库连接池大多使用
- ConcurrentBag 替代阻塞队列
- 大多数传统数据库连接池使用两个阻塞队列(
idle
和busy
)来管理空闲连接和忙碌连接,使用Lock
机制来处理线程竞争,但这种方式在高并发场景下可能会导致性能瓶颈。HikariCP
通过使用ConcurrentBag
替代了传统的阻塞队列,极大地减少了锁的竞争,提高了并发性能。 ConcurrentBag
的核心工作原理:sharedList
:存储所有数据库连接的共享队列,使用CopyOnWriteArrayList
类型,支持并发操作。threadList
:线程本地存储,避免了线程竞争,每个线程会缓存自己获取的连接。waiters
:等待连接的线程数,使用AtomicInteger
类型。handoffQueue
:分配数据库连接的核心队列,使用SynchronousQueue
类型,负责将空闲连接分配给请求的线程。
- 这种设计通过减少线程竞争和优化连接分配,提高了连接池的效率,特别适合高并发的环境。
- 大多数传统数据库连接池使用两个阻塞队列(
- FastList 替代 ArrayList
- 其他优化
- 字节码精简:HikariCP 在字节码上进行了优化,编译后的字节码量极少,这样可以使得更多的代码被 CPU 缓存,从而提高程序的执行效率。减少字节码的大小是提高性能的基础,HikariCP 在这方面做得非常好。
- 优化代理和拦截器:HikariCP 对代理和拦截器的实现进行了精简。例如,它的
Statement
代理类仅有 100 行代码,是 BoneCP 的十分之一。这减少了性能开销,确保数据库连接池在执行 SQL 时更加高效。 - 自定义集合类型(FastStatementList):为了提高对
Statement
的管理效率,HikariCP 使用了自定义的集合类型FastStatementList
来替代传统的ArrayList
。这样避免了每次获取元素时的范围检查,并且采用逆序遍历的方式来优化remove()
操作,使得关闭连接时更加高效。 - 自定义集合类型(ConcurrentBag):HikariCP 为了优化并发读写效率,采用了
ConcurrentBag
代替传统的阻塞队列。这不仅提高了数据库连接分配的效率,而且减少了锁的竞争,显著提升了高并发场景下的性能。 - 针对 BoneCP 缺陷的优化:HikariCP 在设计时,针对 BoneCP 中的一些缺陷进行了优化,特别是在处理 CPU 时间片内的耗时方法调用时,进一步提高了性能。
接下来毛毛张将结合一个完整的项目案例来介绍
SpringBoot
整合MySQL和HikariCP 连接池的过程,并提供了完整的代码
2 快速入门案例
- 案例内容:基于
Spring Boot
,使用MyBatis-Plus
框架,结合HikariCP
连接池,查询并展示数据库中的全部用户信息
2.1 后端代码
2.1.1 创建项目
- 如何快速创建一个
SpringBoot
新项目可以参见毛毛张的这篇博客:【SpringBoot教程】IDEA快速搭建正确的SpringBoot版本和Java版本的项目 - 下面是毛毛张的完整后端代码文件结构如下图:
2.1.2 导入依赖
- 在 Spring Boot 项目中,默认使用 HikariCP 作为数据库连接池,因此在大多数情况下无需手动引入该依赖。若项目中使用了以下某些 starter 依赖,
HikariCP
会自动作为连接池配置:spring-boot-starter-jdbc
spring-boot-starter-data-jpa
mybatis-spring-boot-starter
mybatis-plus-boot-starter
- 需要特别注意的是,
mybatis
和mybatis-plus
已经间接依赖了spring-boot-starter-jdbc
,因此这两个 starter 会自动引入HikariCP
,从而避免重复配置。
方式1:手动导入 HikariCP
依赖
- 如果没有导入
HikariCP
,我们可以通过下面的方式手动导入HikariCP
依赖<dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId><version>6.2.1</version> </dependency>
方式 2:自动引入依赖 - 推荐
-
在使用
SpringBoot
默认配置时,不需要手动添加HikariCP
依赖,可以通过导入下面依赖的方式来自动引入HikariCP
依赖:<!-- 引入 Spring Boot JDBC 模块 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId> </dependency><!-- 引入 Spring Boot JPA 模块 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId> </dependency><!-- 引入 MyBatis Plus 模块 --> <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.2</version> </dependency>
本项目完整依赖
- 需要注意的是,在使用 HikariCP 作为连接池的同时,还需要单独导入 MySQL 数据库驱动,通常通过引入
mysql-connector-java
依赖来实现 - 毛毛张在这个任务中为了方便,使用了
Mybatis-plus
框架,整个项目的pom.xml
文件为:<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.zzx</groupId><artifactId>springboot-HicariCP-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>springboot-HicariCP-demo</name><description>springboot-HicariCP-demo</description><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-parent</artifactId><version>2.7.6</version></parent><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.7.6</spring-boot.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- MySQL 数据库驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope> <!-- 运行时依赖 --></dependency><!-- MybatisPlus 核心库 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.2</version></dependency><!-- 热部署 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><configuration><mainClass>com.zzx.SpringbootHicariCpDemoApplication</mainClass><skip>true</skip></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>
2.1.3 编写配置文件
- 下面是整个项目的配置文件
application.yaml
,关键部分已被注释出来了,更多详细的解释可以参见第三节的内容server:port: 8080 spring:# HikariCP 连接池配置datasource:url: jdbc:mysql://localhost:3306/springboot?useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=true&serverTimezone=UTCdriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: abc123# 指定数据源类型为 HikariDataSourcetype: com.zaxxer.hikari.HikariDataSource# Hikari 连接池的详细配置hikari:# 连接池名称pool-name: HikariCP# 最小空闲连接数minimum-idle: 5# 空闲连接超时时间(毫秒)idle-timeout: 600000# 连接池的最大大小maximum-pool-size: 10# 是否自动提交事务auto-commit: true# 连接的最大生命周期(毫秒)max-lifetime: 1800000# 连接超时时间(毫秒)connection-timeout: 30000# 测试连接的 SQL 语句connection-test-query: SELECT 1logging:level:org.springframework.jdbc.core.JdbcTemplate: DEBUGmybatis-plus:# xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)mapper-locations: classpath:mapper/*.xmlconfiguration:auto-mapping-behavior: full# 开启驼峰映射map-underscore-to-camel-case: true# 如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段call-setters-on-nulls: true# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
2.1.4 初始化数据库并创建对应实体类
-
创建名为
springboot
的数据库,并创建user_info
表:-- 创建数据库 CREATE DATABASE IF NOT EXISTS springboot CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; USE springboot;-- 删除已存在的 user_info 表(如果存在) DROP TABLE IF EXISTS `user_info`;-- 创建 user_info 表 CREATE TABLE `user_info` (`id` INT NOT NULL AUTO_INCREMENT,`user_name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,`pass_word` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,`age` INT NULL DEFAULT NULL,`gender` VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;-- 插入示例数据 INSERT INTO `user_info` (`id`, `user_name`, `pass_word`, `age`, `gender`) VALUES (1, 'sam', 'password123', 32, 'M'), (2, 'hah', 'password456', 10, 'F');-- 确保外键检查被重新启用 SET FOREIGN_KEY_CHECKS = 1;
-
对应实体类
User
:package com.zzx.entity;import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data;@Data @TableName("user_info") public class User {private Integer id; // 对应数据库中的 `u_id`private String userName; // 对应数据库中的 `u_username`private String passWord; // 对应数据库中的 `u_password`private Integer age; // 对应数据库中的 `u_age`private String gender; // 对应数据库中的 `u_gender` }
2.1.5 编写响应封装与前端展示VO
- 定义统一的响应封装类 ResVo:
package com.zzx.reponse;public class ResVo<T> {private Integer code; // 状态码private String message; // 消息内容private T content; // 内容,可以是任何类型的数据// 构造方法public ResVo(Integer code, String message, T content) {this.code = code;this.message = message;this.content = content;}// 成功的返回,通常是常用的,内容可以为空public static <T> ResVo<T> success(T content) {return new ResVo<>(200, "成功", content);}// 失败的返回,通常返回错误信息public static <T> ResVo<T> error(Integer code, String message) {return new ResVo<>(code, message, null);}// Getters and Setterspublic Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public T getContent() {return content;}public void setContent(T content) {this.content = content;} }
- 前端展示对象
UserInfoVo
,在查询用户信息时通过是把查询到的结果封装在一个实体类中,但是返回给前端的信息不一定是用户的全部信息,例如用户的密码就不能直接返回给前端,或者不要返回,所以毛毛张设计了一个这个类:package com.zzx.model.vo;import lombok.Data;@Data public class UserInfoVo {//返回给前端展示的数据,密码不能展示,性别转化成数字private Integer id;private String username;private Integer age;private Integer gender; }
- 为了节省传输的字符,毛毛张将用户的信息对应的内容转化成数字再返回给前端,因此设计了一个枚举类型
UserSexEnum
:package com.zzx.enums;public enum UserSexEnum {M(1, "男"), // M对应男,值为 1F(0, "女"); // F对应女,值为 0private int code; // 对应的数字值(1 或 0)private String description; // 性别描述(男 或 女)// 构造方法,用于设置枚举常量的描述和对应的代码UserSexEnum(int code, String description) {this.code = code;this.description = description;}// 获取性别描述public String getDescription() {return description;}// 获取对应的数字代码public int getCode() {return code;}// 根据传入的字符串 'M' 或 'F' 获取对应的性别枚举public static UserSexEnum fromString(String sexStr) {for (UserSexEnum sex : UserSexEnum.values()) {if (sex.name().equalsIgnoreCase(sexStr)) {return sex;}}throw new IllegalArgumentException("无效的性别字符串: " + sexStr);}// 根据 'M' 或 'F' 获取对应的数字代码public static int getCodeByString(String sexStr) {UserSexEnum sex = fromString(sexStr);return sex.getCode();} }
- 由于
User
类和UserInfoVo
不是完全一一对应的,所以为了数据转换的方便,毛毛张再这里专门写了一个转换类UserConverter
:package com.zzx.converter;import com.zzx.entity.User; import com.zzx.enums.UserSexEnum; import com.zzx.model.vo.UserInfoVo;import java.util.List; import java.util.stream.Collectors;public class UserConverter{// 单个转换public static UserInfoVo toUserInfoDTO(User user) {UserInfoVo userInfoVo = new UserInfoVo();userInfoVo.setId(user.getId());userInfoVo.setUsername(user.getUserName());userInfoVo.setAge(user.getAge());userInfoVo.setGender(UserSexEnum.getCodeByString(user.getGender()));return userInfoVo;}// 批量转换public static List<UserInfoVo> toUserInfoDTOList(List<User> users) {// 使用 Java 8 的 stream API 进行批量转换return users.stream().map(UserConverter::toUserInfoDTO) // 对每个 User 对象进行转换.collect(Collectors.toList()); // 收集成 List<UserInfoDTO>} }
2.1.6 编写业务逻辑
- controller层
UserController
:package com.zzx.controller;import com.zzx.converter.UserConverter; import com.zzx.model.vo.UserInfoVo; import com.zzx.reponse.ResVo; import com.zzx.entity.User; import com.zzx.service.UserService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource; import java.util.List;@RestController @RequestMapping("user") public class UserController {@Resourceprivate UserService userService;@GetMapping("queryAllUserInfo") // 使用 GET 请求public ResVo<List<UserInfoVo>> queryAllUserInfo() {List<User> userInfoList = userService.queryAllUserInfo();return ResVo.success(UserConverter.toUserInfoDTOList(userInfoList));} }
service
层接口UserService
:package com.zzx.service;import com.zzx.entity.User;import java.util.List;public interface UserService {List<User> queryAllUserInfo(); }
- 服务层实现类
UserServiceImpl
:package com.zzx.service.impl;import com.zzx.entity.User; import com.zzx.mapper.UserMapper; import com.zzx.service.UserService; import org.springframework.stereotype.Service;import javax.annotation.Resource; import java.util.List;@Service public class UserServiceImpl implements UserService {@Resourceprivate UserMapper userMapper;@Overridepublic List<User> queryAllUserInfo() {return userMapper.queryAllUserInfo();} }
- mapper层
UserMapper
:package com.zzx.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.zzx.entity.User;import java.util.List;public interface UserMapper extends BaseMapper<User> {List<User> queryAllUserInfo(); }
- Mapper 层 SQL 映射配置文件
UserMapper.xml
:<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zzx.mapper.UserMapper"><!-- 声明标签写sql语句 crud select insert update delete每个标签对应接口的一个方法! 方法的一个实现!注意:mapper接口不能重载!!! 因为mapper.xml无法识别! 根据方法名识别!--><!-- 查询所有用户 --><select id="queryAllUserInfo" resultType="com.zzx.entity.User">SELECT id, user_name, pass_word, age, gender FROM user_info</select></mapper>
2.1.7 跨域资源共享
- 由于毛毛张这个代码还有前端代码,涉及到和前端交互,还需要做一个跨域资源共享的配置,毛毛张没有使用
@CrossOrigin
,而是通过拦截器的方式实现的:package com.zzx.interceptor;import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;public class CorsInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {response.setHeader("Access-Control-Allow-Origin", "*"); // 允许所有来源response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");response.setHeader("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization");response.setHeader("Access-Control-Allow-Credentials", "true");// 处理OPTIONS请求if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {response.setStatus(HttpServletResponse.SC_OK);return false; // 返回false,表示不再执行后续的Controller方法}return true; // 继续执行其他拦截器或Controller方法} }
- 配置类:
package com.zzx.config;import com.zzx.interceptor.CorsInterceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册 CORS 拦截器registry.addInterceptor(new CorsInterceptor()).addPathPatterns("/**") // 拦截所有路径.excludePathPatterns("/login", "/error"); // 排除登录和错误页面} }
2.1.8 启动类
- 启动类
SpringbootHicariCpDemoApplication
:package com.zzx;import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication @MapperScan("com.zzx.mapper") public class SpringbootHicariCpDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringbootHicariCpDemoApplication.class, args);}}
2.1.9 测试
- 启动后端程序,可以在浏览器中输入
http://localhost:8080/user/queryAllUserInfo
,返回结果如下则表示后端代码正确无误:
完整的后端代码已上传至毛毛张
Github
仓库:https://github.com/zzxrepository/SpringBootTutorial/tree/master/springboot-mysql/springboot-HicariCP-demo
2.2 前端代码
- 前端代码和之前毛毛张介绍的
Mybatis
教程的代码是一样的,毛毛张在这里不做过多的介绍了,感兴趣的可以查看毛毛张的相关博客:【SpringBoot教程】SpringBoot整合Mybatis - 前后端分离项目 - vue3 - 完整前端代码已上传至毛毛张
Github
仓库:https://github.com/zzxrepository/SpringBootTutorial/tree/master/springboot-mysql/springboot-mysql-demo-vue
3 配置详解
3.1 常见配置
-
下面是
HicariCP
连接池常见的配置:spring:datasource:url: jdbc:mysql://localhost:3306/springboot?useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=true&serverTimezone=UTCdriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: abc123type: com.zaxxer.hikari.HikariDataSource # 指定数据源类型为 HikariDataSourcehikari: # Hikari 连接池的详细配置pool-name: HikariCP # 连接池名称minimum-idle: 5 # 最小空闲连接数idle-timeout: 600000 # 空闲连接超时时间(毫秒)maximum-pool-size: 10 # 连接池的最大大小auto-commit: true # 是否自动提交事务max-lifetime: 1800000 # 连接的最大生命周期(毫秒)connection-timeout: 30000 # 连接超时时间(毫秒)connection-test-query: SELECT 1 # 测试连接的 SQL 语句
-
上面配置主要分为两部分:一部分是数据源配置,一部分是数据库连接池配置
3.2 数据源配置解析
- 下面部分属于数据源配置:
spring:datasource:# 数据库连接URL,指定数据库地址、端口、库名以及连接参数url: jdbc:mysql://localhost:3306/springboot?useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=true&serverTimezone=UTC# MySQL 8.0 及以上需要 cj,5.7 以下可去掉 cjdriver-class-name: com.mysql.cj.jdbc.Driver # 数据库的用户名和密码username: rootpassword: abc123# 指定使用的数据库连接池实现type: com.zaxxer.hikari.HikariDataSource
- 配置解析:
url
:定义数据库连接的详细信息,包括:localhost:3306
(服务器地址和端口)springboot
(数据库名称)- 连接参数
driver-class-name
:指定数据库连接的驱动,MySQL 8.x使用com.mysql.cj.jdbc.Driver
username
和password
:提供访问数据库的身份凭证。type
:指定数据库连接池的实现,配置为com.zaxxer.hikari.HikariDataSource
,表示使用 HikariCP 作为数据库连接池。如果需要使用 Druid,应将其替换为com.alibaba.druid.pool.DruidDataSource
。
url
连接参数详解:useUnicode=true&characterEncoding=UTF-8
:- 确保数据库与应用之间的字符编码一致,防止乱码问题。
- 推荐使用
utf8mb4
以支持更多字符(如表情符号)。
serverTimezone=UTC
:- 指定数据库服务器的时区,避免时间处理错误。
- 可选值:
UTC
:世界标准时间,比北京时间(CST)早 8 小时。Asia/Shanghai
:中国标准时间(推荐)。
- 说明:MySQL 6.0 及以上的
com.mysql.cj.jdbc.Driver
需要显式指定时区,否则可能导致时区不匹配的异常。
useSSL=false
:- 指定是否启用 SSL 连接。
- 在生产环境(如 Linux 服务器部署)通常关闭 SSL 连接,以减少额外的安全配置。
- 建议:在公共网络或云数据库环境中建议开启 SSL。
autoReconnect=true&failOverReadOnly=false
:autoReconnect=true
:允许 JDBC 驱动在连接意外断开时自动重连(已弃用,推荐使用连接池)。failOverReadOnly=false
:防止发生故障转移时连接错误地设置为只读模式。
allowPublicKeyRetrieval=true
:- 允许客户端从 MySQL 服务器检索公钥,以进行密码验证。
- 安全建议:生产环境下尽量避免使用该参数,建议启用 SSL 进行安全通信。
zeroDateTimeBehavior=convertToNull
:- 当数据库中的日期时间值为
0000-00-00 00:00:00
时,转换为null
,避免 Java 应用程序在处理无效日期值时出错。
- 当数据库中的日期时间值为
rewriteBatchedStatements=true
:- 启用批量执行优化,提高批量
INSERT
、UPDATE
的执行效率。 - 适用于大数据量操作,不适用于
SELECT
查询。
- 启用批量执行优化,提高批量
3.3 连接池配置详解
-
下面是关于连接池的配置部分:
spring:datasource:hikari:# 连接池名称pool-name: HikariCP# 最小空闲连接数minimum-idle: 5# 空闲连接超时时间(毫秒)idle-timeout: 600000# 连接池的最大大小maximum-pool-size: 10# 是否自动提交事务auto-commit: true# 连接的最大生命周期(毫秒)max-lifetime: 1800000# 连接超时时间(毫秒)connection-timeout: 30000# 测试连接的 SQL 语句connection-test-query: SELECT 1
-
关于
HikariCP
连接池的详细配置解析如下:
属性 | 描述 | 构造器默认值 | 默认配置 validate 之后的值 | validate 重置 |
---|---|---|---|---|
autoCommit | 自动提交从池中返回的连接 | true | true | – |
connectionTimeout | 等待来自池的连接的最大毫秒数 | 30000 (30秒) | 30000 | 如果小于 250 毫秒,则重置为 30 秒 |
idleTimeout | 连接允许在池中闲置的最长时间 | 600000 (10分钟) | 受 maxLifetime 影响,条件不符可能重置为 0 | 如果设置小于 10 秒则重置为 10 秒 |
maxLifetime | 池中连接最长生命周期 | 1800000 (30分钟) | 1800000 | 如果小于 30 秒则重置为 30 分钟 |
connectionTestQuery | 如果支持 JDBC4,建议不设置此属性 | null | null | – |
minimumIdle | 池中维护的最小空闲连接数 | 10 | 10 | 小于 0 或大于 maxPoolSize 则重置为 maxPoolSize |
maximumPoolSize | 池中最大连接数,包括闲置和使用中的连接 | 10 | 10 | 如果小于 1,则重置为默认 10 或 minIdle 的值 |
metricRegistry | 指定 Codahale/Dropwizard MetricRegistry 的实例 | null | null | – |
healthCheckRegistry | 指定 Dropwizard HealthCheckRegistry 的实例 | null | null | – |
poolName | 连接池的用户定义名称,主要用于日志和 JMX 管理 | null | HikariPool-1 | – |
initializationFailTimeout | 控制池是否在初始化连接失败时快速失败 | 1 | 1 | – |
isolateInternalQueries | 是否在独立事务中隔离内部池查询 | false | false | – |
allowPoolSuspension | 是否允许通过 JMX 暂停和恢复池 | false | false | – |
readOnly | 从池中获取的连接是否默认处于只读模式 | false | false | – |
registerMbeans | 是否注册 JMX 管理 Bean(MBeans) | false | false | – |
catalog | 设置默认 catalog | default | null | – |
connectionInitSql | 在新连接创建后执行的 SQL 语句 | null | null | – |
driverClassName | JDBC URL 解析失败时指定驱动程序类名称 | null | null | – |
transactionIsolation | 连接的默认事务隔离级别 | null | null | – |
validationTimeout | 连接测试活动的最大时间 | 5000 (5秒) | 5000 | 如果小于 250 毫秒,则重置为 5 秒 |
leakDetectionThreshold | 检测潜在的连接泄漏的时间阈值 | 0 | 0 | 必须 > 0 且 ≥ 2 秒,且不能大于 maxLifetime |
dataSource | 设置要包装的数据源实例 | null | null | – |
schema | 设置默认 schema | default | null | – |
threadFactory | 设置用于创建池线程的 ThreadFactory 实例 | null | null | – |
scheduledExecutor | 设置用于池内部任务的 ScheduledExecutorService 实例 | null | null | – |
- 更具体的可以看官方配置文档
4 底层源码解析
- 前面毛毛张介绍了
HikariCP
连接池的配置,下面毛毛张通过分析源码的方式来介绍以下为什么说SoringBoot2.x
之后默认使用的连接池是HikariCP
,从SpringBoot自动初始化配置 和 默认的数据源 两个角度理解。
4.1 SpringBoot自动初始化配置
- 找到
spring-boot-autocinfigure-2.7.6.jar
依赖下面的org.springframework.boot.autoconfigure.jdbc
包
- 关键代码如下:
- 找到
HikariCP
数据源的配置:你可以发现,为了支持动态更新配置(基于MXBean),这里还设计了一层HikariConfigMXBean
接口
4.2 默认的数据源
- 首先,
springboot-starter-jdbc
中默认加载了HikariCP
- 其次,在配置初始化或者加载时都是第一个被加载的
参考文献
- https://juejin.cn/post/7329033419328307226
- https://zhuanlan.zhihu.com/p/686960884
- https://blog.csdn.net/hansome_hong/article/details/124320410
- https://juejin.cn/post/7224499191962714171#heading-2
- https://blog.csdn.net/hansome_hong/article/details/124320410
- https://pdai.tech/md/spring/springboot/springboot-x-mysql-HikariCP.html
- https://blog.csdn.net/doubiy/article/details/131578389
- https://www.cnblogs.com/lyluoye/p/16627840.html
- https://github.com/brettwooldridge/HikariCP
- https://www.cnblogs.com/zhaojinhui/p/17579010.html
- https://javabetter.cn/springboot/mysql-druid.html
🌈欢迎和毛毛张一起探讨和交流!
联系方式点击下方个人名片