一、TCP连接与MySQL认证流程
1.1 连接建立过程
客户端 服务器
| |
|--------- TCP三次握手 ------------|
| |
|<------- 握手初始化数据包 ---------|
| |
|--------- 发送认证信息 ------------>
| |
|<------- 返回认证结果 -------------|
| |
|========= 执行SQL命令 ============|
|<------- 返回执行结果 -------------|
| |
|--------- 四次挥手断开 -----------|
关键点:
- 每次发送指令,MySQL必然给出回应
- 这是典型的请求-响应模式
- 连接建立成本高,不适合频繁创建销毁
1.2 为什么需要复用连接?
如果每次执行SQL都新建连接:
- TCP三次握手(约1-3ms)
- MySQL握手初始化(约1-2ms)
- 用户认证(约1-2ms)
- 连接关闭四次挥手
一次SQL操作可能需要5-10ms在连接建立上!
复用连接可以跳过这些步骤,直接执行SQL。
二、数据库连接池定义
2.1 什么是连接池?
维持管理一定数量连接的池式结构。
连接池
+---------------+
| 连接1 | <- 可用
| 连接2 | <- 可用
| 连接3 *----|--- 已借出
| 连接4 | <- 可用
+---------------+
^
管理连接生命周期
"维持"的含义:
- 复用已建立的连接
- MySQL会断开长时间空闲的连接
- 需要发送ping包维持心跳保持连接活跃
2.2 连接池解决的问题
| 问题 | 解决方案 | 效果 |
|---|---|---|
| 连接创建开销大 | 预创建连接池 | 避免重复握手 |
| 并发能力不足 | 池中多条连接 | 提升并发处理能力 |
| 资源浪费 | 复用而非销毁 | 降低资源消耗 |
MySQL内部机制:
- 每条连接分配一个独立线程处理
- MySQL使用阻塞IO
- 多连接可以提升MySQL并发处理能力
三、同步连接池 vs 异步连接池
3.1 核心区别
| 特性 | 同步连接池 | 异步连接池 |
|---|---|---|
| 返回方式 | 函数直接返回 | 回调函数接收 |
| 代码阻塞 | 阻塞当前线程 | 不阻塞发起线程 |
| 使用场景 | 初始化阶段 | 业务处理阶段 |
| 逻辑复杂度 | 逻辑通畅 | 可能出现逻辑问题 |
3.2 代码对比
同步接口:
cpp
auto& res = db->Query(sql_str); // 阻塞等待结果
异步接口:
cpp
db->AsyncQuery(sql_str, [](auto& res) {
// 回调函数接收结果
});
3.3 同步连接池工作原理
线程A ---+
线程B ---+---> [申请连接] ---> [加锁] ---> [执行SQL] ---> [解锁] ---> [释放连接]
线程C ---+ ^ |
try_lock() unlock()
(判断是否可用)
关键设计:
- 预创建连接池
- 每条连接配一把锁
- 通过
try_lock()判断连接可用性 - 使用时加锁,完成后解锁
适用场景:
- 服务器初始化阶段
- 可以加快初始化流程
- 适用于对启动速度有要求的场景
3.4 异步连接池工作原理
请求1 ---+
请求2 ---+---> [任务队列] ---> [线程池] ---> [分发到各连接] ---> [执行SQL]
请求3 ---+ |
|
[回调通知]
关键设计:
- 耗时任务丢入队列
- 线程池逐个取出任务
- 每线程对应一个连接
- 不阻塞发起请求的线程
适用场景:
- 业务处理阶段
- 大部分业务场景使用
- 提升系统整体吞吐量
四、MySQL C/C++驱动
4.1 两种主要驱动
| 驱动 | 语言 | 性能 | 使用方式 | 错误处理 |
|---|---|---|---|---|
| libmysqlclient | 纯C | 最高 | 较繁琐 | errno机制 |
| libmysqlcppconn | C++ | 略低 | 方便 | try-catch异常 |
libmysqlclient特点:
- 纯C实现,性能最高
- 需要手动管理资源
- 错误通过
errno传递
c
int err = func();
if (err == 0) {
// 正常
} else if (err == -1) {
// 错误,通过errno获取详情
}
libmysqlcppconn特点:
- C++实现,使用异常机制
- 有性能损失
- 使用更方便
cpp
try {
// 操作
} catch (std::exception& e) {
// 处理异常
}
项目选型建议:
- 严格拒绝异常机制的项目 -> 使用C驱动
- 追求极致性能 -> 使用C驱动
- 快速开发、注重便利性 -> 使用C++驱动
4.2 底层实现原理
底层都是阻塞IO,需要实现以下内容:
1. connect() -> TCP三次握手建立连接
2. recv() -> 接收握手初始化数据包
3. send() -> 发送认证信息(涉及MySQL协议)
4. recv() -> 接收认证结果
5. send() -> 发送SQL命令
6. recv() -> 接收执行结果
MySQL协议要点:
- 基于TCP字节流
- 需要自定义协议解析
- 所有操作都是阻塞IO
五、Future/Promise机制
5.1 为什么需要?
连接池需要将结果传递给请求线程,这要用到Future/Promise机制。
5.2 工作流程
请求线程 线程池线程
| |
| ---> 生成Promise ----> |
| ---> 把任务丢入队列 ----> |
| 获得Future |
| | ---> 执行SQL
| | ---> promise.set_value(res)
| |
| <--- future.get() 获取结果 <----|
| |
5.3 代码示例
cpp
// 请求时生成Promise
auto promise = std::make_shared<std::promise<Result>>();
auto future = promise->get_future();
// 把任务丢到队列,带着promise
taskQueue.enqueue([promise, sql]() {
Result res = executeSql(sql);
promise->set_value(res); // 设置结果
});
// 请求线程等待结果
auto result = future.get(); // 阻塞等待
5.4 前置声明优化
cpp
// .h 文件
class SqlConnection; // 前置声明,避免头文件依赖
// .cpp 文件
#include "SqlConnection.h" // 实际依赖放在源文件
好处:
- 减少头文件依赖
- 加快编译速度
- 降低耦合度
5.5 explicit关键字
cpp
explicit SqlConnection(int id); // 防止隐式转换
作用:
- 防止
SqlConnection conn = 1;这样的隐式调用 - 提高类型安全性
六、连接池设计要点
6.1 一库一池原则
项目
+-- 数据库A --- 连接池A
| +-- 连接1
| +-- 连接2
| +-- 连接3
+-- 数据库B --- 连接池B
+-- 连接1
+-- 连接2
设计原则:
- 一个数据库对应一个连接池
- 避免多个连接池切换
- 统一管理更方便
6.2 连接池核心功能
| 功能 | 说明 |
|---|---|
| 初始化 | 预创建N条连接 |
| 获取连接 | 从池中借出,可用try_lock() |
| 释放连接 | 归还到池中,解锁 |
| 维持心跳 | 定期ping保持连接活跃 |
| 销毁 | 清理所有连接资源 |
七、面试追问FAQ
| 问题 | 答案 |
|---|---|
| 连接池大小怎么定? | 根据并发量、数据库配置、服务器资源综合考虑,通常几十到几百 |
| 连接泄露怎么办? | 记录连接状态、超时检测、优雅关闭时强制回收 |
| MySQL默认超时多久? | 等待wait_timeout默认8小时,但应用层通常更短 |
| 如何处理连接池高并发? | 限流、排队、连接复用、异步化 |
| 连接断了怎么办? | 重连机制、心跳检测、异常状态标记 |
| 同步和异步哪个好? | 初始化用同步(简单快),业务处理用异步(吞吐高) |
八、相关技术对比
| 技术 | 特点 | 适用场景 |
|---|---|---|
| 无连接池 | 每次新建销毁 | 低频、测试环境 |
| 同步连接池 | 阻塞等待 | 初始化、批处理 |
| 异步连接池 | 非阻塞 | 高并发业务 |
| 连接池+线程池 | 解耦+并发 | 生产环境标准搭配 |
九、总结
数据库连接池核心价值:
+-------------------------------------+
| 1. 复用连接 --- 省去握手认证开销 |
| 2. 维持心跳 --- 防止连接断开 |
| 3. 并发提升 --- 多连接提升处理能力 |
| 4. 资源管理 --- 统一控制数量 |
+-------------------------------------+
选型建议:
初始化阶段 -> 同步连接池(简单快速)
业务处理阶段 -> 异步连接池(高吞吐)
性能优先 -> libmysqlclient(C库)
开发效率 -> libmysqlcppconn(C++库)
根据零声教育教学写作https://github.com/0voice