【问题标题】:Eloquent: Difference between Model::orberBy('created_at', 'desc')->first() and Model::all()->last()?雄辩:Model::orderBy('created_at', 'desc')->first() 和 Model::all()->last() 之间的区别?
【发布时间】:2016-01-01 04:08:46
【问题描述】:

场景

我有一个表,其中有一个名为“Membership_ID”的字段,这不是表的主键,也不是自动递增的。 基本上它是一个六位数的 UNSIGNED_ZEROFILL INT 类型,需要为每条新记录递增。

所以我在做什么,我通过 created_at 获取表中的最后一条记录,并增加它的 members_id:

$last_member = Model::orderBy('created_at', 'desc')->first();
$new_id = $last_member->membership_id++;

奇怪的是,有几条记录重复了 members_id。经过检查,我注意到这些具有重复的 members_id 的记录的 created_at 时间几乎相同。

现在我把它改成了:

$last_member = Model::all()->last();
$new_id = $last_member->membership_id++;

彻底测试过,到目前为止没有重复 Membership_ID。

问题是为什么membership_id 在第一种情况下会重复。在 created_at 时间戳中,值并不完全相似,有几秒钟的差异,或者在某些情况下差异只有几分钟?

【问题讨论】:

    标签: php mysql laravel laravel-4 eloquent


    【解决方案1】:

    从题目中回答问题

    Model::orderBy('created_at', 'desc')->first() 做了这样的事情:

    select * from Models order by 'created_at' desc limit 1
    

    Model::all()->last() 这样做

    select * from Models
    

    即它从数据库中获取 all 模型(没有显式排序),最后一个由 Laravel 返回(在 php 中)。这是非常低效且脆弱的,因为返回的实际 Model 依赖于默认的数据库排序。

    回答实际问题

    membership_id 由于两个插入之间的race condition 而重复。当同时创建两个Model时,两个请求同时被两个php进程处理。以下是可能发生的情况(或者不会,这就是为什么您有时只能重现它):

    1. [request 1] 查询最后使用的membership_id -> 得到N
    2. [request 2] 查询上次使用的membership_id -> 得到N
    3. [request 1] 插入新模型,membership_id == N
    4. [request 2] 插入新模型,membership_id == N

    除非您使用数据库事务和适当的事务隔离级别,否则第 2 步和第 3 步可以这样(重复 id)或互换(无重复 id)。

    我看到了 2 个解决方案:

    1

    使用带有Repeatable reads isolation level or higher 的数据库事务处理您进行的2 个查询(获取最后一个membership_id 并插入)。

    2

    使用数据库SEQUENCEtable to emulate it

    不是解决方案!

    你现在做的不是解决方案!除非您采取正确的步骤,否则它与您的第一次尝试一样容易受到竞争条件的影响。根据定义,竞争条件可以发生,但不保证会发生。很难最终测试他们是否缺席。

    【讨论】:

      猜你喜欢
      • 2016-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-29
      • 2023-03-12
      • 2015-10-01
      • 1970-01-01
      相关资源
      最近更新 更多