逻辑代码如下:
...
...
...
project = Project.create!(:name => "name", ...)
api = Api.client.update(...) # 这是一个调用第三方api的一个更新操作
if project.present?
User.update_column(...)
end
...
...
...
现在的问题是在高并发下,这段代码可能会被执行多次,有方法对这整过过程加锁吗?类似于 lock/unlock
如果仅对 Project 加锁,
roject.lock.create!(...)
加锁,后面的 api...这段代码应该还是有可能执行多次吧? Rails Guide 对锁的介绍好抽象,望大神能帮解惑,非常感谢!
Redis::Lock.new("#{cache_key}").lock do
project = Project.create!(:name => "name", ...)
api = Api.client.update(...) # 这是一个调用第三方api的一个更新操作
end
抛一个思路
2 楼、3 楼的两位,只是单纯地给楼主几行 COPY 代码而已,能解决问题,但不一定是最好的方案。
这段代码是用户点击一个按钮然后后台会执行这系列的操作,而且这些步骤不能少,必须在这个点击操作中完成 具体代码如下:
project = Project.where(...).first
if project.present?
redirct_to project_path(...)
else
ActiveRecord::Base.transaction do
project = Project.create!(:name => "name", ...)
api = Api.client.update(...) # 这是一个调用第三方api的一个更新操作
User.update_column(...)
end
end
所以一旦用户量多了,出现大并发情况,用户多次点击,就很有可能多次走 else,数据就会被多创,之前用数据库唯一索引(validates_uniqueness_of),是可以保证不会重复创建,但是重创的时候抛出的异常体验很不好 我想的是能不能通过锁能不能解决这个问题: 比如:
project = Project.where(...).lock(true).first
如果这样是不是就能解决问题,但是这个锁是不是只对 projects 表锁定,并发情况下还会走 else 吗?会出现多次创建的问题吗? 模拟并发环境好麻烦 对于锁我看了官方 guid,但是感觉好抽象,请指教,感谢!
可以先考虑在前端用户点击层,先进行防止短时间快速重复点击的过滤。 然后,数据库加唯一索引其实是挺好的操作,不喜欢报错异常可以捕捉异常。 使用悲观锁会降低数据库并发能力,还不如唯一索引报错 赞同楼上说的,after 这种操作习惯性多了之后,基本就“失控了” 能用别的方法解决问题的,我不会首选用锁的机制。除了这显然用锁是最好的情况
可能还是需要使用锁(悲观锁) 发现一个很奇怪的问题:validates_uniqueness_of 在资源竞争比较激烈的时候不能起到作用 还是会有重复的记录 比较疑惑,这是什么鬼... @pathbox
validates_uniqueness_of 的官方解释,不理解为什么还是用重复记录被创建
# When the record is created, a check is performed to make sure that no record exists in the database
# with the given value for the specified attribute (that maps to a column). When the record is updated,
# the same check is made but disregarding the record itself.
#
@huacnlee 求助,tks
validates_uniqueness_of
这个失效的问题 我遇到过,两个参数相同的请求相差几毫秒,竟然通过了 validates_uniqueness_of 的验证。后来我在数据库表加唯一索引,来避免重复记录的情况,还是相信数据库的性能足够强大
@return 因为高并发的情况第一条数据通过 validate 但是还未在 DB 层面 commit,接着第二条在很短的时间内也通过了验证,这样 validates_uniqueness_of 就没什么卵用。所以只能从有原子性操作的地方入手,数据库唯一索引稳稳的解决你的问题 也可以通过 Redis SETNX lock 处理