批量访问wp_insert_post和add_post_meta的更快方法


16

我有一个要插入的csv文件,包含约1,500行和97列。完全导入大约需要2-3个小时,如果可以的话,我想改善这一点。目前,对于每一行,我正在为$ 97关联的每一行执行$ post_id = wp_insert_post,然后执行add_post_meta。这是非常低效的...

是否有更好的方法可以使post_id保持post及其post_meta值之间的关系?

现在,我正在使用wamp的本地计算机上尝试此操作,但将其在VPS上运行


除了下面的WP技巧,还请按照此答案查看在MySQL中使用InnoDB并批量提交事务。
webaware

Answers:


21

以前,我在使用自定义CSV导入时遇到了类似的问题,但是最后我通过对批量插入使用一些自定义SQL来结束。但那时我还没有看到这个答案:

为批量操作优化帖子的插入和删除?

用于wp_defer_term_counting()启用或禁用术语计数。

另外,如果您查看WordPress导入程序插件的源代码,则将在批量导入之前看到以下功能:

wp_defer_term_counting( true );
wp_defer_comment_counting( true );

然后在批量插入之后:

wp_defer_term_counting( false );
wp_defer_comment_counting( false );

所以这可能是要尝试的东西;-)

将帖子作为草稿而不是publish导入也可以加快速度,因为跳过了为每个帖子查找唯一的条目的缓慢过程。例如,可以稍后以较小的步骤发布它们,但是请注意,这种方法将需要以某种方式标记导入的帖子,因此我们不只是稍后再发布任何草稿!这将需要仔细计划,并且很可能需要一些自定义编码。

例如,如果post_name要导入很多相似的帖子标题(相同),则wp_unique_post_slug()可能会由于循环查询迭代以查找可用的条目而变慢。这可能会生成大量的数据库查询。

从WordPress 5.1开始,该pre_wp_unique_post_slug过滤器可用于避免该条的循环迭代。参见核心票证#21112。这是一个例子:

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

如果有人尝试$override_slug = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix"使用$suffixas作为示例$post_id,那么我们会注意到,这$post_id总是0如预期的那样适用于新帖子。尽管有多种方法可以在PHP中生成唯一数字,例如uniqid( '', true )。但是请小心使用此过滤器,以确保您有独特的子弹。我们可以例如稍后运行组计数查询post_name来确保。

另一种选择是使用WP-CLI以避免超时。请参阅例如我发布的关于使用.csv文件创建20,000个帖子或页面的答案

然后,我们可以import.php使用WP-CLI命令运行自定义PHP导入脚本:

wp eval-file import.php

还应避免导入大量的分层帖子类型,因为当前的wp-admin UI不能很好地处理它。参见例如自定义帖子类型-帖子列表-死亡白屏

这是@otto的妙招:

批量插入之前,请autocommit明确禁用该模式:

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

批量插入后,运行:

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

我也认为做一些整理工作是个好主意,例如:

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

我没有在MyISAM上测试过,但这应该在InnoDB上有效

正如@kovshenin 所提到的,该技巧不适用于MyISAM


6
除此之外,还可以使用查询功能在插入之前关闭自动提交,然后在插入之后手动提交。进行批量插入时,这可以大大加快数据库级别的操作。只需SET autocommit=0;在插入之前发送一个,然后再发送一个COMMIT;
奥托(Otto)

有趣,谢谢!我到家时必须测试一下。
Corey Rowell

@Otto,感谢您的宝贵建议。因此我们可以$wpdb->query('SET autocommit = 0;');在插入之前进行操作,但是$wpdb->query('START TRANSACTION;');在这种情况下可以跳过吗?我将查看MySQL手册以进一步了解它;-)欢呼。
birgire

1
好点马克。如果这些只是插入而不是更新,wp_suspend_cache_addition( true )则应帮助不要将内容放入对象缓存中。@birgire还提到他们没有使用MyISAM对此进行测试-不用担心,存储引擎不支持事务,因此设置自动提交或开始事务将产生零影响。
kovshenin '16

1
大提示@Otto。我的查询之前花费了38秒,现在花费了1秒。
安纳布尔纳(Annapurna)

5

您将需要插入帖子以获取您的ID,但是$wpdb->postmeta表格的结构非常简单。您可能使用直接的INSERT INTO语句,例如来自MySQL文档的语句INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);

就你而言...

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

这不会处理任何编码,序列化,转义,错误检查,重复或其他任何操作,但是我希望它会更快(尽管我没有尝试过)。

未经全面测试,我不会在生产现场执行此操作,如果只需要执行一次或两次,我将使用核心功能并在导入内容时花很长时间。


认为我会吃一顿长饭,而不是将原始数据插入表中,并且没有必要重写Wordpress已经完成的工作。
科里·罗威尔

1
这就是mysql注入的过程,所以请不要使用它。
OneOfOne 2015年

一切都是硬编码的@OneOfOne。没有用户提供的输入,注入就不会发生-根据定义是不会发生的。这就是“注射”的性质。OP正在使用他控制下的代码从.csv文件中导入数据,该文件由他控制。第三方注入任何东西都是没有机会的。请注意上下文。
s_ha_dum 2015年

从我+1,我需要添加20个海关字段值,这比“ add_post_meta”要快得多
Zorox

1
您不能期望OP在导入之前彻底检查CSV文件,因此您应该将其视为用户输入,至少应将其视为->prepare()SQL语句。在您的方案中,如果CSV中的ID列包含类似的内容,会发生什么1, 'foo', 'bar'); DROP TABLE wp_users; --?可能是坏事。
kovshenin '16

5

我必须添加以下内容:

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

请记住,这将跳过do_all_pings,它会处理pingback,附件,引用和其他ping(链接:https : //developer.wordpress.org/reference/functions/do_all_pings/)。通过查看代码,我的理解是,删除此remove_action行后,仍将处理未决的pingbacks / trackbacks /机箱,但是我不确定。

更新:我还添加了

    define( 'WP_IMPORTING', true );

除此之外,我正在使用:

    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

关于的重要说明 'SET autocommit = 0;'

设置autocommit = 0脚本是否停止执行(由于某种原因,例如exit致命错误等)后,您所做的更改就不会保存在数据库中!

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

update_option("something", "value");     

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

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

在这种情况下,update_option将不会保存在数据库中!

因此,最好的建议是先COMMIT注册shutdown功能(以防万一发生意外退出)。

register_shutdown_function( function(){
    $GLOBALS['wpdb']->query( 'COMMIT;' );
} );
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.