За последние 24 часа нас посетили 16636 программистов и 1627 роботов. Сейчас ищут 1406 программистов ...

Оптимизация контроллера, медленно работает

Тема в разделе "Laravel", создана пользователем Ard2p, 8 янв 2021.

Метки:
  1. Ard2p

    Ard2p Новичок

    С нами с:
    8 янв 2021
    Сообщения:
    3
    Симпатии:
    0
    Помогите пожалуйста, очень медленно работает при нагрузке в 70 человек с обновлением раз в 5 сек обрабатывается секунд 10-15

    PHP:
    1.    
    2. public function show(Request $request, $id)
    3.     {
    4.         $user = \Auth::user();
    5.  
    6.         $tournament = \Cache::remember('Tournament@show_' . $id, 5, function () use ($id) {
    7.             return Tournament::select($this->select)->find($id);
    8.         });
    9.         $round = $request->get('round') ? $request->get('round') : $tournament->round;
    10.  
    11.  
    12.         $grids = TGrids::select('*')
    13.             ->where('tournament_id', $tournament->id)
    14.             ->with(['matches' => function ($query) {
    15.                 $query->select(['grid_id', 'tournament_id', 'status', 'win', 'code']);
    16.             }]);
    17.  
    18.         $TPlayersSelect = [
    19.             'tournaments_players.user_id', 'tournaments_players.role', 'round',
    20.             'team', 'nickname', 'avatar', 'exp', 'users.role AS site_role', 'grid_id'
    21.         ];
    22.  
    23.         if (\Perm::allows('tournament@info-role'))
    24.             if (\Perm::role(['moder', 'admin', 'dev']))
    25.                 $TPlayersSelect = array_merge($TPlayersSelect, ['note']);
    26.  
    27.         $players = TPlayers::select($TPlayersSelect)
    28.             ->where('tournaments_players.tournament_id',     $id)
    29.             ->join('users',                        'users.id',                        '=', 'tournaments_players.user_id')
    30.             ->join('games_accounts',    'games_accounts.id',    '=', 'tournaments_players.account_id')
    31.             ->with([
    32.                 'profile' => function ($query) use ($tournament) {
    33.                     $profileSelects = ['user_id', 'game', 'mmr'];
    34.  
    35.                     if (\Perm::allows('tournament@info-role'))
    36.                         if (\Perm::role(['moder', 'admin', 'dev']))
    37.                             $profileSelects = array_merge($profileSelects, ['priority', 'roles']);
    38.  
    39.                     $query->select($profileSelects);
    40.                     $query->where('game', $tournament->game);
    41.                 },
    42.                 'statistics' => function ($query) use ($tournament) {
    43.                     $query->select('user_id', 'win', 'lose');
    44.                     $query->where(DB::raw('DATE_FORMAT(created_at , \'%Y-%m\')'), date('Y-m'));
    45.                     $query->where('game', $tournament->game);
    46.                     $query->where('type',    $tournament->type);
    47.                 }
    48.             ]);
    49.  
    50.         if ($request->get('round') != 'all') {
    51.             $grids->where('round', $round);
    52.             $players->where('tournaments_players.round', $round);
    53.         }
    54.  
    55.         $name = '_t' . $tournament->id . '_r' . $round . '_u-r' . $user->role;
    56.         $players = \Cache::remember('TPlayers' . $name, 5, function () use ($players) {
    57.             return $players->get();
    58.         });
    59.  
    60.         if ($tournament->grid_disable) {
    61.             $grids = [];
    62.             $players = $players->map(function ($player, $key) {
    63.                 $player->team = null;
    64.                 $player->role = null;
    65.                 return $player;
    66.             });
    67.         } else {
    68.             $grids = \Cache::remember('TGrids' . $name, 5, function () use ($grids) {
    69.                 return $grids->get();
    70.             });
    71.  
    72.             if (\Perm::allows('tournament@edit', $tournament)) {
    73.                 $grids->each(function ($grid, $k) {
    74.                     $grid->matches->makeVisible(['code']);
    75.                 });
    76.             } else {
    77.                 $player = $players->where('user_id', $user->id)
    78.                     ->where('round', $tournament->round)->first();
    79.                 if ($player && $player->grid_id) {
    80.                     $grid = $grids->where('id', $player->grid_id)->first();
    81.                     if ($grid) $grid->matches->makeVisible(['code']);
    82.                 }
    83.             }
    84.         }
    85.  
    86.         $tournament->players = $players;
    87.         $tournament->grids = $grids;
    88.  
    89.         return $tournament;
    90.     }
    PHP:
    1. class TGrids extends Model
    2. {
    3.     protected $table = 'tournaments_grids';
    4.    
    5.     public $timestamps = false;
    6.  
    7.     protected $fillable = [
    8.         'tournament_id',
    9.         'round', 'grid', 'bo',
    10.         'win', 'team1', 'team2',
    11.         'team1_score', 'team2_score'
    12.     ];
    13. }
    PHP:
    1. class TMatches extends Model
    2. {
    3.     protected $table = 'tournaments_matches';
    4.  
    5.     protected $fillable = [
    6.         'tournament_id', 'grid_id',
    7.         'status', 'win',
    8.         'code', 'match'
    9.     ];
    10.  
    11.     protected $hidden = ['code'];
    12. }
    PHP:
    1. class Tournament extends Model
    2. {
    3.     protected $fillable = [
    4.         'user_id', 'provider_id',
    5.         'name', 'img', 'desc', 'prize',
    6.         'twitch', 'discord',
    7.         'game', 'type', 'round',
    8.         'lvl', 'max_players',
    9.         'leave_disable', 'grid_disable',
    10.         'start', 'status'
    11.     ];
    12.  
    13.     protected $casts = [
    14.         'leave_disable' => 'boolean',
    15.         'grid_disable'  => 'boolean'
    16.     ];
    17. }
    PHP:
    1. class GameProfile extends Model
    2. {
    3.     protected $table = 'games_profiles';
    4.  
    5.     protected $fillable = [
    6.         'user_id', 'game',
    7.         'mmr', 'priority', 'roles'
    8.     ];
    9.  
    10.     protected $casts = [
    11.         'roles' => 'array',
    12.         'mmr'        => Rang::class
    13.     ];
    14. }
    PHP:
    1. class GameAccount extends Model
    2. {
    3.   protected $table = 'games_accounts';
    4.  
    5.   protected $fillable = [
    6.         'user_id',
    7.         'game','nickname',
    8.         'profileId', 'accountId', 'active'
    9.     ];
    10. }
    PHP:
    1. class TStatistics extends Model
    2. {
    3.     protected $table = 'tournaments_statistics';
    4.  
    5.     protected $fillable = [
    6.         'user_id', 'points',
    7.         'game', 'type',
    8.         'win', 'lose',
    9.         'k', 'd', 'a'
    10.     ];
    11. }

    PHP:
    1. class Rang implements CastsAttributes
    2. {
    3.     /**
    4.     * Cast the given value.
    5.     *
    6.     * @param  \Illuminate\Database\Eloquent\Model  $model
    7.     * @param  string  $key
    8.     * @param  mixed  $value
    9.     * @param  array  $attributes
    10.     * @return mixed
    11.     */
    12.     public function get($model, $key, $value, $attributes)
    13.     {
    14.         $league = null;
    15.         $division = null;
    16.  
    17.         foreach (config('games.lol.leagues') as $league_key => $divisions) {
    18.             $divisions_key = array_reverse(array_keys($divisions));
    19.             foreach ($divisions_key as $division_key) {
    20.                 $elo = $divisions[$division_key];
    21.  
    22.                 if ($value >= $elo) {
    23.                     $league        = $league_key;
    24.                     $division = count($divisions_key) > 1 ? $division_key : null;
    25.                 } else return [
    26.                     'league'        => $league,
    27.                     'division'    => $division
    28.                 ];
    29.             }          
    30.         }
    31.         return [
    32.             'league'        => $league,
    33.             'division'    => $division
    34.         ];
    35.     }
    36. }
    PHP:
    1. return [  
    2.  
    3.     'lol' => [  
    4.  
    5.         'leagues'                => [
    6.             'iron'                => [1 => 400,   2 => 300,   3 => 200,   4 => 100],
    7.             'bronze'            => [1 => 800,   2 => 700,   3 => 600,   4 => 500],
    8.             'silver'            => [1 => 1200,  2 => 1100,  3 => 1000,  4 => 900],
    9.             'gold'            => [1 => 1600,  2 => 1500,  3 => 1400,  4 => 1300],
    10.             'platinum'                => [1 => 2000,  2 => 1900,  3 => 1800,  4 => 1700],
    11.             'diamond'            => [1 => 2400,  2 => 2300,  3 => 2200,  4 => 2100],
    12.             'master'            => [1 => 2500],
    13.             'grandmaster'            => [1 => 2600],
    14.             'challenger'            => [1 => 2700]
    15.         ]
    16.     ]
    17.  
    18. ];
     
  2. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.583
    Симпатии:
    1.761
    Реально ли заменит короткие опросы на уведомления по веб-сокету?
    --- Добавлено ---
    Индексы в базе продуманы, поставлены?
    --- Добавлено ---
    Я бы взял, пропустил все запросы через explain, посмотрел, какие проблемы выявятся. Плюс веб-сокеты, чтоб не надо было без надобности каждые 5 секунд со всех компов опрашивать.
     
  3. Ard2p

    Ard2p Новичок

    С нами с:
    8 янв 2021
    Сообщения:
    3
    Симпатии:
    0
    индексы да проставлены

    Звучит разумно, лучше написать сокет сервер который будет сам опрашивать сайт и рассылать патчи клиентам? или на прямую из базы брать данные?
     
  4. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.583
    Симпатии:
    1.761
    Для ларки есть свой сокет сервер на ноде, от сообщества, для проектов средней сложности его обычно более чем. https://github.com/tlaverdure/laravel-echo-server

    При появлении новых данных он может просто или разослать уведомление, или разослать сами данные клиентам, смотря от объёмов и условий типа авторизации/секретности. Хотя, там есть и приватные каналы, которые защищены. Плюс этого сервера - ларка уже умеет с ним работать.
    --- Добавлено ---
    Ну и плюс, через explain убедитесь, что ваши индексы действительно работают. И что-то мне кажется, что кеш на 5 секунд делает только хуже, а не лучше. Через что кешите?
    --- Добавлено ---
    Лог медленных запросов пробовали?
     
  5. Ard2p

    Ard2p Новичок

    С нами с:
    8 янв 2021
    Сообщения:
    3
    Симпатии:
    0
    через стандартный драйвер = files

    нагрузка на этот маршрут идет только 3 часа в сутки, и чтобы не все запросы отправлять в бд, я решил сделать кеширование чтобы первый запрос записал в бд а остальные брали из файла через 5 сек данные обновятся и получать новую выдачу.
     
  6. Вероломство

    Вероломство Активный пользователь

    С нами с:
    19 июн 2017
    Сообщения:
    626
    Симпатии:
    24
    типичная ошибка, с которой начинал я: собирать сущность игрока на основании сущностей того, что ему принадлежит, да ещё и выполняя кучу джоинов (хотя это и не смертельно, правильный джоин при USING, а не при тупом сравнении, не упадёт даже при ляме запросов), завершилось всё тем, что сущность игрока перекочевала в сущность юзера, которая определяется на момент проверки авторизации, данные уже есть, плюс всё, что касается сущности юзера-игрока хранится в одной табле (какая разница 5 там или 50 столбцов, всё выбирается за один раз) и если нам нужно обновить юзера-игрока, то мы не лазим по всему скрипту и всей БД - мы просто делаем $user->property = что-то, и перед вбросом в рендер делаем $user->save, получая самые последние данные обновляя таблу user один раз и только её

    то есть в чём суть: я просто данные из базовых таблиц переношу в таблы user-бызовая таблица, иначе если хранить просто user-айди_базовой таблицы, то на момент вывода данных юзера-игрока, мы задолбаемся собирать его по всем базовым таблам лазя по их айдишникам, принадлежащим юзеру-игроку

    короче тут долго пояснять, выглядит это так: если юзеру присвоен ранг и есть базовая таблица раногов, то не нужно писать юзеру афдишник ранга, нужно тупо прописать ему этот ранг в его таблу, чтобы при выводе ранга юзера не делать запрос по айдишнику ранга к базовой таблице рангов )))
    --- Добавлено ---
    а какой смысл кеша в 2021-ом году, PHP видимо быстрее к кешу обратится, чем к БД, которая на SSD стоит? :)
    --- Добавлено ---
    70 человек и сервак падает, 10 лет назад при онлайне в 100 падал сервак если там толпой монстра били, так там на процедурке скрипт на пару метров работал, а тут ясен пень фреймворк стоит на 50 метров думает полчаса, как ему один запрос защитить от падения, а тут целых 70)))
     
    #6 Вероломство, 9 янв 2021
    Последнее редактирование: 9 янв 2021
  7. ADSoft

    ADSoft Старожил

    С нами с:
    12 мар 2007
    Сообщения:
    3.854
    Симпатии:
    748
    Адрес:
    Татарстан
    смысл наверное не в скорости доступа - а в снижении нагрузки на БД. Зачем лишний раз ее дергать, когда можно из кеша готовое достать
     
  8. Вероломство

    Вероломство Активный пользователь

    С нами с:
    19 июн 2017
    Сообщения:
    626
    Симпатии:
    24
    70 человек и сервак падает, 10 лет назад при онлайне в 100 падал сервак если там толпой монстра били, так там на процедурке скрипт на пару

    а логика в чём? засунуть в кеш базовые таблы? и что будет быстрее: запрос к БД на SSD или достать из файла - это мы разницу под микроскопом рассмотрим?

    а вот собирать сущность кучей запросов к базовым таблицам - это вот полна дичь, направленная на повышение зарплаты от того что у нас не в одной табле всё, а в паутине да ещё и для понта связей понаделать чтобы нельзя было дичь поудалать