ActiveRecord的find_or_create*方法是否存在根本缺陷?

z5h*_*z5h 5 ruby activerecord ruby-on-rails

有几种方法:first_or_create_by,find_or_create_by等,其原理是:

  1. 与数据库交谈,试图找到我们想要的东西
  2. 如果我们没有找到它,那就自己动手吧
  3. 将它保存到数据库

显然,这些方法的并发调用可能使两个线程都找不到他们想要的东西,并且在步骤3中,一个人意外地失败.

似乎更好的解决方案是, create_or_find

那是:

  1. 在您的数据库中提前创建合理的唯一性约束.
  2. 如果你想保存它,请保存一些东西
  3. 如果它有效,那很好.
  4. 如果由于RecordNotUnique异常而无法正常工作,它已经存在,非常好,加载它

那么在什么情况下我想使用Rails内置的东西而不是我自己的东西(看似更可靠)create_or_find

z5h*_*z5h 9

挖掘之后,我将回答我自己的问题.

用于查找或创建的文档说:

请注意,此方法不是原子方法,它首先运行SELECT,如果没有结果,则尝试INSERT.如果存在其他线程或进程,则两个调用之间存在竞争条件,并且最终可能会出现两个类似的记录.

这是否是一个问题取决于应用程序的逻辑,但在行具有UNIQUE约束的特定情况下,可能会引发异常,只需重试:

begin CreditAccount.find_or_create_by(user_id: user.id) rescue ActiveRecord::RecordNotUnique retry end

总的来说,这将具有更好的性能create_or_find.

考虑到成功create_or_find时需要1个DB跳闸,每个唯一记录只会发生一次.每隔一次需要2次DB跳转(创建和搜索失败).

find_or_create如果失败(搜索,失败创建,再次搜索),重试将需要3次旅行,但这只能在非常小的窗口中发生这么多次.除此之外,每次其他电话都要find_or_create记录,需要1次DB行程.

因此,重试的摊销成本find_or_create更好,并且很快达到.

  • 我实际上有一个概括,修补到我的ActiveRecord,默认的`find_or_create*`方法是相当蹩脚的默认IMO. (2认同)
  • @muistooshort您是否考虑提议对Rails核心进行修复? (2认同)