apscheduler任务不持久主因是jobstore选错:sqlite内存模式重启即丢,需用sqlalchemyjobstore配mysql/postgresql并注意连接池与事务;crontrigger按系统时钟对齐,intervaltrigger依赖上次执行完成时间;web多进程需redis分布式锁防重复启动;未捕获异常会导致静默失败,须配置日志及错误监控。

APScheduler 的 jobstore 选错会导致任务不持久
SQLite 默认只存内存,重启后所有定时任务就丢了。用 SQLAlchemyJobStore 配合 MySQL 或 PostgreSQL 才能真正持久化,但要注意连接池和事务隔离级别——比如在高并发添加任务时,add_job() 可能因唯一约束冲突失败。
- 开发阶段可先用
MemoryJobStore快速验证逻辑,但上线前必须切到数据库后端 - 若用 SQLite 文件路径,要确保进程有写权限,且路径是绝对路径(相对路径容易因工作目录变化失效)
- PostgreSQL 用户注意:表名默认带 schema,如
public.apscheduler_jobs,若改过 search_path,需显式指定
trigger 类型混用会触发意外执行频率
CronTrigger 和 IntervalTrigger 行为差异很大:CronTrigger 按系统时钟对齐(比如设成每小时 0 分执行,哪怕上次延迟了 5 分钟,下次仍卡在 xx:00),而 IntervalTrigger 是“上一次执行完 + 间隔”,可能越积越慢。
- 需要严格按时点运行(如每天凌晨 2 点跑报表),必须用
CronTrigger,别用IntervalTrigger(hours=24) -
CronTrigger(day_of_week='mon-fri')不会跳过节假日,真要排除需额外加逻辑判断日期 - 使用
start_date和end_date时,注意时区:传datetime对象必须带 tzinfo,否则按本地时区解析,部署到 UTC 服务器可能偏差 8 小时
Web 应用里启动 scheduler 容易重复初始化
Django 或 Flask 多 worker 模式下,每个进程都调 start() 会起多份 scheduler,导致任务重复执行。APScheduler 本身不提供分布式锁,得靠外部协调。
- Flask 中推荐在应用工厂函数外单例初始化,再用
app.before_first_request(已弃用)或更稳妥的gunicorn --preload+ 主进程判断os.getpid() == os.environ.get('MAIN_PID') - Django 可用
django-apscheduler封装层,它依赖数据库锁表机制,但要注意remove_all_jobs()会清空所有实例的任务,别在迁移脚本里误调 - 最简方案:用 Redis 做分布式锁,每次
start()前SETNX apscheduler:lock 1 EX 30,抢到锁的进程才启动 scheduler
job 执行异常未捕获会让 scheduler 静默失败
默认情况下,job 抛出异常只会记日志,scheduler 继续跑下一个任务,但你可能根本没开 debug 日志,结果发现任务“好像没跑”,其实是崩在半路了。
- 务必配置
logging,至少把apscheduler.executors.default的 level 设为WARNING以上 - 在 job 函数里包一层
try/except,把关键错误发到监控(如 Sentry)或写入 DB 表,别只打 print - 用
max_instances=1防止同一任务并发堆积;若任务本身耗时长,考虑加misfire_grace_time=None,否则错过执行窗口会被直接丢弃
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











