要暴露ORM真实SQL,Django需配置LOGGING使django.db.backends级别为DEBUG并启用console handler;SQLAlchemy初始化时设echo=True或运行时赋值engine.echo=True,需echo_pool='debug'才显示参数;均不可在生产全量开启。

怎么让 Django/SQLAlchemy 暴露真实执行的 SQL
ORM 生成的 SQL 经常和你写的 filter() 或 query.join() 对不上,尤其嵌套 select_related() 或 prefetch_related() 时。不看实际发给数据库的语句,优化就是蒙眼抓麻雀。
实操建议:
- Django:在
settings.py中打开LOGGING配置,把django.db.backends的日志级别设为DEBUG,且确保'handlers': ['console']生效;别只改LOG_LEVEL = 'DEBUG'—— 这个全局设置不触发 SQL 日志 - SQLAlchemy:初始化
Engine时传echo=True,或运行时动态启用:engine.echo = True;但注意,echo=True不会打印参数值,要看到绑定参数得加echo_pool='debug'(仅限连接池操作) - 两者都慎用生产环境全量开启:Django 的
DEBUG=True会额外记录所有请求上下文,SQLAlchemy 的echo在高并发下直接拖垮日志 IO
为什么 MySQL general_log 不能直接开,而要用 slow_query_log + long_query_time=0
general_log 记录所有语句(含 COMMIT、SET、心跳包),写磁盘极重,MySQL 8.0 后默认禁用,开一次可能让实例 CPU 突增 40%+;slow_query_log 虽名“慢”,但设 long_query_time=0 就等价于全量捕获,且只记 SELECT/INSERT/UPDATE/DELETE 等核心语句,开销低一个数量级。
实操建议:
- 临时开启:执行
SET GLOBAL slow_query_log = ON和SET GLOBAL long_query_time = 0,立刻生效,无需重启 - 日志路径由
slow_query_log_file控制,默认在datadir下,确认磁盘空间够用再开,否则 MySQL 可能因写失败 crash - 别依赖
log_output='TABLE':写入mysql.slow_log表看似方便查,但该表是CSV引擎,无索引、不支持WHERE,查 10 分钟日志就得扫全表
ShardingSphere / MyCat 等中间件里,SQL 被改写后怎么定位原始 ORM 调用
中间件会重写 SQL(比如把 SELECT * FROM t_user 改成 SELECT * FROM t_user_001),但日志里只显示改写后的语句,你根本不知道这是来自 Django 的 User.objects.get(id=123) 还是 SQLAlchemy 的 session.query(User).filter(User.id == 123)。
实操建议:
- 强制 ORM 加入可追踪注释:Django 用
.extra(comment='[biz=user_login]'),SQLAlchemy 用text('/* [biz=user_login] */ SELECT ...');中间件一般保留注释,grep 就能反向匹配业务场景 - 中间件开启
sql.show(ShardingSphere)或log4j.logger.druid.sql=DEBUG(MyCat),但注意这些日志默认不包含客户端 IP 和线程 ID,需配合应用层打点(如 Python 的threading.current_thread().name)做关联 - 避免用中间件的“SQL 审计”功能替代 ORM 日志:它看不到
IN参数膨胀、N+1 查询的源头,只能看到最终合并结果
为什么 SHOW PROFILES / SHOW PROFILE FOR QUERY 不适合线上排查
SHOW PROFILES 只保存最近 15 条查询的耗时快照(由 profiling_history_size 控制),且只对当前连接有效;SHOW PROFILE FOR QUERY N 更麻烦——你得先知道 N 是多少,而 N 是会滚动覆盖的。线上突发慢查,等你登录上去,对应记录早就没了。
实操建议:
- 别依赖它做监控:它的设计初衷是本地调试,不是可观测性工具
- 真要用,必须搭配
SET profiling = 1提前开启,且每个连接独立,Web 应用里每个请求都是新连接,基本不可控 - 替代方案更可靠:用
performance_schema.events_statements_history_long(MySQL 5.6+),查SQL_TEXT和TIMER_WAIT字段,数据保留时间由performance_schema_events_statements_history_long_size决定,可调到 10000 条以上
真正难的是把 ORM 调用栈、中间件路由逻辑、数据库执行计划三者串成一条链——日志格式不统一、时间戳不同源、服务跨进程,光靠开几个开关解决不了问题。得在关键节点埋轻量级 trace_id,不然永远在猜哪一行代码触发了那条慢 SQL。

















