2016-06-03 2 views
1

Я столкнулся с странной «ошибкой» на PHP и, поскольку я новичок, я в конце своих знаний.Первое использование массива медленное

Я разрабатываю расширение TYPO3, которое имеет некоторые важные проблемы с производительностью с данными, или так я думал. Оказалось, что первое использование массива, в котором хранятся все объекты, полученные из моего запроса к базе данных, уходит далеко вперед. Каждое использование или цикл после этого быстро повторяется.

код выглядит следующим образом:

 $productsArr = $this->productRepository->findByDetail($category, $properties); 

     $newSortArr = array(); 
     $familyProductList = array(); 

     $counter = count($productsArr); 
     /** @var Product $product */ 
     for($i = 0; $i < $counter; $i++) { 

      //it takes to long to do this 
      $product = $productsArr[$i]; 

      if(!empty($productsArr[$i])) { 
       $newSortArr[$product->getInFamily()->getUid()][] = $product; 
      } 
     } 

Это не имеет значения, где я первым использовать массив объектов. Первое использование массива всегда занимает около 30 секунд.

Кто-нибудь сталкивался с чем-то похожим? Если вам нужна дополнительная информация, я с радостью сообщите об этом.

Заранее благодарен!

+0

Вероятно, запрос «тяжелый», и после первого использования все еще находится в кеше запросов вашего сервера, поэтому вы получите более быстрые ответы в следующий раз. –

+0

Я не знаком с TYPO3, но переведен на термины Symfony/Doctrine, кажется, вы можете получить список продуктов из базы данных, а затем получить доступ к некоторому отношению внутри цикла ('getInFamily'). Этот _may_ означает, что вы ленивы загружаете эти данные - это значит, что вы увольняете другой запрос базы данных для каждого продукта в цикле. Подсчитайте запущенные запросы или пропустите строку '$ newSortArr' и посмотрите, работает ли цикл быстрее, не делая этого. Можно предположить, что это может быть так. Обычно разрешается путем присоединения к связанному отношению, поэтому он добавляется к набору результатов и не извлекается с помощью ленивой загрузки. – JimL

+0

@MarcB, но запрос выполняется довольно быстро. Только первое использование массива, например, когда я назначаю его другой переменной, принимает длинный – AVisible

ответ

0

Я предполагаю, что массив заполнен в первой строке. Рассматривали ли вы заселение $product с использованием foreach($productArr as $product) вместо o for?

+0

Да, это то, что я делал раньше, но для первого цикла у него все еще были те же проблемы. – AVisible

+0

Что должен содержать продуктArr? Есть ли, например, изображения с кодировкой base64 или другие большие столбцы? Если да, то, возможно, вам следует протестировать, исключив те из результата, чтобы исключить это из списка o подозреваемых. –

+0

$ productArr содержит объекты продукта. Объекты продукта также имеют дочерние объекты, связанные с ними. – AVisible

3

Ваш $productsArr не является массивом, а является классом Object of Exbase QueryResult, который вы можете перебирать с помощью foreach или делать доступ к индексу. Этот объект выполняет запрос и строит его объекты только тогда, когда это необходимо, поэтому на данный момент вы делаете $product = $productsArr[$i];, все Product -объекты $productsArr. Основная проблема заключается в том, что создание объектов в PHP имеет плохую производительность и требует много памяти.

Таким образом, чтобы избежать проблем с производительностью, рекомендуется использовать пользовательский запрос с

$this->productRepository->createQuery()->statement('select * from ...')->execute(); 

, чтобы получить именно то, что вы хотите вместо загрузки огромного количества объектов и уточнить их позже в PHP.

+4

Это правильный ответ, однако ваше решение не рекомендуется. Вам действительно следует избегать использования пользовательской инструкции, поскольку они по своей сути менее безопасны, больше шансов на совместимость с другой базой данных SQL, и в большинстве случаев вы могли бы хорошо их создавать с помощью функций запроса extbase: https://docs.typo3.org/typo3cms /ExtbaseFluidBook/6-Persistence/3-implement-individual-database-queries.html –

+1

@JozefSpisiak Согласен! До тех пор, пока можно построить запрос с функциями запроса extbase по умолчанию, я настоятельно рекомендую всем использовать их вместо использования пользовательского запроса. Но, к сожалению, невозможно объединить таблицы с функциями запроса extbase. Вот почему я использую пользовательские запросы в этом случае. –

1

Как уже упоминалось, ваш результат не является массивом, а QueryResult. Просто FYI, можно превратить его в массив, добавив ->toArray() в конце запроса:

$productsArr = $this->productRepository->findByDetail($category, $properties)->toArray(); 

Но это не улучшит ситуацию. Есть два возможных вопросов:

переборе все объекты

Преимущество QueryResult в том, что она отражает только результат запроса, но не решает все объекты уже. Может быть передан QueryResult, например. на виджет Pagination и затем загружать только запрошенные результаты (например, 1-10, 11-20 и т. д.).

Поскольку вы применяете ручную сортировку, все ваши объекты (в зависимости от вашего проекта, это может быть много ...) загружаются.

По-видимому, вы хотите отсортировать товары по их семейству UID?Почему бы не сделать это с функциональностью Extbase в вашем ProductRepository:

protected $defaultOrderings = array(
    'inFamily.uid' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING 
); 

жадной загрузки вспомогательных объектов

Ваша модель Product может иметь отношения к другим моделям (эх продукт к категории, продукт в опции и т.д.) , По умолчанию Extbase разрешает все эти отношения при доступе к объектам.

Чтобы предотвратить это, вы можете использовать Lazy Loading для отношений. Это имеет смысл для вспомогательных объектов, которые не используются во всех представлениях. Например. в вашем представлении списка вам нужно только заголовок, изображение и цена вашего продукта, но вам не нужны все варианты продукта.

Для настройки отложенной загрузки для этих суб объектов, вам просто нужно @lazy аннотацию в модели:

/** 
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\My\Extension\Domain\Model\ObjectStorageModel> 
* @lazy 
*/ 
protected $categories; 

/** 
* @var \My\Extension\Domain\Model\OtherModel 
* @lazy 
*/ 
protected $author; 

Ленивая загрузка может иметь некоторые недостатки, например, в определенных ситуациях при проверке объекта, являющегося экземпляром OtherModel, вы получаете вместо этого объект типа LazyLoadingProxy. Вы можете обойти большинство этих проблем или, возможно, даже не наткнуться на них в обычных сценариях. Общий обходной путь, если вы действительно зависит от объекта, не будучи LazyLoadingProxy является проверка так:

if ($product->getAuthor() instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) { 
    $product->getAuthor()->_loadRealInstance(); 
} 

Это гарантирует, что в любом случае у вас есть «реальный» экземпляр объекта.

Пожалуйста, не забудьте очистить системные кеши, когда вы делаете изменения в отношении одной из проблем.

Смежные вопросы