Skip to content

第 1 章 数据库连接配置 ​

从一个生活场景说起 ​

想象你经营着一家餐厅,食材都存放在城郊的一座仓库里。每当厨师要做菜,就得派人去仓库取货。

  • 方案 A:每次取货都临时雇一辆新车、现修一条路过去,用完就扔掉——效率低得离谱。
  • 方案 B:养一支固定的运输队,车辆和路线随时待命,用完归还、下次再用——高效且可控。

在程序世界里:

  • 仓库 = MySQL 数据库服务器;
  • 派人去仓库 = 程序向数据库发起一次"连接(Connection)";
  • 方案 A = 每次执行 SQL 都新建连接,用完关闭(开销极大);
  • 方案 B 的运输队 = 连接池(Connection Pool);
  • 管理运输队的调度中心 = Engine(引擎),也就是本章的主角。

理解了"Engine = 调度中心 + 运输队",这一章就学会了一大半。

核心概念 ​

什么是数据库连接? ​

MySQL 是一个独立运行的程序(通常监听在 3306 端口),你的 Python 程序是另一个程序。两个程序之间要通信,必须先建立一条"通道",这就是连接。

建立连接的过程类似打电话:拨号(TCP 握手)→ 验证身份(用户名密码认证)→ 才能开始通话(执行 SQL)。这个过程要耗费几十甚至上百毫秒——对只执行 1 毫秒就能完成的 SQL 来说,"打电话"的成本远超"通话"本身。

什么是 Engine(引擎)? ​

Engine 是 SQLAlchemy 连接数据库的"总闸",由 create_engine() 创建。它本身不直接干活,而是负责:

  1. 记住数据库在哪里(URL);
  2. 在内部维护一个连接池,管理一批可复用的连接;
  3. 当你要执行 SQL 时,从池子里分配一条连接给你。

一个应用程序通常只需要一个 Engine,全局共享。

什么是连接池(Connection Pool)? ​

连接池就是预先建好若干条连接放在"池子"里:

  • 要执行 SQL → 从池子借一条连接;
  • 用完了 → 归还到池子(注意:是归还,不是关闭!);
  • 池子里的连接可以被反复借用,避免了反复"打电话"的开销。

数据库 URL:数据库的"快递地址" ​

SQLAlchemy 用一串 URL 字符串描述数据库的位置,格式为:

数据库类型+驱动名://用户名:密码@主机:端口/数据库名?参数

把我们的连接串拆开看,就像拆一个快递地址:

mysql+pymysql://root:123456@localhost:3306/demo_db?charset=utf8mb4
│───────│      │───│ │────│  │───────────│  │─────│  │──────────────│
   ①            ②      ③          ④            ⑤            ⑥
编号部分含义快递类比
①mysql数据库种类(术语叫方言 dialect)哪个城市
②pymysql驱动名(Python 与 MySQL 之间的"翻译官")用哪家快递公司
③root:123456用户名和密码收件人的身份凭证
④localhost:3306主机地址和端口街道和门牌号
⑤demo_db具体哪个数据库哪一栋楼
⑥charset=utf8mb4通信使用的字符编码双方约定用什么语言交谈

术语解释 —— 驱动(Driver):SQLAlchemy 本身不直接和 MySQL 通信,它把指令交给一个叫"驱动"的底层库去执行。pymysql 就是最常用的 MySQL 驱动,需要用 pip install pymysql 单独安装。

示例代码(逐行讲解) ​

bash
pip install sqlalchemy pymysql
python
from sqlalchemy import create_engine

# MySQL 连接:root 是用户名,123456 是密码,demo_db 是数据库名
engine = create_engine(
    "mysql+pymysql://root:123456@localhost:3306/demo_db?charset=utf8mb4",
    echo=True,          # 打印所有执行的 SQL,学习阶段建议打开
    pool_size=5,        # 连接池常驻连接数(运输队的固定编制:5 辆车)
    max_overflow=10,    # 高峰期允许额外创建的连接数(忙不过来可临时加 10 辆)
    pool_recycle=3600,  # 连接存活 1 小时后回收重建(避开 MySQL 默认 8 小时断连)
    pool_pre_ping=True, # 取连接前先探测是否有效,防止拿到"死连接"
)

逐行说明:

  • create_engine(url):创建引擎。注意这一步只是"登记"了数据库地址,并没有真正拨号。
  • echo=True:让 SQLAlchemy 把每一条实际执行的 SQL 打印到控制台。学习阶段这是最好的老师——你能清楚看到自己的 Python 代码被翻译成了什么 SQL。
  • pool_size=5:池子里常备 5 条连接。
  • max_overflow=10:5 条都不够用时,允许临时再多建 10 条;高峰过后多出的会被关闭。
  • pool_recycle=3600:MySQL 有个"脾气"——一条连接闲置超过 wait_timeout(默认 28800 秒,即 8 小时)就会被服务端单方面挂断。让连接活满 1 小时就主动退休换新,可以避开这个问题。
  • pool_pre_ping=True:每次从池子借连接前,先轻轻"戳一下"(发送一个廉价的 ping),如果连接已被服务端断开,就换一条新的。生产环境强烈建议开启。

注意:create_engine 不会自动创建数据库,需要先在 MySQL 中手动建好:

sql
CREATE DATABASE demo_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

关键知识点 ​

概念说明
create_engine()创建引擎,参数为数据库 URL
echo=True控制台输出实际执行的 SQL,便于调试
连接池Engine 自动管理,复用连接避免频繁建连
pool_size池内常驻连接数
max_overflow超出 pool_size 后允许临时新增的连接数
pool_recycle定期回收连接,避免 MySQL wait_timeout 断连
pool_pre_ping使用前探测连接有效性,生产环境推荐开启
URL 格式mysql+pymysql://user:password@host:port/dbname?charset=utf8mb4

深入理解:连接池的一天 ​

用"运输队"比喻串一遍完整流程:

  1. 程序启动,create_engine() 创建了调度中心,但一辆车都还没雇(懒初始化);
  2. 第一条 SQL 来了,调度中心发现池子是空的,才雇了第 1 辆车(真正建立连接);
  3. SQL 执行完,车回到车库待命(连接归还而非关闭);
  4. 并发来了 8 个请求,5 辆常备车不够,临时加雇 3 辆(不超过 pool_size + max_overflow);
  5. 一辆车跑了满 1 小时,调度中心让它退休、换辆新的(pool_recycle);
  6. 每次派车前先检查车况,坏了的直接报废换新车(pool_pre_ping)。

进阶内容 ​

1. 不要把密码写死在代码里。 真实项目中连接串应放在环境变量或配置中心:

python
import os
from sqlalchemy import create_engine

engine = create_engine(os.environ["DATABASE_URL"])

2. 异步引擎预告。 高并发场景(如 FastAPI 异步视图)可用异步引擎,写法几乎一样,只是换成 create_async_engine 和异步驱动 aiomysql,详见第 17 章。

3. 一个进程一个 Engine。 Engine 是线程安全的,可以多线程共享;但不要每个请求都 create_engine(),那样等于每来一个客人就新组建一支运输队,完全失去了连接池的意义。

注意事项 ​

  • create_engine() 不会立刻连接数据库,真正建立连接发生在第一次执行 SQL 时。
  • 生产环境不要把 echo=True 打开,会产生大量日志。
  • 密码中的特殊字符(如 @、#、/)需要做 URL 编码,例如 @ 写成 %40。
  • 务必指定 charset=utf8mb4,否则中文和 emoji 会存储失败或乱码。
  • MySQL 默认 8 小时无活动会断开连接(wait_timeout),pool_recycle 应小于该值。
  • 连接池上限(pool_size + max_overflow)不要超过 MySQL 的 max_connections(默认 151),否则会出现"Too many connections"错误。

本章小结 ​

  • Engine = 连接数据库的调度中心,全局一个就够;
  • 连接池 = 可复用连接的集合,"借-用-还"三步避免重复建连;
  • 数据库 URL = 数据库的快递地址,各段各司其职;
  • 记住四个池参数:pool_size、max_overflow、pool_recycle、pool_pre_ping。