Rails 6.1中无需原生SQL实现ActiveRecord关系加锁更新的方法
Rails 6.1 锁行更新的官方实现方案
你不需要手写原生SQL就可以实现事务内加行锁再更新的需求,ActiveRecord 本身提供了公开稳定的API支持该操作:
Foo.transaction do # 用 ActiveRecord 查询方法构造匹配条件,自动避免SQL注入风险 foos_query = Foo.where( type: 'bar', associated_object: thing_id, other_party_id: user_id ).order(:id) # 官方API:直接对匹配行加 FOR UPDATE 锁,无需返回业务数据即可完成加锁 foos_query.lock! # 执行批量更新,此时行已被锁定,不会出现并发更新冲突 foos_query.update_all(read: "true", seen: true) end
低开销优化方案
如果你担心默认加锁查询会返回多余字段浪费带宽,可以使用以下轻量写法,只会查询一个常量值,性能和手写原生SELECT FOR UPDATE几乎无差异,且兼容所有支持lock方法的Rails版本:
Foo.transaction do foos_query = Foo.where( type: 'bar', associated_object: thing_id, other_party_id: user_id ).order(:id) # 仅查询常量1完成加锁,不加载任何业务数据和模型对象 foos_query.lock.pluck(Arel.sql('1')) foos_query.update_all(read: "true", seen: true) end
说明
lock、lock!都是ActiveRecord Relation的官方公开API,Rails 5+ 全版本稳定支持,6.1可以放心使用,不需要依赖非官方的hack实现。- 用ActiveRecord原生查询方法构造条件,会自动做参数转义,比手动拼接原生SQL的安全性高很多。
内容的提问来源于stack exchange,提问作者John Bachir
相关产品推荐
相关产品推荐

