首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python 学习手册
Python Cookbook
Python 基础教程
Python Sites
PyPI - Python Package Index
http://www.simple-is-better.com/
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
拉勾
V2EX  ›  Python

求教大神一个 flask 技术问题,百思不得其解

  •  
  •   leochan32767 · 48 天前 · 1382 次点击
    这是一个创建于 48 天前的主题,其中的信息可能已经有所发展或是发生改变。

    使用 python 的 flask 框架搭建了一个服务,用于公司内部的管理系统,公司比较大,员工比较多,有时候访问比较频发,最近发生了如下的一个问题,苦思冥想各种解决方法都试过了,还是无法解决,江湖告急 1 用的数据库是 mysql
    2 表的引擎是 innodb 3 orm 用的是 SQLAlchemy

    问题:

    添加数据的时候,使用 load.reqMoney = 100000 db.session.add(load) db.session.commit() 并且在添加数据前使用了 db.session.query(func.sum(Load.reqMoney)).filter(Load.uid == xxx).scalar() 的语句,进行查询 load 的 reqMoney 的总和 ,然后判断是否允许添加数据 [总和大于某一值时,不允许添加]

    但是生产环境的时候,经常会出现 !!!判断失效的情况!!!,用户连续点击添加数据(请求时间非常短), 判断失效,用户可以添加总和大于某一值的情况,请问这个是为什么,怎么解决 [个人对数据库的知识比较薄弱,是否是数据库锁的问题?代码层面怎么解决] ?

    能帮小弟解决的大神送小蓝杯咖啡十杯,以表敬意

    16 回复  |  直到 2019-01-29 23:06:00 +08:00
        1
    pabupa   48 天前 via Android
    https://www.v2ex.com/t/531596

    看一下这个帖子吧,,,应该有帮助。
        2
    kaneg   48 天前 via iPhone
    你的 flask server 应该是运行在多线程或者多进程模式,所以出现了并发问题。

    如果是多线程模式,不考虑太复杂的情况下,可以加个锁
        3
    leochan32767   48 天前
    @kaneg 的确,开了 16 个 gunicorn 请问下加锁有什么教程吗?
        4
    wwqgtxx   48 天前
    借助 redis 或者是 multiprocessing.Manager 很容易实现跨进程锁,我自己还造过这方面的轮子
    https://github.com/wwqgtxx/RedisTools
        5
    kaneg   48 天前
    可以看看这个链接最后的 Lock 部分: http://blog.jobbole.com/52060/
        6
    Trim21   48 天前 via Android
    你需要一个分布式锁
        7
    okwork   48 天前
    多个 worker 不加锁,出现超卖的情况不可避免,严谨的做法就是加锁。也有粗暴的做法,很多秒杀排队也是简单粗暴的抛弃请求,比如你可以把相邻时间小于 1 秒钟的请求丢弃,具体返回个什么值或提升,根据具体场景设计。
        8
    cz5424   48 天前 via iPhone
    看看能否把高频的判断值扔在 redis,或者函数上 redis 锁,保证高并发只有单个在处理
        9
    guog   48 天前 via Android
    最简单的就是使用 redlock,一个基于 Redis 的简单锁。加几行代码的事。
        10
    ziding   48 天前
    pg 的话可以意向锁或者 for updte,mysql 看看有没有 for update 的等价物。
        11
    dagger   48 天前   ♥ 1
    查询语句把用户表一起 join 出来,用 for update 锁住,记得建好索引,不然 mysql 的 for update 会锁全表
        12
    zeraba   48 天前 via Android   ♥ 1
    锁 MySQL 已经自己做了,判断大于总和这个逻辑可以在执行插入的时候判断 比如 update price where amount > 10000 而不是前端拿这个作为条件去判断能否执行插入
        13
    ericls   48 天前 via iPhone
    在数据库那边应该加个锁
    MySQL 有 select for update. Isolation 等级可能要改一下
        14
    zsen   48 天前
    如果基于以下的条件:
    1、用于公司内部的管理系统
    2、用户连续点击添加数据(请求时间非常短)

    前端加一个点击后禁掉点击事件,根据后台返回结果再处理是否可行呢?
        15
    tomczhen   48 天前
    数据库上根据 select 结果来 update,多线程下要么使用幂等逻辑,类似:update status = 1 where id =1 and status =0; update sum = 19 where id= 1 and sum = 10,要么 select 就要上锁。

    加锁是为了保证一致性,会影响并发数。而且还得注意索引,确保锁的粒度,当然,内部系统没那么讲究的话其实也没所谓。

    redis 也行,毕竟单线程。另外如果取 key,重新写 value 不是原子( redis lua )操作的话,还是会出现并发问题。

    至于前端限制,为了并发最好也是要做的,减少无谓的请求,web 页面可以考虑 Redirect After Post。
        16
    alvin666   48 天前 via Android
    并发大的话用乐观锁,改一下表结构和 update 语句就行了,并发小的话用悲观锁
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   3858 人在线   最高记录 4385   ·  
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 20ms · UTC 05:42 · PVG 13:42 · LAX 22:42 · JFK 01:42
    ♥ Do have faith in what you're doing.
    沪ICP备16043287号-1