在数据处理领域,MySQL作为最流行的开源关系型数据库之一,与Python生态的结合已经成为现代开发的标配。PyMySQL作为纯Python实现的MySQL客户端库,相比MySQLdb具有更好的兼容性和更简单的安装流程,特别适合Python 3.x环境下的数据库操作。
我曾在多个数据密集型项目中采用PyMySQL方案,包括一个日处理百万级订单的电商系统。在这个过程中积累了一些实战经验,也踩过不少性能坑。本文将分享从基础连接到高级特性的完整操作指南,包含那些官方文档没写但实际开发中一定会遇到的细节问题。
当前稳定版PyMySQL(1.0.2)支持Python 3.6+和MySQL 5.5+,安装只需执行:
bash复制pip install pymysql
注意:生产环境强烈建议固定版本号,避免自动升级导致兼容性问题。我曾遇到1.0.0版本批量插入时内存泄漏的问题,锁定0.10.1版本才解决。
高频访问场景下,直接创建连接会导致性能瓶颈。推荐使用连接池方案:
python复制import pymysql
from DBUtils.PooledDB import PooledDB
pool = PooledDB(
creator=pymysql,
maxconnections=20, # 根据服务器内存调整
mincached=5, # 启动时预创建连接数
host='localhost',
user='root',
password='your_password',
database='test',
charset='utf8mb4'
)
实测表明,在4核8G服务器上,该配置可支撑约1500 QPS的查询压力。连接数并非越多越好,超过50个连接时上下文切换开销会抵消并发优势。
错误示范(易受SQL注入):
python复制cursor.execute(f"SELECT * FROM users WHERE name='{user_input}'")
正确做法:
python复制cursor.execute("SELECT * FROM users WHERE name=%s", (user_input,))
我在安全审计中发现,即使内部系统也要严格参数化。曾有用例通过精心构造的UTF-8编码绕过简单过滤。
常规方案(效率低):
python复制for item in data:
cursor.execute("INSERT...")
高效方案(提升50倍+):
python复制# 使用executemany
sql = "INSERT INTO table(col1,col2) VALUES(%s,%s)"
cursor.executemany(sql, [(1,'a'), (2,'b')])
# 或拼接批量SQL(注意长度限制)
values = ",".join([f"({v1},{v2})" for v1,v2 in data])
cursor.execute(f"INSERT INTO table(col1,col2) VALUES {values}")
关键参数:max_allowed_packet需调大(默认4MB),事务批量提交(每1万条commit一次)
处理百万级数据时,传统fetchall()会导致内存溢出:
python复制cursor = conn.cursor(pymysql.cursors.SSDictCursor) # 使用服务端游标
cursor.execute("SELECT * FROM huge_table")
while True:
row = cursor.fetchone()
if not row: break
process(row)
金融级操作示例:
python复制def transfer_funds():
for _ in range(3): # 最大重试次数
try:
with conn.begin() as txn:
# 扣款
cursor.execute("UPDATE accounts SET balance=balance-100 WHERE user_id=1")
# 存款
cursor.execute("UPDATE accounts SET balance=balance+100 WHERE user_id=2")
break
except pymysql.err.OperationalError as e:
if not is_retryable_error(e): raise
continue
| 错误代码 | 含义 | 解决方案 |
|---|---|---|
| 2003 | 连接拒绝 | 检查防火墙/MySQL服务状态 |
| 1045 | 认证失败 | 核对用户名密码/主机权限 |
| 1213 | 死锁 | 重试事务或调整隔离级别 |
| 2013 | 查询超时 | 优化SQL或增加wait_timeout |
MySQL默认8小时断开闲置连接,需在连接串添加:
python复制conn = pymysql.connect(
...,
ping=1 # 每次执行前ping检测
)
或者在连接池配置中设置:
python复制pool = PooledDB(..., ping=7) # 7表示连接取出时检测
在my.cnf中启用慢查询日志:
ini复制slow_query_log = 1
slow_query_log_file = /var/log/mysql-slow.log
long_query_time = 1 # 超过1秒记录
通过PyMySQL获取执行计划:
python复制cursor.execute("EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE user_id=123")
print(cursor.fetchone()[0]) # 获取详细执行计划
未优化前(全表扫描):
sql复制SELECT * FROM orders WHERE DATE(create_time) = '2023-01-01'
优化方案:
sql复制-- 添加函数索引
ALTER TABLE orders ADD INDEX idx_create_date ((DATE(create_time)))
-- 或改写查询
SELECT * FROM orders
WHERE create_time BETWEEN '2023-01-01 00:00:00' AND '2023-01-01 23:59:59'
实测显示,优化后查询耗时从2.3秒降至12毫秒。