Celery Beat任务不执行的首要原因是任务注册失败:CELERY_BEAT_SCHEDULE中配置的函数路径未被celery实例成功import,如'myapp.tasks.cleanup'未用@shared_task装饰或未在celery.py中显式导入,导致beat无法识别和派发任务。

celery-beat 启动后任务不执行?检查 CELERY_BEAT_SCHEDULE 的键名和函数路径
最常见的情况是任务注册失败:celery-beat 只认 CELERY_BEAT_SCHEDULE 里写的函数路径,但实际没被 celery 实例 import 进来。比如你写了 'clean_expired_records': {'task': 'myapp.tasks.cleanup', ...},却忘了在 myapp/tasks.py 里用 @shared_task 装饰 cleanup,或者没在 myapp/__init__.py 或 celery.py 中触发该模块加载。
实操建议:
立即学习“Python免费学习笔记(深入)”;
-
tasks.py必须被 celery worker 和 beat 同时 import 到——推荐在celery.py的__all__或初始化逻辑中显式导入myapp.tasks - 函数路径必须可 import:运行
python -c "from myapp.tasks import cleanup"验证是否报错 - 键名(如
'clean_expired_records')只是标识,不影响执行;真正决定调用哪个函数的是'task'字段的字符串值 - 如果用
django-celery-beat(数据库驱动),则不用配CELERY_BEAT_SCHEDULE,改用管理后台或PeriodicTask.objects.create(...)
Django 模型清理逻辑写在哪?别在 tasks.py 里直接写 ORM 查询
把数据清理逻辑硬编码进 tasks.py,会导致测试难、复用差、事务边界模糊。更稳妥的做法是把核心逻辑抽到 Django app 的 management/commands/ 下,再让 celery task 调用它。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 新建
myapp/management/commands/clean_expired.py,继承BaseCommand,实现handle(self, *args, **options) - 在
tasks.py中用call_command('clean_expired')触发——这样既能复用命令行调试,也自动获得 Django 的数据库连接和事务上下文 - 避免在 task 函数里手动
Model.objects.filter(...).delete():Django 的delete()不触发pre_delete/post_delete信号,且批量删除不走 model 层校验 - 若需信号支持或自定义清理逻辑(如清理关联文件),改用
for obj in qs.iterator(chunk_size=1000): obj.delete()
定时频率设成 */5 * * * * 但实际每 10 分钟跑一次?注意 celery-beat 的调度精度
celery-beat 默认以 5 秒为最小粒度轮询 CELERY_BEAT_SCHEDULE,即使你配置了 crontab(minute='*/5'),也可能因 worker 启动延迟、任务排队、或时区未对齐,导致首次执行偏移。尤其在开发环境用 celery -A proj beat -l info 启动时,日志里会显示 “Scheduler: Sending due task …” 的实际触发时间。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 确认
CELERY_TIMEZONE和TIME_ZONE一致(如都设为'Asia/Shanghai'),否则 crontab 解析会错位 - 避免用
*/1 * * * *做高频清理——Django ORM 批量操作本身有开销,建议结合created_at__lt=timezone.now() - timedelta(hours=24)加索引字段过滤 - 生产环境务必用
celery -A proj beat -S django_celery_beat.schedulers:DatabaseScheduler(即django-celery-beat),而非默认的FilesystemScheduler,否则多实例部署时调度会冲突 - 用
django-celery-beat后,可通过 Admin 修改周期,无需重启 beat 进程
清理任务卡住或报 OperationalError: database is locked?关注事务隔离与并发控制
SQLite 下尤其明显:一个 celery worker 正在执行长事务(如遍历十万条记录并逐条 delete),另一个任务尝试读写同一张表,就容易锁表。PostgreSQL 虽好些,但若清理逻辑没加 select_for_update() 或没分页处理,仍可能引发死锁或慢查询。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 所有清理任务必须加超时:在
@shared_task(soft_time_limit=300, time_limit=360)中限制运行时长 - 用
QuerySet.iterator(chunk_size=1000)替代全量list(qs),减少内存和事务持有时间 - 若涉及多表级联清理,先查 ID 列表(
.values_list('id', flat=True)),再分批filter(id__in=...)删除,避免大 JOIN - 关键表的
created_at或expires_at字段务必建索引,否则WHERE expires_at 会全表扫描
真正麻烦的不是配置 beat,而是清理逻辑怎么跟 Django 的生命周期对齐:command 是否能被 signal 拦截、task 是否该重试、失败后要不要告警——这些得根据数据重要程度一个个权衡,没法套模板。


















