Flask应用扩容后Flask-APScheduler重复写入数据库问题求助
解决Flask-APScheduler扩容后重复数据问题
问题根源很明确:每扩容一个app实例,就会启动一个独立的APScheduler进程,所有实例的定时任务会同时触发,每个实例都往数据库写一遍用户数据,自然就产生重复了。下面给几个适合新手的解决办法:
方法一:用分布式锁控制任务唯一执行
用Redis实现分布式锁,利用Redis的原子操作保证同一时间只有一个实例能执行任务。步骤如下:
- 先安装redis库:
pip install redis
- 修改你的定时任务代码,添加锁逻辑:
import redis from flask import Flask from flask_apscheduler import APScheduler app = Flask(__name__) scheduler = APScheduler() scheduler.init_app(app) # 初始化Redis连接(docker-compose部署时,host填redis服务名即可) redis_client = redis.Redis( host='redis', port=6379, db=0, decode_responses=True ) @scheduler.task('cron', id='store-users', hour='*') def store(): # 定义锁的key,设置有效期(要比任务执行时长略长,避免死锁) lock_key = "task:store-users:lock" # setnx原子操作:只有当key不存在时才设置成功,代表拿到锁 if redis_client.set(lock_key, "locked", ex=300, nx=True): try: # 这里写你的用户入库逻辑 # store user in database print("执行用户存储任务") finally: # 任务完成后必须释放锁 redis_client.delete(lock_key) else: print("已有实例在执行任务,本次跳过") scheduler.start()
方法二:把定时任务单独拆成独立服务
把定时任务从Web应用里抽出来,做成一个单独的服务。部署时Web应用可以随便扩容,而定时任务服务只启动1个实例。
- 创建独立的
task_service.py文件,只保留定时任务逻辑:
from flask import Flask from flask_apscheduler import APScheduler app = Flask(__name__) app.config['SCHEDULER_API_ENABLED'] = True scheduler = APScheduler() scheduler.init_app(app) @scheduler.task('cron', id='store-users', hour='*') def store(): # store user in database pass scheduler.start() if __name__ == '__main__': app.run()
- 在docker-compose.yml里分别定义web服务和task服务,固定task服务的实例数:
version: '3' services: web: build: ./web ports: - "5000:5000" # 可按需扩容web服务 task-service: build: ./task-service # 固定只运行1个实例 deploy: replicas: 1
方法三:数据库层面加唯一约束(兜底方案)
在用户表的唯一标识字段(比如用户ID、邮箱)上添加唯一约束,就算多个实例同时执行插入,数据库会自动拒绝重复数据,避免脏数据。用SQLAlchemy的示例:
from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() class User(db.Model): id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(120), unique=True, nullable=False) # 邮箱设为唯一约束 # 其他字段...
推荐优先用方法一或方法二,方法三作为兜底,避免极端情况的漏网重复数据。
内容的提问来源于stack exchange,提问作者parastoo
相关产品推荐
相关产品推荐

