1、项目环境
Spring Boot 3.4.1
Druid 1.2.24
MySql-connector-j 9.1.0
MySql 连接字符串:jdbc:mysql://xxx.xxx.xxx.xxx:3306/数据库名?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true
2、系统环境
CentOS 8
MySql 8.0.27
3、问题描述
连接 mysql 数据库,启动项目时间超长,特别是设置了 druid 的初始化连接数(intial-size)和最小连接池数量(min-idel)配置过大的情况下,问题排查时,initial-size :5,min-idel:10。在项目启动初始化连接的那部分,大概需要40~50秒才能初始化完成。降低这两个配置,分别设置为1的话,启动时间大概在10秒左右。换个库,比如 postgressql 就很丝滑,这就很神奇了。
4、问题排查
1.程序入口 main 函数打上断点,开始漫长的调试过程,忽略一些不重要的,直接上重点。
2.首先,先来到 DruidDataSource 类,这个类位于:com.alibaba.druid.pool 包下,方法:init()。
3.一探 createPhysicalConnection() 的究竟,这个方法类位于:com.alibaba.druid.pool 包下,方法:createPhysicalConnection()。
4.进入 this.createPhysicalConnection(url, physicalConnectProperties) 方法,再探究竟。这个方法就在当前类下。
5.接着跟代码......
6.接着跟代码,这里走的 else,也就是 filterChain.connection_connect(info),来到 FilterChainImpl 类,这个类位于:com.alibaba.druid 包下。
7.继续继续,重点来了,这里来到了,一路跟到 NonRegisteringDriver 类,方法:connect(String url, Properties info),这个类位于:com.mysql.cj.jdbc 包下,这个包就是 mysql-connector-j 9.1.0。
8.深入 ConnectionImpl.getInstance(conStr.getMainHost()) 方法,该方法在 ConnectionImpl 类中,该类位于:com.mysql.cj.jdbc 包下。
9.我跟,接着来到 InetAddress 类,方法:getHostFromNameService(InetAddress addr, boolean check) 方法,该类位于:java.net 包下,jdk 21 的包。
10.好了,到这就是我的极限了,不跟了,全都梭哈了。开始找相关资料,最后发现 javadoc 说:这是由于触发了反向 DNS 查询导致。当一个 InetAddress 创建时包含了域名信息,getHostName() 方法会直接返回这个域名,否则会触发反向 DNS 解析,当配置的 DNS 服务器与目标 InetAddress 之前网络状况不佳就会产生延时。
5、解决问题
1.引入的 mybatis plus 包排除 mysql-connector-j 9.1.0,然后重新引入 8.0.33 以下版本,因为此版本的 ConnectionImpl 没有 getHostName() 方法。
2.修改 mysql 数据库配置文件,启用 skip-name-resolve 会跳过反向 DNS 解析,这样 MySQL 只会基于 IP 地址来进行连接和身份验证,从而加速连接过程并避免 DNS 配置或延迟导致的问题。在 [mysqld] 节点下,新增配置:skip-name-resolve。推荐此方法,简单粗暴。同时,请注意在增加该配置参数后,mysql 的授权表中的 host 字段就不能够使用域名而只能够使用 ip 地址了,因为这是禁止了域名解析的结果。
[mysqld]
skip-name-resolve
文章评论