

🌈個人首頁: 神馬都會億點點的毛毛張

在前面一篇文章中毛毛張介紹了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

🌈歡迎和毛毛張一起探討和交流!
聯系方式點擊下方個人名片