douhui1957 2015-03-16 08:13
浏览 36
已采纳

需要帮助优化laravel - php代码

I have a working API (php-laravel) that returns the correct data as required. Myself being a java guy I am unable to fully understand what is going wrong. The code was written by a freelancer and I am slowly learning laravel.

The issue is if this particular API is called (even once), the CPU usage on the server hits 100% which shouldnt be the case.

The snippet below is used to check if the promos are applicable to user's cards.

$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;

And returns the whole promo_summary data.

After much experimenting, if I comment out the above snippet, the API response time is much faster and it's also loading the CPU upto 10% only. So I think the problem lies in this snippet. Any suggestions of how this can be optimized? Is there something that is done in a wrong way here?

EDIT 1

Here is the server benchmark results:

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)

EDIT 2 In the code above there is an external call to the "User" model class; A funtion called "getPromoSummaryIdsWhereUserHasCards" specifically. Here there are 2 foreach loops within each other. If I print and check some logs these for loops are getting executed quite a lot of times!

 /**
     * @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);
    }

EDIT 3 PromoSummary model

<?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;
    }

}
  • 写回答

1条回答 默认 最新

  • duanjiong5686 2015-03-16 10:05
    关注

    Well, basically what this is doing is querying sth. similar to the following:

    -- 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;
    

    So if thats all, try to execute that in your db, see if it is faster, and then embed that as a DB Query instead of the loops.

    // 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();
    

    But be aware that if its just a massive amount of data, this simply takes time. Consider adding clever indices in this case too!

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥20 完全没有学习过GAN,看了CSDN的一篇文章,里面有代码但是完全不知道如何操作
  • ¥15 使用ue5插件narrative时如何切换关卡也保存叙事任务记录
  • ¥20 软件测试决策法疑问求解答
  • ¥15 win11 23H2删除推荐的项目,支持注册表等
  • ¥15 matlab 用yalmip搭建模型,cplex求解,线性化处理的方法
  • ¥15 qt6.6.3 基于百度云的语音识别 不会改
  • ¥15 关于#目标检测#的问题:大概就是类似后台自动检测某下架商品的库存,在他监测到该商品上架并且可以购买的瞬间点击立即购买下单
  • ¥15 神经网络怎么把隐含层变量融合到损失函数中?
  • ¥15 lingo18勾选global solver求解使用的算法
  • ¥15 全部备份安卓app数据包括密码,可以复制到另一手机上运行