【问题标题】:Odd behavior with Laravel Eloquent queries using whereHas method使用 whereHas 方法的 Laravel Eloquent 查询的奇怪行为
【发布时间】:2015-01-28 02:48:19
【问题描述】:

我无法弄清楚我的 Laravel 脚本发生了什么。我只是选择一个对象,并在其上调用一个运行其他查询的方法,但我看到的行为非常不一致。代码如下:

$product = Product::find(1);

echo $product->numberOfOrders('2014-05-22', '2015-01-27');

这是numberOfOrders 方法的代码:

public function numberOfOrders($startDate = NULL, $endDate = NULL)
{
        if(is_null($startDate)) $startDate = '0000-00-00 00:00:00';
        if(is_null($endDate)) $endDate = date("Y-m-d");

        return Order::whereHas('orderProducts', function($query)
        {
                $query->where('product_id', $this->id);

        })->whereHas('status', function($query)
        {
                $query->where('sales_data', 1);

        })->where('ext_created_at', '>=', $startDate)->where('ext_created_at', '<=', $endDate.' 23:59:59')->distinct()->count();
}

当这段代码运行时,它会挂起大约 10 秒,然后返回整数结果。然后,如果我再次运行相同的代码,它会立即返回结果,并且每次运行时都会继续这样做。如果我更改$startDate,并再次运行代码,它会挂起 10 秒,然后返回结果。然后,重新运行代码将再次立即返回结果。

其他时候,当代码挂起时,我没有返回结果,而是收到一条错误消息:

PHP Fatal error:  Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General error: 2027 Malformed packet' in /var/www/html/app/vendor/laravel/framework/src/Illuminate/Database/Connection.php:299

出现错误信息后,如果我再次运行代码,结果会立即返回。

对于导致我的代码出现这种不一致的原因,我感到非常困惑。我没有对缓存做任何事情。

我注意到whereHas 方法实际上将子查询添加到查询中,而不是使用连接,我觉得这很奇怪。使用 Datamapper ORM 在 CodeIgniter 中编写类似的代码会将连接添加到查询而不是子查询,从而产生更好的性能。子查询会导致问题吗?如果是这样,我仍然不确定为什么事情会表现得如此不一致。

我已经捕获了一些数据,显示问题正在发生。这是DB::getQueryLog() 第一次的输出:

[3] => Array
    (
        [query] => select count(*) as aggregate from `orders` where `orders`.`deleted_at` is null and (select count(*) from `order_products` where `order_products`.`order_id` = `orders`.`id` and `product_id` = ? and `order_products`.`deleted_at` is null) >= 1 and (select count(*) from `order_statuses` where `orders`.`order_status_id` = `order_statuses`.`id` and `sales_data` = ? and `order_statuses`.`deleted_at` is null) >= 1 and `ext_created_at` >= ? and `ext_created_at` <= ?
        [bindings] => Array
            (
                [0] => 1
                [1] => 1
                [2] => 2014-07-29
                [3] => 2015-01-27 23:59:59
            )

        [time] => 22391.73
    )

如您所见,运行需要很长时间。完成后,我再次运行相同的操作,输出如下:

[3] => Array
    (
        [query] => select count(*) as aggregate from `orders` where `orders`.`deleted_at` is null and (select count(*) from `order_products` where `order_products`.`order_id` = `orders`.`id` and `product_id` = ? and `order_products`.`deleted_at` is null) >= 1 and (select count(*) from `order_statuses` where `orders`.`order_status_id` = `order_statuses`.`id` and `sales_data` = ? and `order_statuses`.`deleted_at` is null) >= 1 and `ext_created_at` >= ? and `ext_created_at` <= ?
        [bindings] => Array
            (
                [0] => 1
                [1] => 1
                [2] => 2014-07-29
                [3] => 2015-01-27 23:59:59
            )

        [time] => 22391.73
    )

[4] => Array
    (
        [query] => select count(*) as aggregate from `orders` where `orders`.`deleted_at` is null and (select count(*) from `order_products` where `order_products`.`order_id` = `orders`.`id` and `product_id` = ? and `order_products`.`deleted_at` is null) >= 1 and (select count(*) from `order_statuses` where `orders`.`order_status_id` = `order_statuses`.`id` and `sales_data` = ? and `order_statuses`.`deleted_at` is null) >= 1 and `ext_created_at` >= ? and `ext_created_at` <= ?
        [bindings] => Array
            (
                [0] => 1
                [1] => 1
                [2] => 2014-07-29
                [3] => 2015-01-27 23:59:59
            )

        [time] => 1.81
    )

如您所见,查询完全相同,但速度要快得多。为什么会有这样的差异?结果是否缓存在某处?

如果有帮助,我将使用 php artisan tinker 运行所有这些代码。

编辑 经过更多研究,我了解到这可能是mysql query cache 的行为。

【问题讨论】:

    标签: php mysql laravel laravel-4 eloquent


    【解决方案1】:

    您的查询速度非常慢。确保您的表已正确编入索引。

    另外,缓存您的查询,如果它们经常运行的话。

    Eloquent 有一个漂亮的 remember() 方法。

    它将使用您当前的缓存驱动程序来存储查询结果。到目前为止,Redis 为我做了很棒的工作。

    【讨论】:

    • 我相信这些表的索引是正确的。它们每个都有一个递增的id 列,并且是使用 Laravel 的 Schema Builder 创建的。为了让它们“正确编入索引”,我还需要做些什么吗?缓存在这里不会对我有太大帮助。我需要在第一次运行时缩短查询时间。当我使用连接重写查询时,速度非常快。我不明白为什么 Laravel 使用子查询而不是连接。你呢?
    • 拥有id 列不等于拥有索引。如果要在主键和外键上建立索引,则查询的运行速度与使用联接一样快。 This website explains it alllink to someone with the same issue on laravel's github
    • 知道了。添加索引有很大的不同。子查询版本的查询时间现在平均约为 0.10 秒。加入版本在 0.01 秒时仍然运行得更快。这是预期的吗?
    • 非常如此。连接总是比子查询快:stackoverflow related question
    猜你喜欢
    • 2021-05-25
    • 1970-01-01
    • 1970-01-01
    • 2021-05-28
    • 2018-02-27
    • 2023-03-07
    • 1970-01-01
    • 2020-04-22
    • 2019-12-28
    相关资源
    最近更新 更多