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

多用户并发下网站数据库单值自增的同步问题咨询

并发点击场景下全局计数器实现方案(Flask + MongoDB/Redis)

这个问题本质是读-改-写竞态冲突,如果采用「先查询数据库拿到当前x值 → 应用层代码执行x+1 → 把新值写回数据库」的流程,高并发下必然出现计数丢失、用户读到错误值的问题。核心解决思路是把自增操作下推到存储层执行原子命令,完全规避应用层计算带来的同步问题,两种存储选型的具体实现如下:

方案1:Redis实现(优先选,适合高频点击场景)

Redis单线程执行模型从根本上避免了并发冲突,原生自带的原子自增命令性能极高,适合点击量很大的场景。

  • 先安装依赖:pip install redis
  • Flask服务中初始化连接:
from flask import Flask
import redis

app = Flask(__name__)
# 生产环境记得配置密码、持久化规则
r = redis.Redis(host='127.0.0.1', port=6379, db=0, decode_responses=True)
# 初始化计数器,不存在则默认设为0
if not r.exists('click_counter:x'):
    r.set('click_counter:x', 0)
  • 点击接口直接调用INCR原子命令即可,命令执行完会直接返回自增后的最新值,不需要额外查询:
@app.route('/click', methods=['POST'])
def click_handler():
    current_x = r.incr('click_counter:x')
    return {"code": 0, "data": {"x": int(current_x)}}
  • 普通查询接口直接读值就行:
@app.route('/get_x', methods=['GET'])
def get_x():
    return {"code": 0, "data": {"x": int(r.get('click_counter:x'))}}

注意:如果担心服务重启丢数据,一定要开启Redis的RDB/AOF持久化配置,不要用默认的无持久化配置。

方案2:MongoDB实现(适合已有Mongo技术栈,不想额外引入组件的场景)

MongoDB单文档更新是天然原子的,直接用$inc自增操作符即可,不需要自己处理并发锁。

  • 先安装依赖:pip install pymongo
  • Flask服务中初始化连接:
from flask import Flask
from pymongo import MongoClient

app = Flask(__name__)
client = MongoClient('127.0.0.1', 27017)
db = client['counter_db']
counter_col = db['counters']
# 初始化计数器文档,不存在则插入初始值
if counter_col.count_documents({"_id": "x"}) == 0:
    counter_col.insert_one({"_id": "x", "value": 0})
  • 点击接口用find_one_and_update搭配$inc,可以在原子自增的同时直接返回更新后的文档,避免二次查询带来的不一致问题:
@app.route('/click', methods=['POST'])
def click_handler():
    updated_doc = counter_col.find_one_and_update(
        {"_id": "x"},
        {"$inc": {"value": 1}},
        return_document=True # 配置后返回更新后的新值,默认是返回更新前的旧值
    )
    current_x = updated_doc["value"]
    return {"code": 0, "data": {"x": current_x}}
  • 普通查询接口直接查单条文档即可:
@app.route('/get_x', methods=['GET'])
def get_x():
    doc = counter_col.find_one({"_id": "x"})
    return {"code": 0, "data": {"x": doc["value"]}}

必看避坑点

  • 别在Flask应用层自己加线程锁/进程锁:如果用gunicorn、uWSGI多进程部署服务,应用层锁不跨进程,完全起不到作用;就算单进程部署,锁也会严重拖低接口吞吐能力。
  • 别用应用层计算后写回的逻辑:高并发下多个请求会查到同一个x值,最终出现多次点击只加1的计数丢失问题。
  • 给用户返回自增结果时,一定要用存储层自带的「自增后返回新值」的能力,不要自增完成后再单独发一次查询请求,中间可能被其他请求更新,导致返回给用户的数值错误。

内容的提问来源于stack exchange,提问作者Nathan Dsouza

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 22:27:25