在Python生态里操作MySQL,pymysql是最常被选用的纯Python驱动之一。它不依赖任何C扩展,兼容Python3各个版本,安装和使用都比较轻量。理解它的工作模型,核心就是先拿到一个连接对象,再基于连接创建游标,通过游标发送SQL并取回结果。

一、安装与基础连接
使用pymysql的第一步是安装包。因为它已发布到PyPI,直接用pip即可完成,不需要本机安装MySQL开发头文件。在虚拟环境中安装可以避免污染全局Python。
pip install pymysql
建立连接时,需要明确主机、端口、用户、密码和库名。下面是一段最基础的连接代码,其中设置了字符集为utf8mb4,避免中文写入变成问号。
import pymysql
# 创建连接
conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='your_password',
database='test_db',
charset='utf8mb4'
)
# 使用完记得关闭
conn.close()
上面代码在脚本结束前手动关闭了连接。实际项目中更推荐用with语句管理生命周期,这样即使中间抛异常也能保证连接释放,减少MySQL服务端连接数被占满的风险。
二、执行查询与增删改
连接建立后,要通过cursor对象执行SQL。pymysql默认游标返回的是元组,若想用字典方式按字段名取值,可指定DictCursor。下面的例子演示了查询和插入两种典型操作。
import pymysql
from pymysql.cursors import DictCursor
conn = pymysql.connect(
host='127.0.0.1',
user='root',
password='your_password',
database='test_db',
charset='utf8mb4'
)
try:
with conn.cursor(DictCursor) as cursor:
# 查询
cursor.execute('SELECT id, name FROM users WHERE age > %s', (18,))
rows = cursor.fetchall()
for row in rows:
print(row['name'])
# 插入
sql = 'INSERT INTO users(name, age) VALUES(%s, %s)'
cursor.execute(sql, ('张三', 20))
conn.commit()
except Exception as e:
conn.rollback()
print('出错回滚:', e)
finally:
conn.close()
注意增删改操作必须调用conn.commit()才能真正落库,这是MySQL事务引擎的要求。如果忘记提交,程序退出后改动会丢失。遇到异常时应当rollback,防止产生半成品数据。
参数化查询里用的%s不是Python字符串格式化,而是pymysql的占位符,它会在底层做转义,有效防止SQL注入。千万不要用字符串拼接拼SQL,否则用户输入单引号就能篡改语义。
三、批量操作与防注入
当需要写入大量数据时,逐条execute效率很低。pymysql提供了executemany,可以在一次网络往返里发送多组参数,显著提升吞吐。
import pymysql
conn = pymysql.connect(host='127.0.0.1', user='root', password='your_password', database='test_db')
with conn.cursor() as cursor:
data = [('李四', 22), ('王五', 25), ('赵六', 19)]
cursor.executemany('INSERT INTO users(name, age) VALUES(%s, %s)', data)
conn.commit()
conn.close()
防注入方面,除了坚持参数化,还应避免把表名和字段名用参数传进去,因为占位符只适用于值。如果表名动态变化,应在白名单中校验后再拼入SQL,且绝不可来自用户直接输入。
另外,从外部读取的CSV或接口数据,可能包含特殊字符,pymysql会自动转义单引号和反斜杠,但开发者仍需确认charset设置一致,否则可能出现乱码导致语句解析异常。
四、连接池与异常处理
短连接频繁创建销毁会带来明显开销。虽然pymysql本身不带连接池,但可结合DBUtils等库实现。以下示例展示用PersistentDB维持每个线程独立连接。
from dbutils.persistent_db import PersistentDB
import pymysql
pool = PersistentDB(
creator=pymysql,
maxusage=1000,
host='127.0.0.1',
user='root',
password='your_password',
database='test_db',
charset='utf8mb4'
)
conn = pool.connection()
try:
with conn.cursor() as cur:
cur.execute('SELECT 1')
print(cur.fetchone())
finally:
conn.close()
异常处理上,应捕获pymysql.err.OperationalError来区分连接超时、认证失败等不同情况。对于临时性网络抖动,可加重试逻辑;对于认证错误,重试无意义,应直接告警。
合理设置connect_timeout和read_timeout也能避免脚本卡死。在Web服务中,把连接池大小控制在数据库最大连接数的七成以内,可留余地给运维命令使用。
五、常见坑与排查思路
编码问题是高频坑。若数据库是utf8mb4而连接用了utf8,写入emoji会报错。统一charset为utf8mb4最稳妥。其次是时区,MySQL的time_zone若与程序所在机器不一致,时间字段会出现偏差,可在连接串加init_command设置。
conn = pymysql.connect(
host='127.0.0.1',
user='root',
password='your_password',
database='test_db',
charset='utf8mb4',
init_command="SET time_zone='+08:00'"
)
还有一种是游标未关闭导致内存占用高。fetchall一次性拉全表在大数据量时会撑爆内存,应改成分页fetchmany或服务端游标。遇到Commands out of sync错误,通常是上一条查询的结果集没读完就执行新命令,需确保先消费完再继续。
掌握上述用法后,Python配合pymysql处理日常MySQL任务已经足够。后续可进一步了解事务隔离级别和慢查询日志,把数据层做得更稳。
pymysqlPython_MySQL数据库连接修改时间:2026-08-08 18:33:32