Eloquent:Model :: orberBy('created_at','desc') - > first()和Model :: all() - > last()之间的区别?

时间:2015-10-04 11:00:05

标签: php mysql laravel laravel-4 eloquent

方案

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

所以我正在做的事情,我通过created_at获取表中的最后一条记录,并增加其membership_id:

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

奇怪的是,一些记录得到了重复的membership_id。在检查时,我注意到这些记录的created_at时间与重复的membership_id几乎完全相同。

现在我已将其更改为:

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

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

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

1 个答案:

答案 0 :(得分:2)

回答标题中的问题

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

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

Model::all()->last()执行此操作

select * from Models

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

回答实际问题

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

  1. [请求1]查询上次使用的membership_id - >得到N
  2. [请求2]查询上次使用的membership_id - >得到N
  3. [request 1]使用membership_id == N
  4. 插入新模型
  5. [request 2]使用membership_id == N
  6. 插入新模型

    除非您使用数据库事务和正确的事务隔离级别,否则步骤2和3可以像这样(重复ID)或互换(没有重复的ID)。

    我看到了2个解决方案:

    1

    对您所做的2个查询使用Repeatable reads isolation level or higher的数据库事务(获取最后一个membership_id并插入)。

    2

    使用数据库SEQUENCEtable to emulate it

    不是解决方案!

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