如何对Django非view函数及数据库查询做profiling并对接现有调试工具?
Django非视图函数与Celery定时任务Profiling方案
可直接使用的Profiling工具
- django-silk原生支持非视图函数采集:silk本身提供了通用的装饰器,不用绑定视图请求上下文,普通函数、celery任务都可以直接接入。
装饰器导入方式为from silk.profiling.profiler import silk_profile,给目标函数加上@silk_profile()装饰器即可,加完之后所有的运行时延、执行的SQL查询语句、SQL耗时、调用栈都会自动同步到silk的后台面板中。如果是Celery任务,注意将装饰器放在@app.task/@shared_task的内层,示例如下:from celery import shared_task from silk.profiling.profiler import silk_profile @shared_task @silk_profile(name="定时数据同步任务") def sync_database_data(): # 任务业务逻辑 pass - django-debug-toolbar适配方案:debug-toolbar默认绑定HTTP请求生命周期,没有直接支持非视图函数的装饰器。如果需要用它来分析函数性能,可以写一个临时的内部测试视图,在视图内调用你要分析的目标函数,访问该视图触发函数执行后,就能在debug-toolbar面板中看到完整的耗时、SQL查询、调用栈数据。
- 无侵入式线上排查工具:如果不需要对接现有监控面板,只是临时排查性能问题,可以用
py-spy直接attach到Celery worker进程或者Django运行进程,不需要修改业务代码就能拿到全量函数的耗时分布、调用占比数据。
轻量自定义Profiling装饰器
如果不想引入重的第三方工具,可以自己封装装饰器实现耗时和SQL查询统计,示例如下:
from django.db import connection import time from functools import wraps def profile_function(func): @wraps(func) def wrapper(*args, **kwargs): start_at = time.perf_counter() initial_sql_count = len(connection.queries) try: return func(*args, **kwargs) finally: total_cost = time.perf_counter() - start_at run_sql_count = len(connection.queries) - initial_sql_count sql_total_cost = sum(float(q["time"]) for q in connection.queries[initial_sql_count:]) # 这里可以自定义输出逻辑,比如打印到控制台、写入日志、上报到监控系统 print(f"函数 {func.__name__} 执行总耗时: {total_cost:.3f}s | 执行SQL数: {run_sql_count} | SQL总耗时: {sql_total_cost:.3f}s") return wrapper
该装饰器可以直接加在任意普通函数、Celery任务上,不需要额外依赖。
注意事项
- 生产环境开启Profiling时要注意性能损耗,django-silk可以通过配置
SILK_PROFILE = False默认关闭采集,仅给需要排查的函数单独加装饰器即可。 - Celery beat触发的定时任务和普通Celery worker执行的任务逻辑完全一致,只需要给对应的task函数加装饰器就能正常采集数据,不需要对beat做额外适配。
内容的提问来源于stack exchange,提问作者mhk
相关产品推荐
相关产品推荐

