Как я могу улучшить KnpPaginatorBundle и скорость обучения?


У меня огромная таблица продуктов (100 тыс.+ строк), и в моем контроллере у меня есть следующая функция:

public function indexAction(Request $request)
    {
        $findProducts = $this->getDoctrine()
           ->getRepository("StockBundle:Product")->findAll();

        $paginator  = $this->get('knp_paginator');
        $producten = $paginator->paginate(
            $findProducts,
            $request->query->getInt('page', 1)/*page number*/,
            20/*limit per page*/
        );

        return $this->render('StockBundle:Default:index.html.twig', 
             array('producten' => $producten));
    }

Проблема в том, что загрузка страницы занимает около 11-12 секунд и занимает 233 МБ оперативной памяти.

Что я могу сделать, чтобы повысить скорость и уменьшить объем памяти?

Это моя сущность:

/**
 * Product
 *
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="Namespace\StockBundle\Entity\ProductRepository")
 */
class Product
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="naam_nl", type="string", length=255)
     */
    private $naamNl;

    /**
     * @var string
     *
     * @ORM\Column(name="naam_fr", type="string", length=255)
     */
    private $naamFr;

    /**
     * @var string
     *
     * @ORM\Column(name="naam_en", type="string", length=255)
     */
    private $naamEn;

    /**
     * @var string
     *
     * @ORM\Column(name="productnummer", type="string", length=255)
     */
    private $productnummer;

    /**
     * @var float
     *
     * @ORM\Column(name="prijs", type="float")
     */
    private $prijs;



    /**
     * @var string
     *
     * @ORM\Column(name="merk", type="string", length=255)
     */
    private $merk;

    /**
     * @ORM\OneToOne(targetEntity="Namespace\StockBundle\Entity\ProductInventory", cascade={"persist"})
     * @ORM\JoinColumn(name="productinventory_id", referencedColumnName="id")
     * 
     */
    private $productinventory;

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

CREATE TABLE `product` (
  `id` int(11) NOT NULL,
  `naam_nl` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `productnummer` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `prijs` double NOT NULL,
  `merk` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `productinventory_id` int(11) DEFAULT NULL,
  `naam_fr` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `naam_en` varchar(255) COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Author: yeouuu, 2015-08-08

2 answers

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

Вместо этого вам следует передать построитель запросов в разделитель страниц, который затем сможет создать запрос, который получит только те объекты, которые вам действительно нужны для текущей страницы.

public function indexAction(Request $request)
    {
        $findProducts = $this->getDoctrine()
           ->getRepository("StockBundle:Product")->createQueryBuilder("p");

        $paginator  = $this->get('knp_paginator');
        $producten = $paginator->paginate(
            $findProducts,
            $request->query->getInt('page', 1)/*page number*/,
            20/*limit per page*/
        );

        return $this->render('StockBundle:Default:index.html.twig', 
             array('producten' => $producten));
    }
 5
Author: Chris, 2015-08-08 17:29:27

Вам лучше использовать Symfony2 profiler, чтобы для отладки вашего запроса вы сначала произнесли add index on your fields и foreign key в поле productinventory_id.

Индекс можно использовать следующим образом:

/**
 * @Entity
 * @Table(name="user",indexes={
 *     @Index(name="email_idx", columns={"email"})
 * })
 */
class User
{
    ...
}

Чтобы оптимизировать этот конкретный запрос в вашей индексации, вы можете выбрать только нужный столбец, который хотите отобразить в своем списке, а также написать свой запрос вместо DQL. например:

// Get connection
$conn = $entityManager->getConnection();

// Get table name
$meta = $entityManager->getClassMetadata(User::class);
$tableName = $meta->getTableName();

// Get random ids
$sql = "SELECT id AS id FROM $tableName WHERE active = true ORDER BY RAND()";
$statement = $conn->executeQuery($sql);
$ids = array_map(function ($element) {
    return $element['id'];
}, $statement->fetchAll());

return $ids;

Наконец, вам следует включить инструменты кэширования, такие как apc или memcache, чтобы освободить ваш сервер sql.

 0
Author: Sunitrams', 2015-08-08 17:27:50