You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask应用扩容后Flask-APScheduler重复写入数据库问题求助

解决Flask-APScheduler扩容后重复数据问题

问题根源很明确:每扩容一个app实例,就会启动一个独立的APScheduler进程,所有实例的定时任务会同时触发,每个实例都往数据库写一遍用户数据,自然就产生重复了。下面给几个适合新手的解决办法:

方法一:用分布式锁控制任务唯一执行

用Redis实现分布式锁,利用Redis的原子操作保证同一时间只有一个实例能执行任务。步骤如下:

  1. 先安装redis库:
pip install redis
  1. 修改你的定时任务代码,添加锁逻辑:
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个实例。

  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()
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 21:32:21