¿Cómo puedo acelerar la función node_save () de drupal?

9

Estoy teniendo muchos problemas con la ineficiencia de node_save (). ¿Pero el nodo salva mi problema? En última instancia, eso es lo que estoy tratando de descubrir.

Creé un bucle con 100,000 iteraciones. Creé el mínimo para que el objeto de nodo sea válido y se guarde correctamente. Aquí está el código de guardado del nodo:

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

Aquí están los resultados:

Se guardaron 100,000 nodos, cada uno usando node_save (). Tardó 5196.22 segundos en completarse. Eso es SOLO 19 salvamentos por segundo.

Por decir lo menos, eso no es aceptable, especialmente cuando esta persona recibe alrededor de 1200 consultas de inserción individuales por segundo , y esta persona obtiene 25,000 inserciones por segundo .

Entonces, ¿qué está pasando aquí? ¿Dónde está el cuello de botella? ¿Es con la función node_save () y cómo está diseñado?

¿Podría ser mi hardware? Mi hardware es un servidor de desarrollo, nadie más que yo, Intel dual core, 3Ghz, Ubuntu 12.04 con 16 gigas de ram.

Mientras el ciclo se ejecuta, mi uso de recursos es: MySQL 27% CPU, 6M RAM; PHP 22% CPU 2M RAM.

Mi configuración mysql fue realizada por el asistente de percona .

Mysql dice que si el uso de mi CPU es inferior al 70%, mi problema está relacionado con el disco . De acuerdo, solo tengo una ejecución del molino WD Caviar 7200 RPM, ¡pero espero obtener más de 19 inserciones por segundo!

No hace mucho tiempo escribí sobre salvar 30,000 nodos en un día . Sin embargo, para ser claros, este nodo no tiene nada que ver con ninguna fuerza externa. Es puramente un punto de referencia aprender sobre cómo aumentar la velocidad de las llamadas a node_save ().

Siendo realistas, necesito ingresar 30,000 elementos en la base de datos cada minuto usando node_save. Si el guardado de nodos no es una opción, me pregunto si puedo escribir mi propia función api de drupal "node_batch_save ()" o algo que aproveche la capacidad de mysql para hacer inserciones masivas con la consulta INSERT . ¿Pensamientos sobre cómo abordar esto?

blue928
fuente
2
Hay una gran diferencia entre el rendimiento de inserción sin procesar y lo que hará node_save. Por un lado, node_save realiza una serie de lecturas y escrituras. Pero no tiene sentido discutir posibles cuellos de botella y optimizaciones sin más datos.
Alfred Armstrong
Debe tener en cuenta por qué está usando Drupal de esta manera para sus propósitos. Si simplemente desea capturar una gran cantidad de datos en una tabla plana y mostrarlos usando Drupal, es posible que desee omitir Drupal por completo al escribirlos y usar un módulo personalizado para integrar los datos usando Vistas, etc.
Alfred Armstrong
Dudo que el cuello de la botella esté en el lado de la base de datos. Node save hace muchas cosas en segundo plano: invocará una serie de ganchos (hook_node_presave, hook_entity_presave, hook_node_insert, hook_entity_insert, etc.), cada uno de los cuales puede llamar a cualquier número de módulos. Además node_save reconstruirá los permisos para ese nodo y borrará el caché para ese nodo ...
Alice Heaton
@AlfredArmstrong Estoy creando nodos basados ​​en datos que están en otra base de datos. Moldeo los datos al tipo de contenido de drupal correcto y node_save. Mis clientes son principalmente universidades que desean cambiar a drupal. No es raro que tengan entre 200,000 y 1,000,000 nodos (contenido del sitio de los departamentos, registros de estudiantes y profesores, etc.) a los que les gustaría migrar después de una década de usar su propia solución web. Leí esto, que es alentador, pero aún menos que un enfoque deseable. evolvingweb.ca/story/…
blue928
.. entonces, preferiría quedarme tan drupalmente como sea posible. El uso de guardar nodo con esta cantidad de datos garantiza la integridad. Si no puedo hacer que eso funcione, estoy dispuesto a ser creativo.
blue928

Respuestas:

10

Nunca obtendrá 30 000 inserciones por minuto usando node_save. De ninguna manera.

Un INSERT es rápido porque eso es todo lo que hace. Node save realiza múltiples inserciones (tabla principal, tabla de revisión, una tabla para cada campo), borra cualquier caché de entidad y dispara ganchos. Los ganchos son la parte difícil. Si tiene muchos módulos contrib (o incluso uno que se porta mal) que realmente pueden matar el rendimiento, especialmente si el autor no tuvo en cuenta el caso de uso "Estoy ahorrando una tonelada de nodos a la vez". Por ejemplo, tuve que agregar esto a mi clase 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);
  }

Por otro lado, si escribe una función de guardado personalizada que no invoca ganchos, está en claro peligro de obtener datos inconsistentes, en un estado inesperado por el sistema. Nunca recomendaría hacer eso. Enciende xhprof y mira lo que está sucediendo.

Bojan Zivanovic
fuente
Algunos de los módulos de migración disponibles, ¿cómo terminan ahorrando nodos en masa? Quiero decir, al final de todo, todo se reduce a una declaración INSERT, ¿verdad? ¿Cómo se inserta en última instancia su clase de migración de 'fuente' a 'destino' cuando no usa el guardado de nodo pero aún necesita mantener la integridad de los datos en las tablas?
blue928
Todos los módulos de migración que he encontrado utilizan un node_save.
Alfred Armstrong
1
@ blue928 Él dice que usa node_save(), pero agrega un código para mitigar los problemas conocidos que pueden ser causados, como Pathauto reconstruyendo el caché del menú después de cada guardado de nodo
Clive
ah, ok, ya veo. ¿Bojan es su código disponible en un módulo o en línea donde pude ver cómo ha lidiado con cuellos de botella como path auto? Buena idea con el xhprof. Lo comprobaré.
blue928
5

En primer lugar, instale XCache / APC (para PHP <5.5) y configure memcached para Drupal.

Luego puede optimizar su configuración de MySQL para consultas pesadas utilizando el script mysqltuner disponible en: http://mysqltuner.pl

P.ej

# 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

Otras sugerencias:

  • deshabilite los módulos que no necesita (por ejemplo , Devel , módulo de registro de base de datos central, etc.),
  • Actualice su PHP a la última rama o superior,
  • recompile su PHP para una arquitectura de 64 bits o superior dependiendo de su CPU,
  • use el dispositivo de almacenamiento más rápido para sus archivos db o todo el entorno LAMP (por ejemplo, SSD o sistema de archivos basado en memoria ),
  • use el depurador PHP o el generador de perfiles para descubrir cualquier cuello de botella de rendimiento (por ejemplo, XDebug Profiler , DTrace o NuSphere PhpED PHP Profiler ),
  • ejecuta un comando drush que consume mucho tiempo con la herramienta de creación de perfiles gprof , para que puedas encontrar también un cuello de botella en el rendimiento
kenorb
fuente
1
Ajustar MySQL parece hacer una gran diferencia. Pasé de aproximadamente 80 node_saves por minuto a aproximadamente 700 solo siguiendo los consejos dados por mysqltuner.pl.
John McCollum