在优化 laravel - php 代码方面需要帮助

Need help in optimizing laravel - php code

我有一个工作 API (php-laravel) returns 正确的数据。我自己是一个 java 的人,我无法完全理解出了什么问题。代码是freelancer写的,慢慢学习laravel.

问题是,如果这个特定的 API 被调用(即使是一次),服务器上的 CPU 使用率会达到 100%,这不应该是这种情况。

下面的代码片段用于检查促销是否适用于用户的卡。

$has_promo_for_users_cards = in_array($promo_summary_id, \User::getPromoSummaryIdsWhereUserHasCards($user_id));
$promo_summary['has_promo_for_users_cards'] = $has_promo_for_users_cards;
// see if the promo is applicable to users cards
if ($has_promo_for_users_cards)
{
    $applicable_cards = [];
    $filter_cards = $inputs['filter_cards'];

    $user_card_ids = $filter_cards == 'my_cards' ? \User::getUsersCardIds($user_id) : explode(',', $inputs['specific_cards']);
    foreach ($cards as $card)
    {
        if (in_array($card['id'], $user_card_ids))
        {
            array_push($applicable_cards, $card);
        }
    }

} else
{
    // if not applicable then return all the cards
    $applicable_cards = $cards;
}    
$promo_summary['applicable_cards'] = $applicable_cards;
return $promo_summary;

和returns整个promo_summary数据。

经过多次试验,如果我注释掉上面的代码片段,API 响应时间会快得多,而且它也会加载 CPU 最多 10%。所以我认为问题出在这个片段中。关于如何优化的任何建议?是不是哪里做错了?

编辑 1

这是服务器基准测试结果:

ab -n 1000 -c 5 http://111.111.111.111/index.html
This is ApacheBench, Version 2.3 <$Revision: 1554214 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 111.111.111.111 (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        Apache/2.4.7
Server Hostname:        111.111.111.111
Server Port:            80

Document Path:          /index.html
Document Length:        59598 bytes

Concurrency Level:      5
Time taken for tests:   51.674 seconds
Complete requests:      1000
Failed requests:        113
   (Connect: 0, Receive: 0, Length: 113, Exceptions: 0)
Non-2xx responses:      1000
Total transferred:      60176968 bytes
HTML transferred:       59597874 bytes
Requests per second:    19.35 [#/sec] (mean)
Time per request:       258.372 [ms] (mean)
Time per request:       51.674 [ms] (mean, across all concurrent requests)
Transfer rate:          1137.25 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        4    6   1.9      5      56
Processing:   102  252 129.6    175     762
Waiting:       84  231 128.1    154     609
Total:        107  258 129.7    181     767

Percentage of the requests served within a certain time (ms)
  50%    181
  66%    288
  75%    321
  80%    336
  90%    471
  95%    579
  98%    616
  99%    619
 100%    767 (longest request)

编辑 2 在上面的代码中有一个对 "User" 模型 class 的外部调用;一个名为 "getPromoSummaryIdsWhereUserHasCards" 的函数。这里有 2 个 foreach 循环。如果我打印并检查一些日志,这些 for 循环会执行很多次!

 /**
     * @param $user_id
     * @return array promo_summary_ids_where_user_has_cards
     */
    public static function getPromoSummaryIdsWhereUserHasCards($user_id)
    {
        //logs
        ini_set("log_errors", 1);
        ini_set("error_log", "/tmp/php-error.log");

     //THE BELOW LINE IS TAKING TIME TO EXECUTE
        $user = \User::with('cards.promos.promo_summary.establishment')->findOrFail($user_id);

        $user_cards = $user->cards;
        $promo_summary_ids_where_user_has_cards = [];
        foreach ($user_cards as $user_card)
        {
            error_log( "Foreach user_cards--!");
            $promos = $user_card->promos;
            foreach ($promos as $promo)
            {
                error_log( "Foreach promos--!");

                $promo_summary = $promo->promo_summary;
                array_push($promo_summary_ids_where_user_has_cards, $promo_summary->id);
            }
        }

        return array_unique($promo_summary_ids_where_user_has_cards);
    }

编辑 3 Promo总结模型

<?php

class PromoSummary extends \Eloquent {

    protected $guarded = [];

    public function promos()
    {
       return $this->hasMany('Promo', 'promo_summary_id');
    }


    public function establishment()
    {
        return $this->belongsTo('Establishment', 'establishment_id');
    }

    public function searchTags()
    {
        return $this->hasMany('SearchTag', 'promo_summary_id');
    }

    /**
     * @param $value
     * @return mixed
     * This is where the Establishment Image url is overriding the image_url in the promo summary.
     * If need to change back to normal behaviour change here
     */
    public function getImageUrlAttribute($value)
    {
        return $this->establishment->image_url;
    }

}

嗯,基本上这是在查询某事。类似于以下内容:

-- note that i dont know you schema so the identifiers of the tables and columns
-- can be inceorrect, but you should get the idea

SELECT DISTINCT(promio_summaries.id) 
FROM users, promos, promo_summaries
WHERE user.id = $user_id -- your method parameter
AND promos.user_id = $user_id
AND promo_summaries.promo_id = promos.id;

如果仅此而已,请尝试在您的数据库中执行它,看看它是否更快,然后将其作为数据库查询而不是循环嵌入。

// never done that, probably fails if copy pasted, 
// but it should get you going.
return 
    DB::table('users')
        ->join('promos', 'promos.user_id', '=', 'users.id')
        ->join('promo_summaries', 'promo_summaries.promo_id', '=', 'promos.id')
        ->where('users.id', '=', $user_id)
        ->select(`promo_summaries.id`)
        ->distinct()->get();

但请注意,如果它只是海量数据,这只是需要时间。在这种情况下也考虑添加智能索引!