根本原因是MySQL连接层未启用UTF-8,需在SQLAlchemy create_engine中显式指定charset=utf8mb4;读大表易OOM,应使用chunksize分块读取。

SQLAlchemy连接MySQL时charset没设对,中文全变问号
根本原因不是Python或pandas的问题,是MySQL连接层默认没走UTF-8。SQLAlchemy的create_engine里不显式指定charset=utf8mb4,哪怕数据库、表、字段全是utf8mb4,Python读出来还是乱码。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 在
create_engine的URL末尾加?charset=utf8mb4,比如:mysql+pymysql://user:pwd@localhost:3306/db?charset=utf8mb4 - 别用
utf8——MySQL里的utf8其实是阉割版(最多3字节),存emoji或某些生僻字会截断;必须用utf8mb4 - 如果用
PyMySQL驱动,还要额外加connect_args={'charset': 'utf8mb4'}才保险,因为URL参数有时被忽略
read_sql查大表卡死或内存爆掉
read_sql默认把整张表一次性拉进内存,100万行+、带TEXT字段的表很容易OOM,或者卡住不动——它不是懒加载,也不支持流式取数。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 加
chunksize参数分块读,返回的是Iterator[pd.DataFrame],比如:read_sql("SELECT * FROM logs", engine, chunksize=10000) - 别在
read_sql里写SELECT *,只选需要的列,尤其避开JSON、TEXT、BLOB字段,它们会显著拖慢传输和解析 - 如果只是要统计或聚合,优先用SQL完成(
GROUP BY、COUNT),别把原始数据全捞上来再pandas.groupby
WHERE条件里传参被当成字符串字面量,SQL注入或类型错配
直接用f-string拼接read_sql的SQL,比如f"WHERE id = {user_id}",不仅危险,还容易让MySQL把数字当字符串比较(触发隐式转换,索引失效)。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 一律用
params参数传值:read_sql("SELECT * FROM users WHERE status = :status", engine, params={"status": "active"}) - SQLAlchemy会自动处理类型映射:传
int就当数值,传datetime就转成MySQL时间字面量,不会包单引号 - 注意
params只支持命名占位符(:name),不支持%s或?,写错会报UnboundParameterError
read_sql结果里datetime列变成string或NaT
常见于MySQL字段是TIMESTAMP但服务器时区设得怪,或SQLAlchemy没正确识别类型,导致pandas拿到的是字符串或object dtype,后续做时间运算全崩。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 建表时统一用
TIMESTAMP(带时区语义)或DATETIME(无时区),避免混用;连库时在create_engine加connect_args={'timezone': True}(PyMySQL) - 查完立刻用
pd.to_datetime()强转,加上errors='coerce'防脏数据:df['created_at'] = pd.to_datetime(df['created_at'], errors='coerce') - 如果发现
read_sql返回的dtype是object,大概率是某几行有非法时间值(如'0000-00-00 00:00:00'),MySQL默认允许这种值,但pandas解析失败
事情说清了就结束。最常漏的是charset=utf8mb4和chunksize,其他问题基本都围绕这两点展开。










