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

2019-01-29 18:37:46 +08:00
 leochan32767

使用 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 的总和 ,然后判断是否允许添加数据 [总和大于某一值时,不允许添加]

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

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

3569 次点击
所在节点    Python
16 条回复
pabupa
2019-01-29 19:45:13 +08:00
https://www.v2ex.com/t/531596

看一下这个帖子吧,,,应该有帮助。
kaneg
2019-01-29 19:53:53 +08:00
你的 flask server 应该是运行在多线程或者多进程模式,所以出现了并发问题。

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

前端加一个点击后禁掉点击事件,根据后台返回结果再处理是否可行呢?
tomczhen
2019-01-29 23:03:20 +08:00
数据库上根据 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。
alvin666
2019-01-29 23:06:00 +08:00
并发大的话用乐观锁,改一下表结构和 update 语句就行了,并发小的话用悲观锁

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/531672

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX