使用`contains`会使CakePHP 3的find()调用变慢,而底层查询本身仍然很快

aex*_*exl 6 cakephp cakephp-3.0

我有一个CakePHP 3 find()调用,我一直试图优化一段时间.我已将其缩小到这两个片段,唯一的区别是contain删除了参数.计时由DebugKit测量.

包括contain:

public function qrytest() {
    $table = TableRegistry::get('OrderOptions', array(
        'className' => 'App\Model\Table\Opencart\OrderOptionsTable',
    ));
    $results = $table->find('all', array(
        'fields' => array(
            'OrderOptions.order_product_id',
            'OrderOptions.order_option_id',
            'OrderOptions.value',
        ),
        'conditions' => array(
            'OrderOptions.order_id' => 123,
        ),
        'contain' => array(
            'OpencartProductOptions' => array(
                'OpencartOption',
            ),
            'OpencartOrderProducts' => array(
                'OpencartOrders' => array(
                    'OpencartShippingZone',
                ),
                'OpencartProduct' => array(
                    'OpencartWeightClass',
                ),
            ),
            'OpencartProductOptionValues',
        ),
    ))->all();
    $this->viewBuilder()->template('anEmptyView');
}
Run Code Online (Sandbox Code Playgroud)

Sql Log:1/99 ms; 定时器:4.42 s/5.72 MB


没有 contain

public function qrytest() {
    $table = TableRegistry::get('OrderOptions', array(
        'className' => 'App\Model\Table\Opencart\OrderOptionsTable',
    ));
    $results = $table->find('all', array(
        'fields' => array(
            'OrderOptions.order_product_id',
            'OrderOptions.order_option_id',
            'OrderOptions.value',
        ),
        'conditions' => array(
            'OrderOptions.order_id' => 123,
        ),
        // contain parameter removed; nothing else changed
    ))->all();
    $this->viewBuilder()->template('anEmptyView');
}
Run Code Online (Sandbox Code Playgroud)

Sql Log:1/92 ms; 定时器:1.06秒/4.83 MB


总结:包含contains参数使得控制器花费近5秒的时间进行渲染,同时不会显着改变实际的查询时间.扩展DebugKit的Timers选项卡显示Controller操作:4,388.74 ms(屏幕截图第1 部分,第2部分)


这里首先想到的是优化连接和索引,但是如果你看一下上面的结果,在这两种情况下,查询本身似乎需要相同的时间.为了确认,我在MySQL Workbench中直接运行了原始查询(包括JOIN),大约需要100毫秒,这与我在DebugKit输出中看到的相匹配.此外,我实际上确实在不久前创建了所有必需的索引.

也就是说,我开始认为问题不在于我的MySQL查询,而在于CakePHP如何处理它,这就是我需要帮助的地方.

如果你需要,我可以发布表结构信息.

更新

  • 启用和禁用水合没有区别.尝试都$query->enableHydration(false);$query->hydrate(false);(按照查询builder.html#得到阵列,而不是-的实体)
  • 通过显式设置禁用调试'debug' => false,仍然很慢:时间= 4.30秒,由浏览器测量
  • 这是生成的查询:

    SELECT 
      `OrderOptions`.`order_product_id` AS `OrderOptions__order_product_id`, 
      `OrderOptions`.`order_option_id` AS `OrderOptions__order_option_id`, 
      `OrderOptions`.`value` AS `OrderOptions__value` 
    FROM 
      `order_option` `OrderOptions` 
      LEFT JOIN `product_option` `OpencartProductOptions` ON `OpencartProductOptions`.`product_option_id` = (
        `OrderOptions`.`product_option_id`
      ) 
      LEFT JOIN `option` `OpencartOption` ON `OpencartOption`.`option_id` = (
        `OpencartProductOptions`.`option_id`
      ) 
      LEFT JOIN `order_product` `OpencartOrderProducts` ON `OpencartOrderProducts`.`order_product_id` = (
        `OrderOptions`.`order_product_id`
      ) 
      LEFT JOIN `order` `OpencartOrders` ON `OpencartOrders`.`order_id` = (
        `OpencartOrderProducts`.`order_id`
      ) 
      LEFT JOIN `zone` `OpencartShippingZone` ON `OpencartShippingZone`.`zone_id` = (
        `OpencartOrders`.`shipping_zone_id`
      ) 
      LEFT JOIN `product` `OpencartProduct` ON `OpencartProduct`.`product_id` = (
        `OpencartOrderProducts`.`product_id`
      ) 
      LEFT JOIN `weight_class` `OpencartWeightClass` ON `OpencartProduct`.`weight_class_id` = (
        `OpencartWeightClass`.`weight_class_id`
      ) 
      LEFT JOIN `product_option_value` `OpencartProductOptionValues` ON `OpencartProductOptionValues`.`product_option_value_id` = (
        `OrderOptions`.`product_option_value_id`
      ) 
    WHERE 
      `OrderOptions`.`order_id` = 123
    
    Run Code Online (Sandbox Code Playgroud)
  • 这是XDebug输出:

    XDebug输出

小智 0

我与较大的企业项目合作,前端是 CakePHP,但是当您开始讨论快速开发框架的性能时,您会提出这种框架无法自行处理的需求。

在适当的时候使用 CakePHP,例如显示表中的注册表。

当我进入像你这样的更大的数据集时,我利用数据库资源来解决它们的性能问题,这样我就不会要求 CakePHP 的性能期望。

使用 EXPLAIN,您可以为查询设置适当的索引,当它足够优化时,设置一个简单的视图,例如:

CREATE VIEW %VIEW-NAME% AS %QUERY%
Run Code Online (Sandbox Code Playgroud)

如果使用视图还不够,例如,如果视图仍然具有较大的数据集,请开始使用存储过程,这是一种很好的工作方式,因为您可以使程序的一部分在数据库内运行,并且再次使用 CakePHP 仅用于显示这个数据。