Traitement d’un gros volume de données avec Symfony et Doctrine

Bases de données, PHP Ajouter un commentaire

Retour d’expérience sur un projet avec Symfony, où il est question de consommation de mémoire excessive et de volumétrie.

Le site à réaliser est un portage d’un ancien projet PHP vers Symfony 1.2 + Doctrine. Un point d’attention en particulier touche à la reprise des données existantes du site V1 tournant sous un antique MySQL 3.23 et un stockage MyISAM.

Le développement d’une pake task a été privilégiée au détriment de fixtures YAML :

  • volume de données important (près de 500000 comptes utilisateurs à injecter dans des sfGuardUser, sans compter toutes les données dépendantes) qui aurait abouti à des fichiers YML longs à vérifier,
  • changements profonds dans la structure de la base de données entre la V1 et la V2
  • avoir le contrôle sur les données importées (notamment connaitre le nombre d’erreurs et leur nature)
  • pouvoir tirer malgré tout parti de la puissance de Symfony (Validators, Doctrine…)

Quelques points sont cependant importants à vérifier.

1) Attention aux fuites mémoire avec les objets

L’import des données a donné lieu a de nombreuses boucles pour fabriquer les objets Doctrine. Ces derniers utilisent beaucoup de références circulaires, que PHP ne gère plus depuis la 5.2.5.
Ceci se résoud avec deux appels importants :

  • utilisation de la méthode free d’un objet Doctrine qui libère les ressources allouées à l’objet, et se débarrasse des références circulaires
  • utilisation de unset($obj) pour supprimer la variable complètement.

La 1ère version du script qui engloutissait la mémoire, passe désormais sous une barre satisfaisante de 128M. Une boucle de traitement sur une ancienne table donne ceci :

foreach ($results as $datav1) {
    $o = new ObjectV2();
    $s->name = $datav1['name'];
    $o->save();
    $o->free();
    unset($o);
}

2) Faire travailler au maximum MySQL plutôt que PHP

Nous avons trouvé des adresses emails folkloriques dans les comptes utilisateurs : des valeurs avec espaces et des formats d’adresses non-standard principalement, qui ont fait hurler sfValidatorEmail tout le long de l’import. La solution :

  • requête de TRIM préalable pour “épurer” les adresses e-mail valides qui contenaient des espaces,
  • utilisation de la clause RLIKE sur la requête de chargement des utilisateurs, pour contrôler en amont - plutôt que de faire subir à Symfony et PHP - la validation de comptes avec des adresses e-mail bidons

Le requête de filtrage avec RLIKE donne ceci :

$sql = 'SELECT t.* FROM MaTableV1 t WHERE t.email RLIKE \'^[[:alnum:][.period.][.hyphen.][.underscore.]]+@([[:alnum:][.hyphen.][.underscore.]]+[.period.])+.([[:alnum:][.hyphen.]]){2,4}$\'';
$q = $conn->query($sql);
 
foreach ($q1 as $key => $dataV1) {
    // Traitement des données
}

Sur le schéma de la base de données “V1″, il est également nécessaire de s’assurer que les optimisations basiques sont en place pour les requêtes d’import :

  • présence de clés primaires
  • présence d’index sur les “clés étrangères”
  • présence d’index sur les champs utilisés dans les clauses WHERE, ORDER BY des requêtes d’import

A noter que la lecture des données de la V1 est effectuée directement à l’aide de PDO :

$conn = new PDO('mysql:dbname=' . $arguments['name'] . ';host=' . $arguments['host'], $arguments['username'], $arguments['password']);
...
$q = $conn->query($sql);

6 Réponses to “Traitement d’un gros volume de données avec Symfony et Doctrine”

  1. Hervé Says:

    Bonjour,

    Intéressant comme sujet mais cela m’amène à une autre question, qu’en est il des performances de Doctrine avec une telle masse de données ?

    A+
    Hervé

  2. Adrien Says:

    Sympa !

    J’ai deja eu peu ou proue les meme problematiques ; on les avait resolu a peu pres de la meme maniere :)

  3. Christophe Buguet Says:

    @Hervé : Doctrine à mon sens donne des résultats corrects. Même si un ORM n’est pas à proprement parler l’outil le plus performant pour faire des INSERT en masse, je le trouve assez confortable pour écrire un traitement clair pour un import.

  4. Christophe Buguet Says:

    Précision qui a son importance, Doctrine 2.0 apporte une fonctionnalité de Batch Processing qui permet de gérer plus proprement les Insertions/MAJ/Suppressions en masse. C’est expliqué ici : http://www.doctrine-project.org/documentation/manual/2_0/en/batch-processing

  5. desfrenes Says:

    Bonjour,

    Je me suis retrouvé dans une situation similaire, ce qui m’a amené à trouver d’autres solutions que Doctrine pour les nouveaux projets (unset et free n’ayant été d’aucun secours dans mon cas).

  6. hervé Says:

    merci pour l’info

Leave a Reply

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in