2015-08-08 13 views
5

Ho una tabella di prodotti enorme (100k + file) e nel mio controllo ho la seguente funzione:Come posso migliorare la velocità di KnpPaginatorBundle e Doctrine?

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)); 
    } 

Il problema è la pagina dura circa 11-12 secondi per caricare e consuma 233MB di RAM.

Cosa posso fare per migliorare la velocità e ridurre la memoria?

Questo è il mio soggetto:

/** 
* 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; 

La struttura della tabella viene creata dalla dottrina e si presenta così:

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; 
+0

Non dipende solo dalla dottrina, potresti pubblicare la tabella dello schema SQL? –

+0

Ho aggiunto l'entità e la tabella. – yeouuu

risposta

5

In questo momento, si sta chiamando findAll() che recupererà tutti i record dal database e quindi li sintetizzerà in oggetti. Ciò richiede un sacco di tempo che è sprecato perché la maggior parte di questi oggetti non viene mai più utilizzata poiché si stampa solo una pagina alla volta.

Quello che dovresti fare è passare un generatore di query all'impaginatore che dovrebbe quindi essere in grado di creare una query che ottiene solo gli oggetti effettivamente necessari per la pagina corrente.

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)); 
    } 
+0

Ecco, ora la mia pagina impiega circa 300ms (in app_dev.php) e usa solo circa 19MB di memoria. – yeouuu

0

Faresti meglio utilizzare la Symfony2 profiler per eseguire il debug la tua ricerca, si shoud prima add index on your fields e un foreign key sul campo productinventory_id.

Indice potrebbe essere utilizzare come questo:

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

Per ottimizzare questa query specifica nel vostro indexAction, si potrebbe fare una select con solo bisogno di colonna che si desidera visualizzare nella vostra lista e anche scrivere la vostra richiesta, invece di uno DQL uno. per esempio:

// 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; 

Finaly, si dovrebbe avere attivare strumenti di cache come apc o memcache per liberare il server sql.

+2

Mentre tutte queste sono valide strategie di ottimizzazione dell'accesso ai DB, nessuna di queste si applica veramente qui. Il problema principale è che il richiedente sta recuperando troppe voci. L'aggiunta di un indice non aiuterà in alcun modo e suggerendo di passare a query SQL non elaborate quando tutto ciò che serve sono 20 oggetti con 8 proprietà, ognuna delle quali è semplicemente inutile. – Chris

+0

Sono d'accordo con te, la tua risposta è migliore! –

Problemi correlati