Cách nhanh hơn để wp_insert_post & add_post_meta hàng loạt


16

Tôi có một tệp csv mà tôi muốn chèn bao gồm ~ 1.500 hàng và 97 cột. Mất khoảng 2-3 giờ để nhập đầy đủ và tôi muốn cải thiện điều này nếu có cách. Hiện tại cho mỗi hàng tôi đang thực hiện $ post_id = wp_insert_post và sau đó là add_post_meta cho 97 cột được liên kết với mỗi hàng. Điều này khá kém hiệu quả ...

Có cách nào tốt hơn để giải quyết vấn đề này theo cách mà một người có thể có được một post_id giữ mối quan hệ giữa bài viết và các giá trị post_meta của nó không?

Ngay bây giờ tôi đang thử cái này trên máy cục bộ của tôi với wamp nhưng sẽ có nó chạy trên VPS


Ngoài các mẹo WP bên dưới, cũng xem xét sử dụng InnoDB trong MySQL và cam kết giao dịch theo đợt, theo câu trả lời này .
web biết

Câu trả lời:


21

Tôi đã gặp vấn đề tương tự trước đây khi nhập CSV tùy chỉnh, nhưng tôi đã kết thúc bằng cách sử dụng một số SQL tùy chỉnh để chèn hàng loạt. Nhưng tôi đã không thấy câu trả lời này sau đó:

Tối ưu hóa bài chèn và xóa cho các hoạt động hàng loạt?

để sử dụng wp_defer_term_counting()để kích hoạt hoặc vô hiệu hóa tính thời hạn.

Ngoài ra nếu bạn kiểm tra nguồn cho plugin nhập khẩu WordPress, bạn sẽ thấy các chức năng này ngay trước khi nhập hàng loạt:

wp_defer_term_counting( true );
wp_defer_comment_counting( true );

và sau đó chèn số lượng lớn:

wp_defer_term_counting( false );
wp_defer_comment_counting( false );

Vì vậy, đây có thể là một cái gì đó để thử ;-)

Nhập bài viết dưới dạng bản nháp thay vì xuất bản , cũng sẽ tăng tốc mọi thứ, vì quá trình chậm chạp trong việc tìm kiếm một con sên duy nhất cho mỗi cái bị bỏ qua. Người ta có thể ví dụ xuất bản chúng sau này trong các bước nhỏ hơn, nhưng lưu ý rằng cách tiếp cận này sẽ cần đánh dấu các bài đăng được nhập bằng cách nào đó, vì vậy chúng tôi không xuất bản bất kỳ bản nháp nào sau này! Điều này sẽ cần lập kế hoạch cẩn thận và rất có thể là một số mã hóa tùy chỉnh.

Nếu có rất nhiều tiêu đề bài tương tự (giống nhau post_name) được nhập, thì wp_unique_post_slug()có thể trở nên chậm, do lặp lại truy vấn vòng lặp để tìm một sên có sẵn. Điều này có thể có thể tạo ra một số lượng lớn các truy vấn db.

Vì WordPress 5.1, pre_wp_unique_post_slugbộ lọc có sẵn để tránh vòng lặp cho sên. Xem vé lõi # 21112 . Đây là một ví dụ:

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

Nếu một cố gắng ví dụ như $override_slug = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix"với $suffixnhư $post_id, sau đó chúng tôi xin lưu ý rằng $post_idluôn luôn là 0bài mới, như mong đợi. Có nhiều cách khác nhau để tạo ra các số duy nhất trong PHP, như thế uniqid( '', true ). Nhưng hãy cẩn thận sử dụng bộ lọc này để đảm bảo bạn có những con sên độc đáo. Chúng tôi có thể ví dụ chạy một truy vấn đếm nhóm sau đó post_nameđể chắc chắn.

Một lựa chọn khác là sử dụng WP-CLI để tránh thời gian chờ. Xem ví dụ câu trả lời của tôi được đăng để Tạo 20.000 Bài đăng hoặc Trang bằng tệp .csv?

Sau đó, chúng ta có thể chạy tập lệnh nhập PHP tùy chỉnh của mình bằng lệnh import.phpWP-CLI:

wp eval-file import.php

Ngoài ra, tránh nhập số lượng lớn các loại bài đăng phân cấp, vì giao diện người dùng wp-admin hiện tại không xử lý tốt. Xem ví dụ: Loại bài đăng tùy chỉnh - danh sách bài viết - màn hình trắng của cái chết

Đây là mẹo tuyệt vời từ @otto:

Trước khi chèn hàng loạt , hãy tắt autocommitchế độ một cách rõ ràng:

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

Sau khi chèn số lượng lớn, chạy:

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

Tôi cũng nghĩ rằng sẽ là một ý tưởng tốt để làm một số công việc vệ sinh như:

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

Tôi đã không thử nghiệm điều này trên MyISAM nhưng nó sẽ hoạt động trên InnoDB .

Như được đề cập bởi @kovshenin, mẹo này sẽ không hiệu quả với MyISAM .


6
Ngoài ra, bạn cũng có thể sử dụng chức năng truy vấn để tắt tự động tự động trước đó và sau đó cam kết thủ công sau khi quá trình chèn đã được thực hiện. Điều này giúp tăng tốc đáng kể các hoạt động ở cấp DB khi thực hiện chèn số lượng lớn. Chỉ cần gửi một SET autocommit=0;trước khi chèn, tiếp theo COMMIT;sau đó.
Otto

Thật thú vị, cảm ơn vì điều đó! Tôi sẽ phải kiểm tra nó khi tôi về nhà.
Corey Rowell

@Otto, cảm ơn vì lời khuyên tuyệt vời. Vì vậy, chúng ta có thể làm $wpdb->query('SET autocommit = 0;');trước khi chèn nhưng chúng ta có thể bỏ qua $wpdb->query('START TRANSACTION;');trong trường hợp đó không? Tôi sẽ kiểm tra hướng dẫn sử dụng MySQL để tìm hiểu thêm về nó ;-) chúc mừng.
bạch dương

1
Điểm tốt Mark. Nếu đây chỉ là các phần chèn và không cập nhật, thì wp_suspend_cache_addition( true )nên giúp KHÔNG đặt nội dung vào bộ đệm đối tượng. Ngoài ra @birgire đã đề cập rằng họ đã không kiểm tra điều này với MyISAM - đừng bận tâm, công cụ lưu trữ không hỗ trợ các giao dịch nên việc thiết lập tự động hoặc bắt đầu giao dịch sẽ không có hiệu lực.
kovshenin

1
mẹo tuyệt vời @Otto. Truy vấn của tôi trước đó mất 38 giây, bây giờ mất 1 giây.
Annapurna

5

Bạn sẽ cần phải chèn bài đăng để lấy ID của mình nhưng $wpdb->postmetabảng có cấu trúc rất đơn giản. Bạn có thể có thể sử dụng một INSERT INTOcâu lệnh thẳng , như thế này từ các tài liệu MySQL:INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);

Trong trường hợp của bạn...

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

Điều đó sẽ không giải quyết bất kỳ mã hóa, tuần tự hóa, thoát, kiểm tra lỗi, sao chép hoặc bất cứ điều gì khác, nhưng tôi hy vọng nó sẽ nhanh hơn (mặc dù tôi đã không thử).

Tôi sẽ không làm điều này trên một trang sản xuất mà không kiểm tra kỹ lưỡng và nếu tôi chỉ phải thực hiện một hoặc hai lần, tôi sẽ sử dụng các chức năng cốt lõi và ăn trưa dài trong khi mọi thứ nhập khẩu.


Hãy nghĩ rằng tôi sẽ có một bữa ăn trưa dài, thay vì không chèn dữ liệu thô vào các bảng của mình và không có ý nghĩa gì trong việc viết lại những gì Wordpress sẽ làm.
Corey Rowell

1
Đây là cách tiêm mysql xảy ra, vì vậy xin vui lòng không sử dụng điều này.
OneOfOne

Mọi thứ đều được mã hóa cứng, @OneOfOne. Tiêm không - không thể theo định nghĩa-- xảy ra mà không có đầu vào do người dùng cung cấp. Đó là bản chất của "tiêm". OP đang nhập dữ liệu từ tệp .csv nằm dưới sự kiểm soát của anh ấy bằng mã dưới sự kiểm soát của anh ấy. Không có cơ hội cho bên thứ ba tiêm bất cứ thứ gì. Hãy chú ý đến bối cảnh.
s_ha_dum

+1 từ tôi, tôi cần thêm 20 giá trị trường hải quan và tốc độ này nhanh hơn nhiều so với "
add_post_meta

1
Bạn không thể mong đợi OP kiểm tra kỹ tệp CSV trước khi nhập nó và do đó bạn nên coi nó là đầu vào của người dùng và ít nhất là ->prepare()các câu lệnh SQL của bạn. Trong kịch bản của bạn, điều gì sẽ xảy ra nếu cột ID trong CSV chứa nội dung như thế 1, 'foo', 'bar'); DROP TABLE wp_users; --nào? Một cái gì đó xấu có lẽ.
kovshenin

5

Tôi đã phải thêm điều này:

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

Hãy nhớ rằng điều này sẽ bỏ qua do_all_pings, xử lý pingback, bao vây, trackback và các ping khác (liên kết: https://developer.wordpress.org/reference/fifts/do_all_pings/ ). Sự hiểu biết của tôi khi xem mã là các pingback / trackbacks / thùng đang chờ xử lý vẫn sẽ được xử lý sau khi bạn xóa remove_actiondòng này , nhưng tôi không hoàn toàn chắc chắn.

Cập nhật: Tôi cũng đã thêm

    define( 'WP_IMPORTING', true );

Ngoài ra tôi đang sử dụng:

    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

Lưu ý quan trọng về 'SET autocommit = 0;'

sau khi cài đặt autocommit = 0nếu tập lệnh dừng thực thi (vì một số lý do, như exitlỗi nghiêm trọng hoặc vv ...), thì các thay đổi của bạn sẽ KHÔNG ĐƯỢC TIẾT KIỆM TRONG DB!

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

update_option("something", "value");     

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

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

Trong trường hợp update_optionnày sẽ không được lưu trong DB!

Vì vậy, lời khuyên tốt nhất là đã COMMITđăng ký shutdownchức năng như một tiền tố (trong trường hợp có bất kỳ lối thoát bất ngờ nào xảy ra).

register_shutdown_function( function(){
    $GLOBALS['wpdb']->query( 'COMMIT;' );
} );
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.