Zend FR

Consultez la FAQ sur le ZF avant de poster une question

Vous n'êtes pas identifié.

#1 06-01-2010 18:21:57

citronbleu-v
Membre
Lieu: Béziers ou Arles
Date d'inscription: 03-02-2009
Messages: 79
Site web

Extraction d'une base de données de 18000 produits

Bonjour je suis confronté à un problème d'optimisation.

j'ai une variable $selection qui contient un tableau d'objets ( class Produit {} )
cette variable peut contenir jusqu'à 18000 instances de Produit.

Le problème c'est que cette solution fait que ma variable est beaucoup trop grosse pour php (je suis déjà à 500MO de memory_limit dans php.ini donc je ne pense pas qu'il soit judicieux de l'augmenter encore plus).

J'ai donc pensé mémoriser juste les id des produits dans ma variable $selection puis faire :

Code:

$pdf = new My_Pdf() // Une classe personnelle qui permet de créer des Pdf

foreach ($selection as $id) {
   $produit       = $monMapper->find($id); // Retourne une instance de Produit peuplé en Lazy Loading
   $documents = $produit->getDocuments(); // Exécute une autre requête car LazyLoading
   // Divers traitements
   $pdf->addDocuments($documents);
}

$pdf->save(); // Enregistre un fichier PDF

Je ne peux pas tester pour le moment mais pensez vous que c'est une bonne méthode de faire comme ceci lorsqu'on travaille sur un grand nombre d'éléments.

Hors ligne

 

#2 07-01-2010 08:21:35

philippe
Administrateur
Lieu: Grenoble
Date d'inscription: 01-03-2007
Messages: 1624

Re: Extraction d'une base de données de 18000 produits

Je vois 2 points :
- ton memory limit, il faut le modifier dans ton script et pas dans php.ini. Ca évite qu'il concerne toutes les pages de ton site => ini_set("memory_limit","500M");
- bannir les fetchAll quand tu as trop de résultats

hum... tu peux avoir intérêt ne pas créer tableau de 18000 entrées, et faire le traitement au fur et à mesure de ta requête (en clair bannir le fetchAll quand tu as trop d'entrées).

Voilà à quoi ça pourrait ressembler avec PDO (hum... vérifie les syntaxes, je suis un peu rouillé en PDO)

Code:

$query = "SELECT id FROM mes_selections WHERE xxx";
$stmt = $pdo->prepare($query);
$stmt->bindValue(ce qui va bien);
$stmt->execute();

$pdf = new My_Pdf()
while($id = $stmt->fetch(PDO::FETCH_COLUMN)) {
   $produit       = $monMapper->find($id); // Retourne une instance de Produit peuplé en Lazy Loading
   $documents = $produit->getDocuments(); // Exécute une autre requête car LazyLoading
   // Divers traitements
   $pdf->addDocuments($documents);
}
$pdf->save();

A+, Philippe


twitter : @plv ; kitpages.fr : Création de sites internet à Grenoble et Paris

Hors ligne

 

#3 07-01-2010 08:28:19

nORKy
Membre
Date d'inscription: 06-03-2008
Messages: 1098

Re: Extraction d'une base de données de 18000 produits

Il n'est pas possible de réduire l'occupation mémoire de Produit ? (genre des propriétés qui ne servent pas pour le traitement nécessaire)


----
Gruiiik !

Hors ligne

 

#4 07-01-2010 08:35:14

sekaijin
Membre
Date d'inscription: 17-08-2007
Messages: 1137

Re: Extraction d'une base de données de 18000 produits

dernière solution que j'ai utilisé lorsque j'avais beaucoup de lignes à remonter
de l'ordre de 10 fois plus que toi

utiliser SELECT .... INTO FILE filename
puis faire un read de chaque ligne du fichier pour formater la sortie
et enfin envoyer le fichier et faire le ménage

lorsque c'est très volumineux tu peux faire ça dans trois actions avec des redirect
Action1 (select -> temfile.csv)
Action2 (Read -> formatedfile.xml)
Action3 (send formatedfile.xml and clean temps files)

ainsi tu minimise les ressource mémoire et tu réponds régulièrement au client et évite les timeouts
par contre je ne sais pas faire pour ne pas générer tout un pdf en mémoire donc avec ce type de doc de très gros volumes c'est probablement pas la solution

A+JYT

Hors ligne

 

#5 07-01-2010 09:48:16

keilnoth
Membre
Date d'inscription: 30-08-2008
Messages: 128
Site web

Re: Extraction d'une base de données de 18000 produits

Pour extraire un gros tas de données, il faut premièrement évaluer ce dont on a besoin en sortie. Il faut ensuite récupérer uniquement ce dont on a besoin. Donc, éviter le fetchAll() et éviter le SELECT * FROM.

Egalement, la gestion par objet de Zend est très gourmande en mémoire. Il ne faut pas hésiter à utiliser la méthode toArray() qui permet de gagner un bon gros tas de mémoire et de rapidité d'exécution.

Et finalement, comme déjà dit plus haut, procéder par batch, par exemple de 500 objets.

Et ne pas oublier de libérer la mémoire de manière explicite à chaque itération, sur les objets ou variables qu'on utilise plus.


Quelques tutoriaux Zend Framework !

Hors ligne

 

#6 07-01-2010 10:36:29

citronbleu-v
Membre
Lieu: Béziers ou Arles
Date d'inscription: 03-02-2009
Messages: 79
Site web

Re: Extraction d'une base de données de 18000 produits

Merci pour toute ces réponses je vais essayer un peut tout smile

Je suis obligé cependant de créer un tableau en session (voire fichier) car je procède en amont à une présélection des produits que je veux extraire (mais ça peut être tout ou tout - 1 ou tout -2 etc..).

Donc je vais voir avec un tableau juste d'ID ça devrait passer.

Dernière modification par citronbleu-v (07-01-2010 10:39:30)

Hors ligne

 

#7 07-01-2010 10:55:15

citronbleu-v
Membre
Lieu: Béziers ou Arles
Date d'inscription: 03-02-2009
Messages: 79
Site web

Re: Extraction d'une base de données de 18000 produits

Code:

        $db     = Zend_Registry::get('db');
        $stmt    = $db->query($query);
        
        $array = new ArrayObject();
        while ($row = $stmt->fetch()) 
               $array[] = $row['id_produit'];

j'ai fait comme ça, mais par contre où placer ce code dans quel Mapper ? mon Mapper_Produit je suppose ?
Pour le moment je l'ai mis dans une méthode d'un objet service ( Service_Produit et méthode getIdArticleByQuery($query) )

Hors ligne

 

#8 07-01-2010 15:32:24

sekaijin
Membre
Date d'inscription: 17-08-2007
Messages: 1137

Re: Extraction d'une base de données de 18000 produits

keilnoth a écrit:

Pour extraire un gros tas de données, il faut premièrement évaluer ce dont on a besoin en sortie. Il faut ensuite récupérer uniquement ce dont on a besoin. Donc, éviter le fetchAll() et éviter le SELECT * FROM.

Egalement, la gestion par objet de Zend est très gourmande en mémoire. Il ne faut pas hésiter à utiliser la méthode toArray() qui permet de gagner un bon gros tas de mémoire et de rapidité d'exécution.

Et finalement, comme déjà dit plus haut, procéder par batch, par exemple de 500 objets.

Et ne pas oublier de libérer la mémoire de manière explicite à chaque itération, sur les objets ou variables qu'on utilise plus.

attention un objet php et une hashTable (tableau associatif) sont très proche en taille mémoire et ne varient que de quelques octets et cela ne dépend ni de la classe ni du nombre de champs bref un StdClass Object est quasi identique à une hashTable

par contre un Zend_Db_Table_Row object est lui un objet qui contient 2 fois chaque valeur de chaque champs plus tout un tas d'autre truc et là c'est très gourmand

attention aussi au tableau suivant comment on fait son extraction avec les couches Db de php on peut obtenir des tableau qui sont plus gourmand que les objets (stdClass) quant on prend une ligne dans un rowset on peut le sortir sous forme de StdClass de tableau indexé, de tableau associatif ou de tableau mixte

le plus petit est le tableau indexé en php ce n'est pas une suite de valeurs comme en C ou C++ mais une hashTable (donc un tableau associatif) dont toutes les clefs sont sur 1 octets.
le StdClass et le tableau associatif pèsent la même chose sauf que chaque index est remplacé par le nom du champs ce qui représente la somme des longueurs des noms des champs en plus (la StdClass à un ou deux octets de plus je sais plus exactement combien en fait un pointeur vers la classe)

et le tableau mixte qui lui est le pire car il contient à la fois le tableau indexé et le tableau associatif il pèse donc (1 octet) x (le nombre de champs) + (la somme des longueurs des noms des champs) + 2 x (le poids des données de tous les champs) ce qui finalement n'est pas si loin d'un Znd_Db_Table_Row

A+JYT

Hors ligne

 

Pied de page des forums

Propulsé par PunBB
© Copyright 2002–2005 Rickard Andersson
Traduction par punbb.fr

Graphisme réalisé par l'agence Rodolphe Eveilleau
Développement par Kitpages