新手问题 并发情况下无法依赖 cache counter 的问题

Awlter1 · 2019年06月02日 · 最后由 Rei 回复于 2019年06月02日 · 582 次阅读
class Shelf
  field :books_count
  has_many :books
end

class Book
  belongs_to :shelf, counter_cache: :books_count
end

假设,一个shelf最多有10本书,现在书架上已有9本,shelf.books_count = 9

现在同时进来两个请求,尝试在shelfs上创建一个book

在controller中create book时验证books_count,但是此时两个请求拿到的都是9,所以两个请求的验证都会通过,会出现书架上出现11本书的情况

请问下,这种情况下怎么处理比较好呢?redis?

共收到 6 条回复
Rei 回复

谢Rei神,看完悲观锁之后我发现我帖子没描述清楚,现在的问题是检查是否超出限值books_count的逻辑和创建book的逻辑不是在一起的

检查限值是写在controller的callback里的,before_action :check_shelf_availability, 而且下面还有10几个其他callback,才走到BooksController#create

check_shelf_availability需要在这个callback之前被call到,而且里面的逻辑是,如果大于限制,直接return render一个报错的页面

这样好像就没法把这些逻辑写在一个transaction里了吧

trigger或check constraint都可以吧

ALTER TABLE book_shelfs ADD CONSTRAINT books_count_check CHECK (
   books_count <= 10
);

Rei 说的是锁 Shell

Shell.find(params[:id]).lock(:true)

其他线程还想读的这个 id 的 shell 的话,就要等锁。

如果你不想锁 Shell 的话,还可以先把 shell 的 book 都创建出来(不给 id),在加 book 的时候,就可以上锁了。

Redis 的分布式锁,也可以,效果应该是一样的(哦,不是,如果操作时间特别长,redis 好,因为上了 exclusive lock,所有读操作都锁了,redis 分布式锁,只锁了加入 shell 这个操作)

·用 select for update 锁记录

Awlter1 回复
around_action :lock_shelf
before_action :check_shelf_availability

private

def lock_shelf
  @shelf = ...
  @shelf.with_lock do
    yield
  end
end
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册