Laravel查询在死锁发生时返回空集合

想象一下,您有两个进程同时执行以下PHP代码,将显示什么结果?

\DB::transaction(function (){
   dump(User::where('id',1)->lockForUpdate()->first());
});

令人惊讶的是,一个进程打印正常的查询结果,而另一个进程打印一个空集合(数组)。 返回空集合的代码并未按照我们想的那样触发死锁异常!然而,如果您查阅了transaction方法的源代码,则会发现在发现死锁之后,该方法会自动重试事务。 而且当死锁的数量达到设定值时,最终死锁将被作为异常抛出。 但是,当前这个实际发生死锁的查询看起来却很正常,因为它返回了一个空集合!

这个意外返回的空数组和未抛出的死锁异常实际上是一个古老且棘手的PHP-PDO bug。 在7.4.13发布之前,它已经广泛存在于许多PHP版本中。 有许多与此相关的讨论,例如:

  1. https://bugs.php.net/bug.php?id=76742
  2. https://github.com/php/php-src/pull/5937
  3. https://github.com/php/php-src/pull/6203
  4. https://github.com/php/php-src/commit/b03776adb5bbb9b54731a44377632fcc94a59d2f

在PHP7.4.13之前,你几乎没有办法直接检测到此错误。 切勿尝试打开PDO :: ATTR_EMULATE_PREPARES来解决此问题! 因为此选项将使您所有的PDO查询结果都变成字符串,数据类型的丢失将会导致更严重的后果。

因此,唯一可行的方法是手动编译已修复的PHP源代码,或安装新的预编译版本的PHP7.4.13。