Comment puis-je accélérer la fonction node_save () de drupal?

9

J'ai beaucoup de mal avec l'inefficacité de node_save (). Mais le nœud sauve-t-il mon problème? C'est finalement ce que j'essaie de découvrir.

J'ai créé une boucle avec 100 000 itérations. J'ai créé le strict minimum pour que l'objet nœud soit valide et enregistre correctement. Voici le code de sauvegarde du nœud:

$node = new stdClass();
        $node->type = "test_page";

        node_object_prepare($node);

        $node->uid = 1;
        $node->title = $node_title;
        $node->status = 1;
        $node->language = LANGUAGE_NONE;
        if($node = node_submit($node)){
            node_save($node);
}

Voici les résultats:

100 000 nœuds ont été enregistrés, chacun utilisant node_save (). Il a fallu 5196,22 secondes pour terminer. C'est SEULEMENT 19 sauve une seconde.

Pour dire le moins, ce n'est pas acceptable, surtout lorsque cette personne reçoit environ 1 200 requêtes d'insertion individuelles par seconde , et que cette personne reçoit 25 000 insertions par seconde .

Alors, que se passe-t-il ici? Où est le goulot d'étranglement? Est-ce la fonction avec la fonction node_save () et comment est-elle conçue?

Serait-ce mon matériel? Mon matériel est un serveur de développement, personne d'autre que moi - Intel dual core, 3Ghz, Ubuntu 12.04 avec 16 Go de RAM.

Pendant que la boucle fonctionne, mon utilisation des ressources est: MySQL 27% CPU, 6M RAM; PHP 22% CPU 2M RAM.

Ma configuration mysql a été effectuée par l' assistant percona .

Mysql dit que si mon utilisation CPU est inférieure à 70%, mon problème est lié au disque . Certes, je n'ai qu'une course du moulin WD Caviar 7200 RPM, mais je devrais obtenir plus de 19 insertions par seconde avec lui, j'espère!

Il n'y a pas si longtemps, j'ai écrit sur la sauvegarde de 30 000 nœuds par jour . Cependant, pour être clair, ce nœud n'a rien à voir avec des forces externes. C'est purement une référence pour savoir comment augmenter la vitesse des appels à node_save ().

De manière réaliste, j'ai besoin d'obtenir 30 000 éléments dans la base de données chaque minute en utilisant node_save. Si la sauvegarde de nœud n'est pas une option, je me demande si je peux écrire ma propre fonction drupal api "node_batch_save ()" ou quelque chose qui tire parti de la capacité de mysql à faire des insertions en masse avec la requête INSERT . Réflexions sur la façon d'aborder cela?

blue928
la source
2
Il y a une grande différence entre les performances d'insertion brutes et ce que fera node_save. Pour une chose, node_save effectue une série de lectures et d'écritures. Mais il est inutile de discuter des goulots d'étranglement et des optimisations possibles sans plus de données.
Alfred Armstrong
Vous devez vous demander pourquoi vous utilisez Drupal de cette manière pour vos besoins. Si vous voulez simplement capturer beaucoup de données dans une table plate et l'afficher à l'aide de Drupal, vous voudrez peut-être contourner Drupal complètement lors de l'écriture et utiliser un module personnalisé pour intégrer les données à l'aide de vues, etc.
Alfred Armstrong
Je doute que le col de la bouteille soit du côté de la base de données. La sauvegarde de noeud fait beaucoup de choses en arrière-plan: elle invoquera un certain nombre de hooks (hook_node_presave, hook_entity_presave, hook_node_insert, hook_entity_insert, etc.), chacun pouvant appeler n'importe quel nombre de modules. De plus, node_save reconstruira les autorisations pour ce nœud et effacera le cache pour ce nœud ...
Alice Heaton
@AlfredArmstrong Je crée des nœuds à partir de données qui se trouvent dans une autre base de données. Je moule les données au type de contenu Drupal correct et je les enregistre par nœud. Mes clients sont principalement des universités souhaitant passer à Drupal. Il n'est pas rare qu'ils aient entre 200 000 et 1 000 000 nœuds (contenu du site des députés, dossiers des étudiants et des professeurs, etc.) sur lesquels ils aimeraient migrer après une décennie d'utilisation de leur propre solution Web. J'ai lu ceci, ce qui est encourageant, mais encore moins que l'approche souhaitable. evolvingweb.ca/story/…
blue928
.. donc, je préfère rester aussi drupement que possible. L'utilisation de la sauvegarde de noeud avec autant de données garantit l'intégrité. Si je n'arrive pas à faire fonctionner cela, je suis prêt à faire preuve de créativité.
blue928

Réponses:

10

Vous n'obtiendrez jamais 30 000 insertions par minute en utilisant node_save. En aucune façon.

Un INSERT est rapide car c'est tout ce qu'il fait. L'enregistrement de nœud effectue plusieurs insertions (table principale, table de révision, une table pour chaque champ), efface tous les caches d'entités et déclenche les crochets. Les crochets sont la partie délicate. Si vous avez de nombreux modules contrib (ou même un qui se comporte mal) qui peuvent vraiment nuire aux performances, surtout si l'auteur n'a pas tenu compte du cas d'utilisation "Je sauvegarde une tonne de nœuds à la fois". Par exemple, j'ai dû ajouter ceci à ma classe Migrate:

  public function processImport(array $options = array()) {
    parent::processImport($options = array());
    // Do not force menu rebuilding. Otherwise pathauto will try to rebuild
    // in each node_save() invocation.
    variable_set('menu_rebuild_needed', FALSE);
  }

D'un autre côté, si vous écrivez une fonction de sauvegarde personnalisée qui n'invoque aucun hook, vous courez un risque évident d'obtenir des données incohérentes, dans un état inattendu par le système. Je ne recommanderais jamais de faire ça. Lancez xhprof et voyez ce qui se passe.

Bojan Zivanovic
la source
Certains modules de migration existent, comment finissent-ils par enregistrer des nœuds en bloc? Je veux dire, à la fin de tout cela, tout se résume à une instruction INSERT, non? Comment votre classe de migration s'insère-t-elle finalement de «source» à «cible» lorsque vous n'utilisez pas la sauvegarde de noeud mais que vous avez toujours besoin de maintenir l'intégrité des données entre les tables?
blue928
Tous les modules de migration que j'ai rencontrés utilisent un node_save.
Alfred Armstrong
1
@ blue928 Il dit qu'il fait usage node_save(), mais ajoute un peu de code pour atténuer les problèmes connus qui peuvent être causés, comme Pathauto la reconstruction du cache de menu après chaque noeud de sauvegarde
Clive
ah, ok, je vois. Bojan est votre code disponible dans un module ou en ligne où je pourrais voir comment vous avez traité les goulots d'étranglement comme le chemin automatique? Bonne idée avec le xhprof. Je vais vérifier cela.
blue928
5

Tout d'abord, installez XCache / APC (pour PHP <5.5) et configurez memcached pour Drupal.

Ensuite, vous pouvez optimiser votre configuration MySQL pour les requêtes lourdes en utilisant le script mysqltuner disponible sur: http://mysqltuner.pl

Par exemple

# performance tweaks (adjusted based on mysqltuner.pl)
query_cache_size = 32M
query_cache_limit = 256M
join_buffer_size = 32M
key_buffer = 8M
max_allowed_packet = 32M
table_cache = 512
sort_buffer_size = 1M
net_buffer_length = 8K
read_buffer_size = 256K
read_rnd_buffer_size = 1M
myisam_sort_buffer_size = 8M

# When making adjustments, make tmp_table_size/max_heap_table_size equal
tmp_table_size = 16M
max_heap_table_size = 16M

thread_cache_size = 4

Autres suggestions:

  • désactiver les modules dont vous n'avez pas besoin (par exemple Devel , module de base de données de journalisation, etc.),
  • mettez à jour votre PHP vers la dernière ou plus récente branche,
  • recompilez votre PHP pour une architecture 64 bits ou supérieure en fonction de votre CPU,
  • utiliser le périphérique de stockage plus rapide pour vos fichiers db ou tout l'environnement LAMP (par exemple SSD ou système de fichiers basé sur la mémoire ),
  • utiliser le débogueur PHP ou le profileur pour trouver tout goulot d'étranglement des performances (par exemple XDebug Profiler , DTrace ou NuSphere PhpED PHP Profiler ),
  • exécutez une commande drush qui prend du temps sous l' outil de profilage gprof , afin que vous puissiez également trouver un goulot d'étranglement des performances
Kenorb
la source
1
Le réglage de MySQL semble faire une grande différence. Je suis passé d'environ 80 node_saves par minute à environ 700 en suivant simplement les conseils donnés par mysqltuner.pl.
John McCollum