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

时间:2017-04-21 15:38:35

标签: ruby-on-rails ruby activerecord

有一些方法:first_or_create_byfind_or_create_by等,其工作原理如下:

  1. 与数据库交谈,试图找到我们想要的东西
  2. 如果我们没找到,请自己动手
  3. 将其保存到db
  4. 显然,这些方法的并发调用可能让两个线程都找不到他们想要的东西,而在第3步,一个人会意外地失败。

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

    那是:

    1. 提前在您的数据库中创建合理的唯一性约束。
    2. 如果你想保存它,可以保存一些东西
    3. 如果有效,那就好。
    4. 如果由于RecordNotUnique异常而无法正常工作,那么它已经存在,很棒,加载它
    5. 那么在什么情况下我想使用Rails内置的东西而不是我自己的(看似更可靠)create_or_find

2 个答案:

答案 0 :(得分: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的摊销成本更好,并且很快就达到了。

答案 1 :(得分:0)

显然,默认情况下它不是线程安全的,但它们可以通过这种方式设计,以便更好地运行。

如果有必要,可以更快地找到并创建大部分时间的失败创建,有时可以避免异常(可以处理)。

discussion可能对您有所帮助。