iT邦幫忙

0

数据库连接原理:JDBC、MyBatis-Plus、GORM、PHP、Python、C++

  • 分享至 

  • xImage
  •  

一、数据库连接的核心原理

无论 Java、Go、PHP、Python 还是 C++,连接 MySQL、Oracle 等关系型数据库,本质上都是通过数据库驱动 + 网络通信协议与数据库服务器交互。

1.1 整体架构

业务代码(Controller / Service)
           │
           ▼
数据库访问框架(ORM / SQL框架)
           │
           ├── Java:MyBatis / MyBatis-Plus / Hibernate
           ├── Go:GORM / Ent / sqlx
           ├── PHP:Laravel Eloquent / Doctrine
           ├── Python:SQLAlchemy / Django ORM
           └── C++:ODB / SOCI
           │
           ▼
数据库访问接口及驱动
           │
           ├── Java:JDBC + Connector/J
           ├── Go:database/sql + MySQL Driver
           ├── PHP:PDO + PDO_MYSQL
           ├── Python:DB-API + PyMySQL
           └── C++:Connector/C++ / ODBC / OCI
           │
           ▼
连接池管理(可选)
           │
           ▼
TCP/IP 或 Unix Domain Socket
           │
           ▼
MySQL / Oracle 数据库服务器
           │
           ▼
SQL解析 → SQL优化 → SQL执行 → 返回结果

注意:连接池和驱动不是严格的串行层级。连接池通常通过驱动创建物理连接,并向业务框架提供可复用的连接。

1.2 核心结论

  1. ORM 框架主要负责 SQL 构造、对象映射,不直接实现底层 TCP 通信。
  2. 数据库驱动负责实现数据库协议、身份认证、SQL 请求发送和结果解析。
  3. 远程连接 MySQL、Oracle 通常使用 TCP/IP。
  4. 本机数据库也可能使用 Unix Domain Socket 等通信方式。
  5. 连接池负责复用物理数据库连接,减少频繁创建和销毁连接的开销。
  6. 一个数据库物理连接通常对应一个 TCP 连接,但并非所有数据库架构都严格一对一。

二、数据库连接是 TCP 连接吗?

2.1 MySQL

MySQL 默认 TCP 端口:

3306

常见连接方式:

连接方式 使用场景 是否 TCP
TCP/IP 远程连接数据库 是
Unix Domain Socket Linux/macOS 本机连接 否
Named Pipe Windows 特定配置 否
Shared Memory Windows 本机特定配置 否

MySQL 建立连接的流程:

应用程序                         MySQL服务器
    │                                  │
    │──── TCP SYN ────────────────────>│
    │<─── TCP SYN + ACK ───────────────│
    │──── TCP ACK ────────────────────>│
    │                                  │
    │<─── MySQL握手信息 ───────────────│
    │                                  │
    │──── 身份认证及协议协商 ─────────>│
    │                                  │
    │<─── 认证成功 ────────────────────│
    │                                  │
    │──── SQL请求 ────────────────────>│
    │                                  │
    │<─── SQL执行结果 ─────────────────│
    │                                  │

如果启用了 TLS,还需要进行 TLS 协商。

2.2 Oracle

Oracle 常见监听端口:

1521

Oracle 远程连接通常使用:

TCP/IP + Oracle Net(TNS协议)

工作过程:

应用程序
   ↓
Oracle数据库驱动
   ↓
TCP连接
   ↓
Oracle Listener
   ↓
Oracle数据库服务
   ↓
身份认证
   ↓
建立数据库会话
   ↓
执行SQL

注意:TCP 连接与数据库 Session 是不同概念。

  • TCP Connection:网络层面的通信连接。
  • Database Session:数据库服务器建立的逻辑会话。
  • Physical Connection:数据库客户端实际建立的物理连接。
  • Logical Connection:连接池提供给业务代码使用的逻辑连接对象。

三、ORM、数据库驱动、连接池的区别

组件 主要职责 代表工具
ORM SQL构造、对象关系映射 GORM、Hibernate、SQLAlchemy
SQL映射框架 SQL管理、参数绑定、结果映射 MyBatis、MyBatis-Plus
数据库访问接口 提供统一数据库操作 API JDBC、database/sql、PDO
数据库驱动 实现数据库通信协议 Connector/J、PyMySQL
连接池 连接复用、连接数限制、连接回收 HikariCP、Druid、QueuePool
数据库服务器 SQL解析、优化、执行、事务管理 MySQL、Oracle

可以理解为:

ORM:负责生成和组织SQL

数据库访问接口:负责统一调用方式

Driver:负责数据库协议通信

Connection Pool:负责管理连接

TCP:负责网络传输

MySQL / Oracle:负责真正执行SQL

四、不同开发语言如何连接数据库?

4.1 Java:JDBC、MyBatis-Plus

Java 数据库访问架构:

Controller
    ↓
Service
    ↓
MyBatis-Plus
    ↓
MyBatis
    ↓
JDBC
    ↓
HikariCP连接池
    ↓
MySQL Connector/J
    ↓
TCP
    ↓
MySQL

JDBC 是 Java 数据库访问标准 API。

常见接口:

接口 作用
DriverManager 创建数据库连接
DataSource 提供数据库连接
Connection 数据库连接
Statement 执行SQL
PreparedStatement 参数化SQL
ResultSet 获取查询结果

JDBC 连接示例:

Connection conn = DriverManager.getConnection(
    "jdbc:mysql://127.0.0.1:3306/test",
    "root",
    "password"
);

MyBatis-Plus 查询:

User user = userMapper.selectById(1);

底层流程:

MyBatis-Plus生成SQL
        ↓
MyBatis执行器
        ↓
JDBC获取Connection
        ↓
数据库Driver
        ↓
TCP发送SQL
        ↓
MySQL执行SQL
        ↓
ResultSet返回结果
        ↓
映射Java对象

Java 常用连接池:HikariCP、Druid。

Spring Boot HikariCP 配置:

spring:
  datasource:
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000

参数:

  • maximum-pool-size:连接池最大连接数。
  • minimum-idle:期望维持的最小空闲连接数。
  • connection-timeout:等待连接的超时时间。
  • idle-timeout:空闲连接回收时间。
  • max-lifetime:连接最大生命周期。

4.2 Go:database/sql、GORM

Go 数据库访问架构:

Gin Controller
      ↓
Service
      ↓
GORM
      ↓
database/sql
      ↓
go-sql-driver/mysql
      ↓
TCP
      ↓
MySQL

其中:

  • GORM:ORM框架。
  • database/sql:Go标准库,提供数据库访问接口和连接池。
  • go-sql-driver/mysql:MySQL数据库驱动。

GORM 连接 MySQL:

dsn := "root:password@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"

db, err := gorm.Open(
    mysql.Open(dsn),
    &gorm.Config{},
)

控制连接池:

sqlDB, err := db.DB()
if err != nil {
    panic(err)
}

// 最大打开连接数
sqlDB.SetMaxOpenConns(20)

// 最大空闲连接数
sqlDB.SetMaxIdleConns(10)

// 连接最大生命周期
sqlDB.SetConnMaxLifetime(30 * time.Minute)

// 连接最大空闲时间
sqlDB.SetConnMaxIdleTime(10 * time.Minute)

说明:

MaxOpenConns = 20

表示该连接池最多打开20条数据库连接。

当连接全部被占用时,新的数据库操作需要等待空闲连接,或在超时、上下文取消后返回错误。

重点:Go 的 *sql.DB 不是一条数据库连接,而是连接池句柄。

应在程序启动时创建并复用,不应该每次 HTTP 请求都创建新的连接池。


4.3 PHP:PDO、MySQLi、Laravel Eloquent

PHP 常见数据库访问工具:

  • PDO:通用数据库访问接口。
  • MySQLi:MySQL专用扩展。
  • Eloquent:Laravel ORM。
  • Doctrine:PHP ORM。
  • OCI8:Oracle数据库扩展。

PDO 连接 MySQL:

$pdo = new PDO(
    "mysql:host=127.0.0.1;port=3306;dbname=test",
    "root",
    "password"
);

工作过程:

PHP业务代码
    ↓
PDO
    ↓
PDO_MYSQL驱动
    ↓
MySQL客户端协议
    ↓
TCP
    ↓
MySQL

PHP 支持持久连接:

$pdo = new PDO(
    $dsn,
    $username,
    $password,
    [
        PDO::ATTR_PERSISTENT => true
    ]
);

注意:

  • PHP持久连接不等于全局共享连接池。
  • PHP-FPM不同Worker通常不共享PDO连接。
  • Worker数量增加,数据库连接数也可能增加。
  • Laravel Eloquent本身不是底层TCP连接管理器。

例如:

PHP-FPM Worker:30

每个Worker保留1条持久连接

理论上可能保留约30条数据库连接

4.4 Python:PyMySQL、SQLAlchemy、Django ORM

Python 常见数据库工具:

工具 作用
PyMySQL MySQL驱动
mysqlclient MySQL驱动
SQLAlchemy SQL工具包及ORM
Django ORM Django数据库ORM
python-oracledb Oracle驱动

Python 数据库访问流程:

Python业务代码
      ↓
SQLAlchemy
      ↓
Python DB-API
      ↓
PyMySQL
      ↓
Socket / TCP
      ↓
MySQL

SQLAlchemy 连接池:

from sqlalchemy import create_engine

engine = create_engine(
    "mysql+pymysql://root:password@127.0.0.1:3306/test",
    pool_size=10,
    max_overflow=5,
    pool_timeout=30,
    pool_recycle=1800,
    pool_pre_ping=True
)

参数:

参数 作用
pool_size 常规连接池容量
max_overflow 允许额外创建的连接数
pool_timeout 获取连接等待超时
pool_recycle 连接达到指定年龄后回收重建
pool_pre_ping 借出连接前检测可用性

例如:

pool_size = 10
max_overflow = 5

最大并发借出连接数 = 15

这是 SQLAlchemy QueuePool 的典型行为。

额外溢出连接通常在归还时关闭。

Django ORM 主要通过 CONN_MAX_AGE 等参数管理持久连接,其默认连接管理机制与 SQLAlchemy QueuePool 不同。


4.5 C++:Connector/C++、ODBC、OCI

C++ 常见数据库工具:

工具 数据库
MySQL Connector/C++ MySQL
MySQL Connector/C MySQL
ODBC 多种数据库
SOCI 多种数据库
ODB 多种数据库
OCI / OCCI Oracle

连接过程:

C++业务代码
     ↓
数据库访问库
     ↓
数据库客户端驱动
     ↓
TCP / Socket
     ↓
MySQL / Oracle

C++没有所有数据库框架统一使用的标准连接池。

常见连接池实现:

  1. 数据库客户端库自带连接池。
  2. 使用第三方连接池。
  3. 自定义线程安全连接池。
  4. 通过数据库代理管理后端连接。

五、数据库连接池的工作原理

5.1 为什么需要连接池?

不使用连接池:

HTTP请求
    ↓
创建TCP连接
    ↓
数据库握手
    ↓
身份认证
    ↓
执行SQL
    ↓
关闭数据库连接
    ↓
关闭TCP连接

每次请求都可能重新创建连接,增加网络、认证及数据库资源开销。

使用连接池:

HTTP请求
    ↓
从连接池获取连接
    ↓
执行SQL
    ↓
返回结果
    ↓
归还连接池
    ↓
下一个请求复用连接

连接池的核心作用:连接复用、限制连接数量、管理连接生命周期。

5.2 连接池内部结构

             应用程序
                │
                ▼
           数据库连接池
    ┌─────────────────────────┐
    │ Connection 1:使用中    │
    │ Connection 2:使用中    │
    │ Connection 3:空闲      │
    │ Connection 4:空闲      │
    │ Connection 5:空闲      │
    └─────────────────────────┘
                │
                ▼
             MySQL

连接获取流程:

请求获取数据库连接
        ↓
检查是否存在空闲连接
        │
        ├── 有 → 复用空闲连接
        │
        └── 无
             ↓
        是否达到最大连接数?
             │
             ├── 否 → 创建新连接
             │
             └── 是 → 等待空闲连接
                         │
                         ├── 获取成功
                         │
                         └── 超时返回错误

5.3 连接池管理哪些参数?

  • 最大打开连接数。
  • 最大空闲连接数。
  • 当前使用中连接数。
  • 当前等待连接的请求数。
  • 连接最大生命周期。
  • 连接最大空闲时间。
  • 获取连接的超时时间。
  • 连接健康检查。

5.4 close() 是否真正关闭 TCP?

分为两种情况。

没有连接池:

Connection.close()
       ↓
关闭数据库物理连接
       ↓
关闭底层Socket

使用连接池:

Connection.close()
       ↓
释放逻辑连接
       ↓
归还连接池
       ↓
TCP连接继续保留
       ↓
供后续请求复用

连接池也可能因为连接失效、超过生命周期等原因真正关闭 TCP 连接。


六、HTTP连接、数据库连接、TCP连接的区别

典型 Web 应用:

浏览器
   │
   │ HTTP/HTTPS
   ▼
Nginx
   │
   │ HTTP
   ▼
Java / Go / PHP / Python服务
   │
   │ ORM
   ▼
数据库连接池
   │
   │ MySQL协议 / TCP
   ▼
MySQL

需要区分:

连接 用途
HTTP连接 浏览器与Web服务器通信
数据库逻辑连接 业务代码借用的连接对象
数据库物理连接 驱动与数据库建立的实际连接
TCP连接 底层网络通信通道
数据库Session 数据库服务器维护的逻辑会话

6.1 1000个HTTP请求需要1000条数据库连接吗?

不需要。

例如:

HTTP并发请求:1000

Go连接池MaxOpenConns:50

可能出现:

1000个HTTP请求
       ↓
Go服务器
       ↓
database/sql连接池
       │
       ├── 最多50条数据库连接
       │
       └── 其余需要连接的操作等待
       ↓
MySQL

一个HTTP请求可能:

  • 不执行SQL。
  • 执行一条SQL。
  • 执行多条SQL。
  • 使用事务持续占用一条连接。

因此:

HTTP并发数 ≠ 数据库连接数

Goroutine数量 ≠ 数据库连接数

线程数量 ≠ 数据库连接数

七、MySQL如何控制数据库连接数?

7.1 查看最大连接数

SHOW VARIABLES LIKE 'max_connections';

例如:

max_connections = 300

表示MySQL配置的常规客户端最大连接数为300。

7.2 查看当前连接数

SHOW STATUS LIKE 'Threads_connected';

7.3 查看正在运行的线程数

SHOW STATUS LIKE 'Threads_running';

7.4 查看历史最大连接数

SHOW STATUS LIKE 'Max_used_connections';

7.5 查看连接详情

SHOW FULL PROCESSLIST;

7.6 修改最大连接数

动态修改:

SET GLOBAL max_connections = 500;

配置文件:

[mysqld]
max_connections = 500

注意:max_connections不是越大越好,过多连接会增加数据库内存消耗、线程调度和资源竞争。


八、多实例部署如何计算数据库连接数?

假设:

Java服务:3个实例,每实例20条连接

Go服务:4个实例,每实例30条连接

Python服务:2个进程,每进程15条连接

计算:

Java:3 × 20 = 60

Go:4 × 30 = 120

Python:2 × 15 = 30

理论连接上限合计:210

如果:

MySQL max_connections = 200

说明各服务的连接池配置上限之和已经超过MySQL的常规连接限制。

但连接池通常按需创建连接,并不代表启动后立即占满210条连接。

容量规划建议:

所有应用实例连接池上限
+
定时任务连接预算
+
数据库管理工具连接预算
+
其他业务连接预算
+
运维预留连接
≤
数据库可用连接预算

九、一条SQL的完整执行过程

以 Go + Gin + GORM + MySQL 为例。

业务代码:

var user User

db.Where("id = ?", 1).First(&user)

完整流程:

1. 浏览器发送HTTP请求
          ↓
2. Nginx接收并转发请求
          ↓
3. Gin Router匹配路由
          ↓
4. Controller调用Service
          ↓
5. Service调用GORM
          ↓
6. GORM构造SQL
          ↓
7. database/sql获取数据库连接
          ↓
8. 连接池复用或创建连接
          ↓
9. MySQL Driver编码SQL请求
          ↓
10. 通过TCP发送给MySQL
          ↓
11. MySQL解析SQL
          ↓
12. 优化器选择执行计划
          ↓
13. 存储引擎读取数据
          ↓
14. MySQL通过TCP返回结果
          ↓
15. Driver解码结果
          ↓
16. GORM映射到Go Struct
          ↓
17. 释放结果集和连接资源
          ↓
18. 连接归还连接池
          ↓
19. Controller返回JSON
          ↓
20. 浏览器接收响应

注意:事务中连接通常会持续占用,直到事务提交或回滚。


十、数据库事务与连接池的关系

事务一般需要绑定数据库会话和连接上下文。

从连接池获取Connection
          ↓
BEGIN
          ↓
执行SQL 1
          ↓
执行SQL 2
          ↓
COMMIT / ROLLBACK
          ↓
归还Connection

长事务的问题:

事务执行时间过长
        ↓
数据库连接长期被占用
        ↓
连接池空闲连接减少
        ↓
其他请求等待连接
        ↓
接口响应变慢
        ↓
可能出现连接池超时

长事务还可能造成锁等待和数据库资源占用。

因此应该:

  • 尽量缩短事务时间。
  • 避免事务中执行长时间外部网络请求。
  • 正确提交或回滚事务。
  • 确保异常情况下释放连接。

十一、常见数据库连接问题

11.1 Too many connections

MySQL报错:

ERROR 1040 (HY000): Too many connections

可能原因:

  • 应用实例太多。
  • 连接池配置过大。
  • 数据库连接泄漏。
  • 长事务占用连接。
  • PHP-FPM持久连接数量过多。
  • MySQL连接数上限不足。

排查SQL:

SHOW VARIABLES LIKE 'max_connections';

SHOW STATUS LIKE 'Threads_connected';

SHOW STATUS LIKE 'Max_used_connections';

SHOW FULL PROCESSLIST;

11.2 连接池耗尽

例如:

最大连接数:20

当前使用中连接:20

新的SQL请求:需要等待连接

Go可以查看连接池状态:

stats := sqlDB.Stats()

fmt.Println(stats.OpenConnections)
fmt.Println(stats.InUse)
fmt.Println(stats.Idle)
fmt.Println(stats.WaitCount)
fmt.Println(stats.WaitDuration)

重点关注:

  • OpenConnections:当前打开连接数。
  • InUse:正在使用的连接数。
  • Idle:空闲连接数。
  • WaitCount:累计等待连接次数。
  • WaitDuration:累计等待时间。

11.3 数据库连接泄漏

常见原因:

  • 没有关闭Rows。
  • 没有释放Connection。
  • 事务没有提交或回滚。
  • 异常路径没有正确清理资源。

Go示例:

rows, err := sqlDB.Query("SELECT id FROM users")

if err != nil {
    return
}

defer rows.Close()

11.4 MySQL主动断开空闲连接

MySQL配置:

SHOW VARIABLES LIKE 'wait_timeout';

可能出现:

连接池保留空闲连接
        ↓
长时间没有使用
        ↓
超过MySQL wait_timeout
        ↓
MySQL关闭连接
        ↓
应用下次使用连接
        ↓
发现连接失效

解决思路:

  • 配置连接最大生命周期。
  • 配置空闲连接回收时间。
  • 使用连接健康检查。
  • 驱动正确处理失效连接并重新建立连接。

十二、MySQL与Oracle的区别

对比项 MySQL Oracle
常见端口 3306 1521
远程连接 TCP/IP TCP/IP
通信协议 MySQL协议 Oracle Net / TNS
Java驱动 Connector/J Oracle JDBC
Go驱动 go-sql-driver/mysql godror
Python驱动 PyMySQL python-oracledb
PHP驱动 PDO_MYSQL / MySQLi OCI8
C++驱动 Connector/C++ OCI / OCCI
连接池 客户端连接池、代理 客户端连接池、DRCP等

Oracle支持DRCP:

Database Resident Connection Pooling

用于复用数据库服务器端的连接和会话资源。

MySQL可通过ProxySQL等代理实现后端连接管理和部分连接复用能力。


十三、不同语言的连接池配置对比

语言 ORM/框架 连接池 最大连接数配置
Java MyBatis-Plus HikariCP maximumPoolSize
Go GORM database/sql SetMaxOpenConns
PHP Laravel Eloquent 持久连接/专用连接池 Worker数量及池配置
Python SQLAlchemy QueuePool pool_size + max_overflow
Python Django ORM 持久连接管理 CONN_MAX_AGE等
C++ ODB / SOCI 第三方或自定义 具体实现决定

十四、生产环境连接池如何设置?

假设:

MySQL max_connections = 300

Go实例:4个

Java实例:3个

示例连接预算:

Go:4 × 25 = 100

Java:3 × 30 = 90

其他业务及管理连接预算:60

总预算:250

剩余余量:50

以上仅为示例,不能作为固定最佳配置。

连接池大小应结合:

  1. 数据库CPU和内存。
  2. SQL平均执行时间及P95/P99延迟。
  3. 数据库实际活跃连接数。
  4. 连接池等待时间。
  5. 慢SQL和锁等待情况。
  6. 应用实例数量。
  7. 请求峰值和事务持续时间。

连接池不是越大越好。

连接池过大可能导致数据库同时处理过多SQL,增加CPU调度、内存消耗和锁竞争。


十五、核心知识点总结

15.1 必须理解的10个问题

问题 答案
JDBC是驱动吗? 不是,是Java数据库访问标准API
MyBatis-Plus直接操作TCP吗? 不是,通过JDBC驱动通信
GORM自己维护连接池吗? 常规用法依赖database/sql
*sql.DB是一条连接吗? 不是,是连接池句柄
ORM一定自带连接池吗? 不一定
数据库连接一定是TCP吗? 不一定,也可能是Unix Socket
一个HTTP请求对应一个数据库连接吗? 不一定
1000个Goroutine需要1000条数据库连接吗? 不需要
连接池设置20就立即创建20条连接吗? 不一定,通常按需创建
调用close()一定关闭TCP吗? 不一定,可能只是归还连接池

15.2 最重要的五层架构

第一层:ORM / SQL框架
负责SQL构造和对象映射
           ↓
第二层:数据库访问接口及驱动
负责数据库协议和SQL通信
           ↓
第三层:连接池
负责连接复用、限制和回收
           ↓
第四层:网络通信
TCP/IP、Unix Socket等
           ↓
第五层:数据库服务器
负责SQL解析、优化、执行及事务管理

15.3 结论

ORM负责组织SQL,驱动负责协议通信,连接池负责复用连接,TCP负责传输数据,MySQL/Oracle负责执行SQL。

  1. Java JDBC、Go GORM、PHP PDO、Python SQLAlchemy、C++数据库连接器,底层都需要通过数据库驱动与数据库服务器通信。
  2. 远程数据库连接通常基于TCP,但也支持其他传输方式。
  3. 数据库连接池并不是每次请求都创建新连接,而是尽可能复用已有连接。
  4. 连接池最大连接数是单个池的配置,需要结合应用实例数计算总体连接预算。
  5. HTTP并发数、线程数、Goroutine数和数据库连接数不是一回事。
  6. 数据库连接数既受客户端连接池限制,也受数据库服务器配置限制。
  7. 长事务、慢SQL、连接泄漏都可能导致连接池耗尽。
  8. 生产环境需要同时监控连接池使用情况、MySQL连接数、SQL延迟和数据库资源。
  9. 连接池过小可能导致请求排队,连接池过大可能增加数据库负担。
  10. 理解ORM、驱动、连接池、TCP、数据库Session之间的关系,是排查数据库性能和连接问题的基础。

圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言