Улучшение производительности — быстрая загрузка Yii2

#php #yii2

#php #yii2

Вопрос:

У нас есть таблица с именем content, а размер таблицы превышает 5 ГБ. В нашем серверном приложении Yii2 есть раздел для просмотра этого содержимого, и доступно несколько фильтров для фильтрации содержимого. Мне нужно отфильтровать содержимое, которое назначено или не назначено категориям.

Отношения

Контент может быть общим для многих категорий. Таким образом, существует отношение «один ко многим» между таблицами содержимого и категорий.

Таблица сопоставления — content_cat_xref .

Модели следующие.

Модель контента

  /**
 * @return yiidbActiveQuery
 */
public function getContentCatXrefs()
{
    return $this->hasMany(ContentCatXref::className(), ['content_id' => 'id']);
}
 

Модель Content_cat_xref

 /**
 * @return yiidbActiveQuery
 */
public function getCategory()
{
    return $this->hasOne(Category::className(), ['id' => 'category_id']);
}

/**
 * @return yiidbActiveQuery
 */
public function getContent()
{
    return $this->hasOne(Content::className(), ['id' => 'content_id']);
}
 

Контроллер

  public function actionIndex()
{
    $searchModel = new commonmodelsContentSearch();
    $dataProvider = $searchModel->searchMedia(Yii::$app->request->queryParams);
    return $this->render('index', [
        'searchModel' => $searchModel,
        'dataProvider' => $dataProvider
    ]); 
}
 

Модель поиска содержимого

 public function searchMedia($params)
{
    $query = Content::find()->where(['file_type_id'=>[1,2,3,4,5,6,8,11]]);
    
    $dataProvider = new ActiveDataProvider([
        'query' => $query,
    ]);
    
    $this->load($params);
    
    if (!$this->validate()) {
        return $dataProvider;
    }
    
    //Conditionally add filters
    if(isset($this->is_category_available) amp;amp; $this->is_category_available === 'yes'){
        $query = $query->joinWith(['contentCatXrefs' => function($queryw){
            $queryw->andWhere(['is not', 'content_cat_xref.category_id', new yiidbExpression('null')]);
        }]);
    }else if(isset($this->is_category_available) amp;amp; $this->is_category_available === 'no'){
        $query = $query->joinWith(['contentCatXrefs' => function($queryw){
            $queryw->andWhere(['is', 'content_cat_xref.category_id', new yiidbExpression('null')]);
        }]);
    }
    
    if(!is_null($this->file_name) amp;amp; !empty($this->file_name)) {
        $query->andWhere("MATCH (file_name) AGAINST ("".$this->file_name."" IN NATURAL LANGUAGE MODE)");
    }
    
    if(!is_null($this->file_path) amp;amp; !empty($this->file_path)) {
        $query->andWhere("MATCH (file_path) AGAINST ("".$this->file_path."" IN NATURAL LANGUAGE MODE)");
    }
    
    $query->andFilterWhere(['type_code'=>$this->type_code]);
    $query->andFilterWhere(['file_type_id'=>$this->file_type_id]);
    $query->andFilterWhere(['content.active_status'=>$this->active_status]);
    $query->andFilterWhere(['content.content_status'=>$this->content_status]);
    
    if(is_null($this->file_name)) {
        $query->orderBy("id desc");
    }

    return $dataProvider;
}
 

У меня есть несколько вопросов

  1. Каким должен быть правильный и эффективный способ объединения двух таблиц ( content и content_cat_xref) для извлечения данных. Мне нужно отфильтровать содержимое, которое назначено или не назначено категориям. Итак, я использую этот фильтр для этого.
         if(isset($this->is_category_available) amp;amp; $this->is_category_available === 'yes'){
        $query = $query->joinWith(['contentCatXrefs' => function($queryw){
            $queryw->andWhere(['is not', 'content_cat_xref.category_id', new yiidbExpression('null')]);
        }]);
    }else if(isset($this->is_category_available) amp;amp; $this->is_category_available === 'no'){
        $query = $query->joinWith(['contentCatXrefs' => function($queryw){
            $queryw->andWhere(['is', 'content_cat_xref.category_id', new yiidbExpression('null')]);
        }]);
    }
     
  2. Я использую быструю загрузку для повышения производительности здесь. Но это дает повторяющиеся записи при объединении этих двух таблиц. После того, как я использую distinct, это снижает производительность. Каким должен быть правильный способ сделать это Yii2?
  3. Есть ли способ передать параметры функциям отношений при построении запроса в модели поиска контента?
  4. Если мы упорядочиваем содержимое по убыванию идентификатора, снова снижаем производительность.

Очень признателен, если есть кто-нибудь, кто может дать рекомендации по этому вопросу.

Комментарии:

1. Вы можете использовать andFilterWhere() , чтобы избежать проверки пустых фильтров.

2. @InsaneSkull Спасибо. Я заменил andWhere на andFilterWhere. 2-я проблема не исправлена из-за этого. Если я использую distinct() в $query, он зависает, пока я не перезапущу сервер. Это происходит только на сервере с содержимым более 5 ГБ. Все работает нормально, если я использую какой-либо код на своем локальном сервере

3. @InsaneSkull обновил сообщение

Ответ №1:

  1. Если цель состоит в том, чтобы фильтровать содержимое, которое назначено или не назначено категориям, я предложу сохранить флаг в таблице «содержимое». Или вы можете добавить индекс в существующие таблицы, чтобы повысить производительность при соединении и условиях, но это всегда будет медленнее, чем условие для поля флага в одной таблице. Вы можете использовать инструкцию «explain», чтобы получить информацию об используемых индексах, а также вам будет полезно добавить новый индекс
  2. Проблема не в Yii2, а в том, будут ли записи сохраняться в таблицах. Если у вас есть несколько записей в таблице «content_cat_xref» для соответствующей строки из таблицы «content», то ваше объединение вернет несколько записей, где записи из таблицы «content» будут одинаковыми для соответствующей строки, но записи из «content_cat_xref» будут разными для каждой строки. Решение состоит в том, чтобы использовать подзапрос (повлияет на производительность) или сохранить флаг, как описано в пункте 1
  3. В Yii2 отношения формируются на основе возврата метода в классе модели. Вы можете написать условия в этих методах. например:
 if($this->is_category_available == 'yes') {
return $this->hasMany(ContentCatXref::className(), ['content_id' => 'id'])
        ->andOnCondition(['a_type' => 1]);
}
else  {
return $this->hasMany(ContentCatXref::className(), ['content_id' => 'id'])
        ->andOnCondition(['a_type' => 0]);
}
 
  1. Добавьте индексы в свою таблицу

Комментарии:

1. 1. Я ценю ваше предложение. Я думаю, что это улучшит производительность для моих текущих требований. Я применю его. 2. В настоящее время я использую индексы в обеих таблицах. 3. Есть ли возможность анализировать параметры для поиска функций модели (отношений) 4. Столбец ‘id’ уже является индексом

2. Использование индекса с одним столбцом не улучшит производительность, вместо этого используйте составной индекс. Пожалуйста, перейдите по этой ссылке для получения дополнительной информации . Я не уверен, что вы имеете в виду, используя параметры синтаксического анализа для функций поиска, но вы можете присвоить значение переменной-члену модели и использовать эту переменную в функции отношения, как показано в примере.

3. Также обратите внимание: используйте EXPLAIN, чтобы выяснить, используют ли и какие индексы ваши запросы в настоящее время, и используйте подсказки по индексу (ИСПОЛЬЗУЙТЕ ИНДЕКС, ПРИНУДИТЕЛЬНЫЙ ИНДЕКС), если необходимо, чтобы помочь оптимизатору MySQL выбрать правильный индекс

4. Игнорируйте комментарий выше для 3-го вопроса. Я пытаюсь спросить об этом. В модели поиска контента мы используем функцию joinWith() с отношением contentCatXrefs . Связь находится в модели содержимого. Если мы сможем передать параметры фильтра в отношение, мы сможем уменьшить количество записей при объединении этих двух таблиц. В настоящее время я фильтрую, используя предложение where. Но если мы сможем добавить условия к ON при объединении, я думаю, мы сможем еще больше сократить записи. Что вы думаете?

5. Чтобы добавить больше условий в ON, вы можете использовать «onCondition», «andOnCondition» или «orOnCondition» в отношении. Пожалуйста, обратитесь к приведенному примеру.