Wp_insert_post ve add_post_meta'yı toplu olarak daha hızlı bir yolla


16

Eklemek istediğim ~ 1.500 satır ve 97 sütundan oluşan bir csv dosyası var. Tam bir ithalat yapmak yaklaşık 2-3 saat sürer ve bir yol varsa bunu geliştirmek istiyorum. Şu anda her satır için $ post_id = wp_insert_post ve sonra her satır ile ilişkili 97 sütun için bir add_post_meta yapıyorum. Bu oldukça verimsiz ...

Bir post_id alabilirsiniz post ve post_meta değerleri arasındaki ilişkiyi tutmak bir şekilde bu konuda daha iyi bir yolu var mı?

Şu anda bunu wamp ile yerel makinemde deniyorum ama bir VPS üzerinde çalıştıracak


Aşağıdaki WP ipuçlarına ek olarak, MySQL'de InnoDB'yi kullanmaya bakın ve bu cevap başına işlemleri toplu olarak yapın .
webaware

Yanıtlar:


21

Bir zaman önce özel bir CSV içe aktarma ile benzer sorunlar yaşadım, ancak toplu ekleme için bazı özel SQL kullanarak sona erdi. Ama o zamana kadar bu cevabı görmemiştim:

Toplu ekleme için silme sonrası ekleme ve silme optimize edilmeli mi?

wp_defer_term_counting()terim saymayı etkinleştirmek veya devre dışı bırakmak için kullanılır .

Ayrıca , WordPress içe aktarma eklentisi için kaynağı kontrol ederseniz, toplu içe aktarmadan hemen önce şu işlevleri görürsünüz:

wp_defer_term_counting( true );
wp_defer_comment_counting( true );

ve sonra toplu ekleme sonra:

wp_defer_term_counting( false );
wp_defer_comment_counting( false );

Yani bu denemek için bir şey olabilir ;-)

Yayınları yayınlamak yerine taslak olarak içe aktarmak, her biri için benzersiz bir bilgi bulmanın yavaş süreci atlandığı için işleri hızlandırır. Bunlardan biri daha sonra daha küçük adımlarla yayınlanabilir, ancak bu tür bir yaklaşımın içe aktarılan yayınları bir şekilde işaretlemesi gerektiğine dikkat edin, bu yüzden daha sonra herhangi bir taslak yayınlamıyoruz! Bunun için dikkatli bir planlama ve büyük olasılıkla bazı özel kodlamalar gerekir.

post_nameİçe aktarılacak benzer posta başlıkları (aynı ) gibi çok sayıda varsa wp_unique_post_slug(), döngü sorgusu yinelemesi nedeniyle kullanılabilir bir bilgi bulmak için yavaşlayabilir. Bu muhtemelen çok sayıda db sorgusu oluşturabilir.

WordPress 5.1'den beri pre_wp_unique_post_slug, bilgi için döngü yinelemesini önlemek için filtre kullanılabilir. Bkz. Çekirdek bilet # 21112 . İşte bir örnek:

add_filter( 'pre_wp_unique_post_slug', 
    function( $override_slug, $slug, $post_id, $post_status, $post_type, $post_parent ) {
        // Set a unique slug value to shortcircuit the slug iteration loop.
        // $override_slug = ...

        return $override_slug;
    }, 10, 6
);

Biri örneğin as $override_slug = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix"ile çalışırsa , beklendiği gibi her zaman yeni gönderiler için olduğunu not ederiz . PHP gibi benzersiz sayılar oluşturmak için çeşitli yollar vardır . Ancak benzersiz salyangozlara sahip olduğunuzdan emin olmak için bu filtreyi dikkatli kullanın. Emin olmak için daha sonra örneğin bir grup sayısı sorgusu çalıştırabiliriz .$suffix$post_id$post_id0uniqid( '', true )post_name

Başka bir seçenek, zaman aşımını önlemek için WP-CLI kullanmak olacaktır . Örneğin , .csv dosyası kullanarak 20.000 Mesaj veya Sayfa Oluşturmak için gönderdiğim yanıta bakın ?

Daha sonra import.phpWP-CLI komutuyla özel PHP içe aktarma komut dosyamızı çalıştırabiliriz :

wp eval-file import.php

Ayrıca, geçerli wp-admin kullanıcı arayüzü iyi işlemediğinden, çok sayıda hiyerarşik gönderi türü içe aktarmaktan kaçının. Örneğin bkz Özel yazı alanı - yayınların listesini - Ölüm beyaz ekran

@Otto'dan harika ipucu:

Toplu eklemeden önce autocommitmodu açıkça devre dışı bırakın:

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

Toplu eklemelerden sonra şunu çalıştırın:

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

Ben de bazı temizlik gibi iyi bir fikir olacağını düşünüyorum:

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

Bu MyISAM üzerinde test etmedim ama bu InnoDB üzerinde çalışması gerekir .

@Kovshenin tarafından belirtildiği gibi bu ipucu MyISAM için işe yaramaz .


6
Buna ek olarak, daha önce otomatik taahhüdü kapatmak için sorgu işlevini kullanabilir ve eklemeler yapıldıktan sonra manuel olarak işleme koyabilirsiniz. Bu, toplu ekler yaparken DB düzeyindeki işlemleri büyük ölçüde hızlandırır. Eklemeden SET autocommit=0;önce a , ardından da gönderin COMMIT;.
Otto

İlginç, bunun için teşekkürler! Eve geldiğimde test etmem gerekecek.
Corey Rowell

@Otto, harika ipucu için teşekkürler. Eklemeden $wpdb->query('SET autocommit = 0;');önce yapabiliriz, ancak $wpdb->query('START TRANSACTION;');bu durumda atlayabilir miyiz ? Bu konuda daha fazla bilgi edinmek için MySQL kılavuzuna bakacağım ;-) şerefe.
birgire

1
İyi nokta Mark. Bunlar yalnızca ekler ve güncellemeler değilse wp_suspend_cache_addition( true ), nesne önbelleğine bir şey KOYMAMAYA yardımcı olmalıdır. Ayrıca @birgire bunu MyISAM ile test etmediklerinden bahsetti - rahatsız etmeyin, depolama motoru işlemleri desteklemediğinden otomatik devreye alma veya bir işlem başlatmanın sıfır etkisi olacaktır.
kovshenin

1
büyük ipucu @Otto. Sorgum daha önce 38 saniye sürdü, şimdi 1 saniye sürüyor.
Annapurna

5

Kimliğinizi almak için gönderi eklemeniz gerekir, ancak $wpdb->postmetatablonun yapısı çok basittir. Muhtemelen MySQL belgelerinde olduğu gibi düz bir INSERT INTOifade kullanabilirsiniz :INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);

Senin durumunda...

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

Bu herhangi bir kodlama, serileştirme, kaçış, hata kontrolü, kopyalar veya başka bir şeyle uğraşmayacak, ancak daha hızlı olmasını beklerdim (denemedim).

Bunu kapsamlı bir test yapmadan bir üretim sitesinde yapmazdım ve eğer sadece bir veya iki kez yapmak zorunda kalsaydım, temel işlevleri kullanır ve işler içe aktarılırken uzun bir öğle yemeği alırdım.


Uzun bir öğle yemeği alacağım, tablolarıma ham veri eklemeyeceğim ve Wordpress'in zaten ne yapacağını yeniden yazmanın bir anlamı yok.
Corey Rowell

1
mysql enjeksiyonu böyle olur, bu yüzden lütfen bunu kullanmayın.
OneOfOne

Her şey kodlanmış, @OneOfOne. Enjeksiyon, kullanıcı tarafından sağlanan girdi olmadan tanım olarak yapılamaz. "Enjeksiyonun" doğası budur. OP, kontrolü altındaki kodu kullanarak denetimi altındaki bir .csv dosyasından veri alıyor. Üçüncü bir tarafın herhangi bir şey enjekte etmesi için bir fırsat yoktur. Lütfen bağlama dikkat edin.
s_ha_dum

+1 benden, 20 gümrük alanı değerleri eklemek gerekiyordu ve bu "
add_post_meta

1
OP'nin CSV dosyasını içe aktarmadan önce iyice kontrol etmesini bekleyemezsiniz ve bu nedenle kullanıcı girişi ve en azından ->prepare()SQL ifadeleriniz olarak ele almalısınız . Senaryonuzda, CSV'deki kimlik sütunu benzer bir şey içeriyorsa ne olurdu 1, 'foo', 'bar'); DROP TABLE wp_users; --? Muhtemelen kötü bir şey.
kovshenin

5

Bunu eklemek zorunda kaldım:

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

do_all_pingsPingbacks, muhafazalar, trackbacks ve diğer ping'leri işleyen bunun atlanacağını unutmayın (bağlantı: https://developer.wordpress.org/reference/functions/do_all_pings/ ). Koda baktığımdan anladığım kadarıyla, beklemedeki pingbacks / trackbacks / muhafazaları bu remove_actionsatırı kaldırdıktan sonra hala işlenecek , ancak tam olarak emin değilim.

Güncelleme: Ayrıca ekledim

    define( 'WP_IMPORTING', true );

Bunun ötesinde kullanıyorum:

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

1

Hakkında önemli not 'SET autocommit = 0;'

ayarladıktan sonra autocommit = 0komut (nedense, benzerleri için yürütme durursa exit, o zaman değişiklikler olmayacağım KAYITLI OLARAK DB, ölümcül hata veya vs ...)!

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

update_option("something", "value");     

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

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

Bu durumda update_optionDB'ye kaydedilmez!

Yani, en iyi tavsiye etmiş olduğu COMMITtescil shutdownbir precatuion (durumunda herhangi bir beklenmedik bir çıkış olur) olarak işlevi.

register_shutdown_function( function(){
    $GLOBALS['wpdb']->query( 'COMMIT;' );
} );
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.