Manera más rápida de wp_insert_post & add_post_meta a granel

12

Tengo un archivo csv que quiero insertar que consta de ~ 1,500 filas y 97 columnas. Se tarda aproximadamente 2-3 horas para realizar una importación completa y me gustaría mejorar esto si hay una manera. Actualmente, para cada fila estoy haciendo un $ post_id = wp_insert_post y luego un add_post_meta para las 97 columnas asociadas con cada fila. Esto es bastante ineficiente ...

¿Hay una mejor manera de hacerlo para que un post_id pueda mantener la relación entre post y sus valores post_meta?

Ahora mismo estoy probando esto en mi máquina local con wamp, pero lo haré funcionar en un VPS

    
pregunta Corey Rowell 08.06.2013 - 18:37

4 respuestas

16

Hace algún tiempo tuve problemas similares con una importación de CSV personalizada, pero terminé usando un SQL personalizado para la inserción masiva. Pero no había visto esta respuesta para entonces:

¿Optimizar la inserción y eliminación de publicaciones para operaciones masivas?

para usar wp_defer_term_counting() para habilitar o deshabilitar el conteo de términos.

También si revisa la fuente para la El complemento de importación de WordPress, verá estas funciones justo antes de la importación masiva:

wp_defer_term_counting( true );
wp_defer_comment_counting( true );

y luego después de la inserción masiva:

wp_defer_term_counting( false );
wp_defer_comment_counting( false );

Entonces, esto podría ser algo para probar ;-)

Importar publicaciones como draft en lugar de publicar , también acelerará las cosas, ya que se omite el proceso lento de encontrar un slug único para cada una. Se podría, por ejemplo, publíquelas más tarde en pasos más pequeños, pero tenga en cuenta que este tipo de enfoque debería marcar las publicaciones importadas de alguna manera, ¡así que no solo publicamos los borradores más tarde! Esto requeriría una planificación cuidadosa y muy probablemente alguna codificación personalizada.

Otra opción sería utilizar WP-CLI para evitar el tiempo de espera. Ver por ejemplo Mi respuesta publicada para ¿Creando 20,000 publicaciones o páginas usando un archivo .csv?

También evite importar un gran número de tipos de publicaciones jerárquicas, ya que la interfaz de usuario actual de wp-admin no lo maneja bien. Ver por ejemplo Tipo de publicación personalizada - lista de publicaciones - pantalla blanca de la muerte

Aquí está el gran consejo de @otto:

Antes de inserciones masivas , deshabilite el modo autocommit explícitamente:

$wpdb->query( 'SET autocommit = 0;' );

Después de las inserciones masivas, ejecuta:

$wpdb->query( 'COMMIT;' );

También creo que sería una buena idea hacer algunas tareas de limpieza como:

$wpdb->query( 'SET autocommit = 1;' );

No he probado esto en MyISAM pero esto debería funcionar en InnoDB .

Como mencionado por @kovshenin, esta sugerencia no funcionaría para MyISAM .

    
respondido por el birgire 08.06.2013 - 21:36
5

Necesitará insertar la publicación para obtener su ID, pero la tabla $wpdb->postmeta es muy simple en su estructura. Probablemente podría usar una declaración INSERT INTO directa, así desde la documentación de MySQL: INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);

En tu caso ...

$ID = 1; // from your wp_insert_post
$values = '($ID,2,3),($ID,5,6),($ID,8,9)'; // build from your 97 columns; I'd use a loop of some kind
$wpdb->query("INSERT INTO {$wpdb->postmeta} (post_id,meta_key,meta_value) VALUES {$values}");

Eso no tratará con ninguna codificación, serialización, escape, comprobación de errores, duplicaciones o cualquier otra cosa, pero esperaría que fuera más rápido (aunque no lo he intentado).

No haría esto en un sitio de producción sin realizar pruebas exhaustivas, y si solo tuviera que hacerlo una o dos veces, usaría las funciones principales y tomaría un largo almuerzo mientras las cosas importan.

    
respondido por el s_ha_dum 08.06.2013 - 19:13
3

Tuve que agregar esto:

    remove_action('do_pings', 'do_all_pings', 10, 1);

Tenga en cuenta que esto omitirá do_all_pings , que procesa pingbacks, recintos, trackbacks y otros ping (enlace: enlace ). Según entiendo el código, entiendo que los pingbacks / trackbacks / recintos pendientes aún se procesarán después de que elimines esta línea remove_action , pero no estoy completamente seguro.

Actualización: también he añadido

    define( 'WP_IMPORTING', true );

Más allá de eso estoy usando:

    ini_set("memory_limit",-1);
    set_time_limit(0);
    ignore_user_abort(true);

    wp_defer_term_counting( true );
    wp_defer_comment_counting( true );
    $wpdb->query( 'SET autocommit = 0;' );

    /* Inserting 100,000 posts at a time
       including assigning a taxonomy term and adding meta keys
       (i.e. a 'foreach' loop with each loop containing:
       'wp_insert_post', 'wp_set_object_terms', 'add_post_meta'.)
    */

    $wpdb->query( 'COMMIT;' );
    wp_defer_term_counting( false );
    wp_defer_comment_counting( false );
    
respondido por el firasd 01.08.2016 - 23:41
0

Nota importante sobre 'SET autocommit = 0;'

después de configurar autocommit = 0 si la secuencia de comandos detiene la ejecución (por algún motivo, como exit , error fatal o etc ...), sus cambios NO SE GUARDARÁN EN DB.

$wpdb->query( 'SET autocommit = 0;' );

update_option("something", "value");     

exit; //lets say, here happens error or anything...

$wpdb->query( 'COMMIT;' );

¡En este caso, update_option no se guardará en DB!

Por lo tanto, el mejor consejo es tener COMMIT registrado en la función shutdown como una recomendación previa (en caso de que ocurra una salida inesperada).

register_shutdown_function( function(){
    $GLOBALS['wpdb']->query( 'COMMIT;' );
} );
    
respondido por el T.Todua 14.09.2018 - 19:04

Lea otras preguntas en las etiquetas