У меня есть огромная таблица продуктов (100 000+ строк), и в моем контроллере у меня есть следующая функция:
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;
Прямо сейчас вы звоните findAll()
который будет извлекать все записи из базы данных, а затем синтезировать их в объекты. Это занимает много времени, которое тратится впустую, потому что большинство этих объектов никогда не используются снова, поскольку вы выводите только одну страницу за раз.
Вместо этого вам следует передать конструктор запросов в paginator, который затем сможет создать запрос, который получит только те объекты, которые вам действительно нужны для текущей страницы.
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));
}
Вы бы лучше использовать Symfony2 profiler
чтобы отладить ваш запрос, вы должны сначала add index on your fields
и foreign key
на поле productinventory_id
,
Индекс можно использовать так:
/**
* @Entity
* @Table(name="user",indexes={
* @Index(name="email_idx", columns={"email"})
* })
*/
class User
{
...
}
Чтобы оптимизировать этот конкретный запрос в вашем indexAction, вы можете сделать выборку только с нужным столбцом, который хотите отобразить в списке, а также написать свой запрос вместо 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
сервер.