那天一个刚入行的朋友问我,Python怎么连 MySQL,我愣了一下——这个问题我十年前学的时候也问过。现在想想,Python 调用 MySQL 就像点外卖,看着复杂,实际上只要几步。你打开手机,选餐厅、下单、等餐、吃饭。Python 也一样:装个库、连上库、写个 SQL、取数据。关键是要知道哪些地方容易卡壳,比如密码写错、端口没开、或者表名拼错。

刚开始接触这个,大部分人第一反应是装 。这玩意儿官方出品,稳定,但用起来有点“重”。装完后导入,写 ,然后创建 cursor,执行 ,再 。每一步都像在填表,填完就跑通了。不过要注意,官方库的默认参数有时会坑你,比如字符集没设为 utf8,中文直接变乱码。我有个朋友 debug 了一个下午,发现是少了 。这种小细节文档里写得很清楚,但谁会一条条看完呢?
后来有人推荐 PyMySQL。这库轻量,纯 Python 实现,不用装 C 扩展,对新手友好。用 装完后,代码和官方库几乎一模一样,只是把 换成 。但有一个坑:PyMySQL 默认是自动提交关闭的。插入一条数据后不 commit,数据库里什么也没有。我见过不止一个新手,写了个 insert 语句,跑完没报错,一查表空空的,急得挠头。其实加个 就行,或者在创建连接时设 ,更省心。
说到连接参数,这是事故高发区。主机地址、端口、用户名、密码、数据库名,五个参数一个都不能错。开发时很多人喜欢用 ,但到了服务器上,MySQL 可能只监听 ,或者绑定了内网 IP。写 时,Python 解析成 socket 连接不上,会报错 “Can't connect to MySQL server”。这时换成 试试,有时就好了。还有端口,MySQL 默认 3306,但有人为了“安全”改了,你得确认。我见过最离谱的,是有人把密码设成空字符串,结果连接时没传密码参数,一直报权限错误。
写 SQL 语句也是门学问。Python 里写 SQL 时,很多人习惯用字符串拼接,但千万别这么干,容易导致 SQL 注入。比如写 ,用户输入 ,表就全暴露了。正确做法是参数化查询:。PyMySQL 会自动处理转义,安全又干净。而且参数化查询还能提高性能,因为 MySQL 会缓存执行计划。重复执行同一个 SQL,只是参数不同,效率会高不少。
取数据也有讲究。很多人习惯用 ,一次性把所有结果拉回内存。如果数据量小,比如几十行,没问题。但要是几十万行,内存会直接爆炸。我见过一个报表系统,每天跑一次全量数据,结果服务器内存被撑爆,导致其他服务也挂了。后来改成 或者 ,分批处理,内存稳得像老狗。还可以使用 cursor 的 (服务器端游标),不一次性加载全部结果,而是边读边取,特别适合大数据量。
连接池这东西,很多人一开始想不到。每次操作都新建连接,用完就关,频繁建立 TCP 连接开销很大。尤其是高并发场景,比如 Web API,每个请求都连一次 MySQL,数据库会被搞死。使用 或者 的连接池,预先创建一批连接,用的时候取一个,用完归还。这样连接复用,性能提升明显。但要注意,连接池里的连接可能因为网络超时而断开,需要定期检查或重连。比如设置 ,让连接每小时重置一次。
事务处理是另一个容易翻车的地方。很多人以为 execute 完数据就写进去了,其实不是。默认情况下,PyMySQL 自动提交是关闭的,你需要显式调用 。如果忘了,程序退出时未提交的事务会自动回滚,数据就丢了。另一方面,事务有隔离级别,默认是 REPEATABLE READ,对高并发写入场景可能不太友好。可以改成 READ COMMITTED,减少锁冲突,但要先确认业务是否允许。
错误处理不能只靠笼统的 try‑except。MySQL 会返回各种错误码,例如 1062 是唯一键冲突,1045 是权限错误。不要只捕获 然后打印“数据库连接失败”,这样根本排查不出问题。应该捕获 处理唯一键冲突,捕获 处理连接问题。而且在生产环境中不要打印完整的错误堆栈,以免泄露敏感信息。可以在日志里记录错误码和简短描述,给用户返回“操作失败,请重试”。
说说性能调优。很多人写 SQL 不注意索引,Python 再快也没用。比如查询用户邮箱,,如果 email 字段没有索引,就会全表扫描,几百万行数据要跑几秒。加个索引后,秒级变毫秒。还有,不要频繁执行相同结构的 SQL,可以用 cursor 的 批量插入。一次插入 1000 条数据,比 1000 次单条插入快一个数量级。另外,连接时设置 ,如果数据传输量大,压缩能减少网络开销。但别在小数据量上使用,压缩本身也有开销。
说到底,Python 调 MySQL 就是个熟练工。踩的坑越多,记得越牢。刚开始可能会被各种报错搞得崩溃,但熬过去后会发现其实就是几套套路。而且现在有 ORM 框架比如 SQLAlchemy,帮你屏蔽了很多底层细节,但理解原理仍然重要。毕竟出了问题,你得知道是 Python 的锅还是 MySQL 的锅。


