Php 将大量自定义帖子从csv文件导入wordpress

Php 将大量自定义帖子从csv文件导入wordpress,php,mysql,wordpress,performance,csv,Php,Mysql,Wordpress,Performance,Csv,我的脚本从csv文件中导入条目并将其作为wordpress自定义帖子插入(每行都是一篇帖子)时出现问题。。。 最初,我在自己的类中设置了导入功能,但这几乎不起作用…从我收集的信息来看,问题是全局变量没有被缓存,每次调用实例时都会消耗更多的内存,直到进程耗尽内存并崩溃。。。因此,我删除了这个类,并按照下面的代码设置了导入功能 有了这个设置,我就可以处理多达17k个帖子,但如果我尝试导入更多的帖子,它就会自动退出,不会出现任何错误(我的php错误日志或wordpress debug.log文件中没有

我的脚本从csv文件中导入条目并将其作为wordpress自定义帖子插入(每行都是一篇帖子)时出现问题。。。 最初,我在自己的类中设置了导入功能,但这几乎不起作用…从我收集的信息来看,问题是全局变量没有被缓存,每次调用实例时都会消耗更多的内存,直到进程耗尽内存并崩溃。。。因此,我删除了这个类,并按照下面的代码设置了导入功能

有了这个设置,我就可以处理多达17k个帖子,但如果我尝试导入更多的帖子,它就会自动退出,不会出现任何错误(我的php错误日志或wordpress debug.log文件中没有报告任何错误)

脚本成功地插入了17k个打印回显信息的帖子,直到它在“剩余XXX个项目”处提前停止,并在该点完成加载页面,不再输出任何内容。。。它永远不会进入最后的
echo“Done!”语句

这在本地主机开发环境和托管开发服务器上都会发生。 我一直在关注内存使用情况,在我的本地主机上,内存使用率从未超过60%(开始时约为50%),而且我没有看到内存逐步上升表示内存泄漏

我还尝试使用ini_集(“内存限制”,“64M”);并设置时间限制(0)

从我读到的其他类似问题来看

  • 对于SQL 20k,条目应该不是什么大问题
  • 如果服务器不可用,wordpress也应该能够处理这个问题 足够强大
我可以对下面的代码做什么样的优化/改进,以使这个脚本在这种规模上工作

或者跳过wordpress的内置功能,使用fancypants提到的加载数据填充处理所有内容

我更愿意通过提供的wordpress功能处理数据

csv文件约为1mb

守则:


这些函数位于它们自己的文件import.php中

function fileupload_process() {
  ini_set('memory_limit', '64M');
  set_time_limit(0);
  $uploadfiles = $_FILES['uploadfiles'];
  if (is_array($uploadfiles)) {
    foreach ($uploadfiles['name'] as $key => $value) {
      // look only for uploaded files
      if ($uploadfiles['error'][$key] == 0) {
        $filetmp = $uploadfiles['tmp_name'][$key];
        if (($handle = fopen($filetmp, "r")) !== FALSE) {
          $flag = true;
          $songs = explode("\n",file_get_contents($filetmp));
          $count = count( $songs );
          unset($songs);
          echo "Total item count: " . $count . "<BR />";
          // typical entry: If You Have To Ask,Red Hot Chili Peppers,0:03:37, Rock & Alternative,1991,on
          // using a generous 1000 length - will lowering this actually impact performance in terms of memory allocation?
          while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
            // Skip the first entry in the csv containing colmn info
            if($flag) {
                      $flag = false; 
              echo "<BR />"; 
              $count--; 
              continue; 
            }
            // insert the current post and relevant info into the database
            $currently_processed = process_custom_post($data, $count);
            $count--;
          }
          echo "Done!";
          fclose($handle);
        }
        unlink($filetmp); // delete the temp csv file
      }
    }
  }
} // END: file_upload_process()
function process_custom_post($song, $count) {
  $track =  (array_key_exists(0, $song) && $song[0] != "" ?  $song[0] : 'N/A');
  $artist = (array_key_exists(1, $song) && $song[1] != ""  ?  $song[1] : 'N/A');
  $length = (array_key_exists(2, $song) && $song[2] != ""  ?  $song[2] : 'N/A');
  $genre = (array_key_exists(3, $song) && $song[3] != ""  ?  $song[3] : 'N/A');
  $year = (array_key_exists(4, $song) && $song[4] != ""  ?  $song[4] : 'N/A');
  $month = (array_key_exists(5, $song) && $song[5] != ""  ?  $song[5] : 'N/A');
  $playlist = (array_key_exists(6, $song) && $song[6] != ""  ?  $song[6] : '');
  $custom_post = array();
  $custom_post['post_type'] = 'songs';
  $custom_post['post_status'] = 'publish';
  $custom_post['post_title'] = $track;
  echo "Importing " . $artist  . " - " . $track . " <i> (" . $count ." items remaining)...</i><BR />";
  $post_id = wp_insert_post( $custom_post );
  $updated = update_post_meta($post_id, 'artist_name', $artist);
  $updated = update_post_meta($post_id, 'song_length', $length);
  $updated = update_post_meta($post_id, 'song_genre', $genre);
  $updated = update_post_meta($post_id, 'song_year', $year);
  $updated = update_post_meta($post_id, 'song_month', $month);
  $updated = update_post_meta($post_id, 'sample_playlist', $playlist);
  return true;
} // END: process_custom_post()
function import_page () {
//HTML for the import page + the file upload form
  if (isset($_POST['uploadfile'])) {
    fileupload_process();
       }
}


任何想法、评论或建议都将不胜感激。

因此,在与XDEBUG、phpmyadmin和朋友们进行了长时间的评测之后,我终于缩小了瓶颈:mysql查询

我启用

define('savequerys',true)

为了更详细地检查查询,我将查询数组输出到debug.log文件

global $wpdb;
error_log( print_r( $wpdb->queries, true ) );
检查对数据库的每个INSERT调用后

  • wp_插入_后()

    [0]=>插入
    wp\u帖子
    post\u author
    post\u date
    post\u gmt
    post\u内容
    post\u内容
    post\u标题
    post\u摘录
    post\u状态
    评论状态
    ping状态
    post\u状态
    ng
    ping
    post\u modified
    post\u gmt
    post\u parent
    菜单顺序
    guid
    )值(…)

    [1] =>0.10682702064514

  • 更新\u post\u meta()

    [0]=>插入
    wp\u postmeta
    post\u id
    meta\u key
    meta\u value
    )值(…)

    [1] =>0.10227680206299

  • 我发现,在我的本地主机服务器上,对数据库的每一次调用平均花费约0.1秒

    但是,由于我要为每个条目更新6个自定义字段,结果是

    6 cf插入呼叫/输入*0.1s/cf插入呼叫*20000个总输入*1分钟/60s=200分钟

    约2.5小时,仅处理20000个职位的自定义字段

    现在,由于所有自定义字段都插入到同一个表wp_post_meta中,因此我希望将所有update_post_meta调用合并到一个调用中,从而大大提高此导入脚本的执行时间性能

    我查看了wp core中的update_post_meta函数,发现没有传递数组的本机选项,而不是传递单个cf键和值,因此我查看了相关代码,以确定SQL插入行,并查看了如何执行以下操作:

    `INSERT INTO `wp_postmeta` (`post_id`,`meta_key`,`meta_value`) 
      VALUES ( $post_id,  'artist_name' , $artist) 
             ( $post_id,  'song_length' , $length )
             ( $post_id,  'song_genre' , $genre ) ...` 
    
    在我的例子中,所有6个自定义字段都在一个
    $wpdb->query();
    中滚动

    调整我的process_custom_post()函数以反映这一点,我最终得到:

    function process_custom_post($song) {
      global $wpdb;
    
      // Prepare and insert the custom post
      $track =  (array_key_exists(0, $song) && $song[0] != "" ?  $song[0] : 'N/A');
      $custom_post = array();
      $custom_post['post_type'] = 'songs';
      $custom_post['post_status'] = 'publish';
      $custom_post['post_title'] = $track;
      $post_id = wp_insert_post( $custom_post );
    
      // Prepare and insert the custom post meta
      $meta_keys = array();
      $meta_keys['artist_name'] = (array_key_exists(1, $song) && $song[1] != ""  ?  $song[1] : 'N/A');
      $meta_keys['song_length'] = (array_key_exists(2, $song) && $song[2] != ""  ?  $song[2] : 'N/A');
      $meta_keys['song_genre'] = (array_key_exists(3, $song) && $song[3] != ""  ?  $song[3] : 'N/A');
      $meta_keys['song_year'] = (array_key_exists(4, $song) && $song[4] != ""  ?  $song[4] : 'N/A');
      $meta_keys['song_month'] = (array_key_exists(5, $song) && $song[5] != ""  ?  $song[5] : 'N/A');
      $meta_keys['sample_playlist'] = (array_key_exists(6, $song) && $song[6] != ""  ?  $song[6] : '');
      $custom_fields = array();
      $place_holders = array();
      $query_string = "INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value) VALUES ";
      foreach($meta_keys as $key => $value) {
         array_push($custom_fields, $post_id, $key, $value);
         $place_holders[] = "('%d', '%s', '%s')";
      }
      $query_string .= implode(', ', $place_holders);
      $wpdb->query( $wpdb->prepare("$query_string ", $custom_fields));
      return true;
    }
    
    还有viola!每个自定义帖子有7个对数据库的INSERT调用,当我尝试处理多个自定义字段,同时迭代大量帖子时,我最终得到了2个对数据库的gynormous调用——在我的例子中,20k条目的处理在15-20分钟内完成(而在这种情况下,我会在大约17k个帖子和几个小时的处理时间后辍学)

    所以我从这次经历中学到的关键是


    注意你的数据库调用-它们可以很快累加起来!

    我能看到的唯一建议是在while循环中获取你的计数,而不是读取整个文件,然后分解它来获取计数。此外,似乎你只是使用计数器来输出它,以便于人的可读性。每当我进行大的导入时,我只会在最后回显,可能是一个偶数e行,将20k行输出到屏幕可能是另一个问题。我取出了所有的echo语句,除了最后的echo'Done!';,我省略了计数文件爆炸,并在循环中实现了它…它仍然没有导入所有的帖子,在~17K处停止…但是,我现在收到了500个内部服务器错误,尽管我的provider的服务器日志文件完全没有任何错误??我将尝试在localhost上执行此操作,因为我有更多的可用监督,希望我能找到导致此问题的原因…不走运,localhost会像以前一样无错误退出…谢谢
    function process_custom_post($song) {
      global $wpdb;
    
      // Prepare and insert the custom post
      $track =  (array_key_exists(0, $song) && $song[0] != "" ?  $song[0] : 'N/A');
      $custom_post = array();
      $custom_post['post_type'] = 'songs';
      $custom_post['post_status'] = 'publish';
      $custom_post['post_title'] = $track;
      $post_id = wp_insert_post( $custom_post );
    
      // Prepare and insert the custom post meta
      $meta_keys = array();
      $meta_keys['artist_name'] = (array_key_exists(1, $song) && $song[1] != ""  ?  $song[1] : 'N/A');
      $meta_keys['song_length'] = (array_key_exists(2, $song) && $song[2] != ""  ?  $song[2] : 'N/A');
      $meta_keys['song_genre'] = (array_key_exists(3, $song) && $song[3] != ""  ?  $song[3] : 'N/A');
      $meta_keys['song_year'] = (array_key_exists(4, $song) && $song[4] != ""  ?  $song[4] : 'N/A');
      $meta_keys['song_month'] = (array_key_exists(5, $song) && $song[5] != ""  ?  $song[5] : 'N/A');
      $meta_keys['sample_playlist'] = (array_key_exists(6, $song) && $song[6] != ""  ?  $song[6] : '');
      $custom_fields = array();
      $place_holders = array();
      $query_string = "INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value) VALUES ";
      foreach($meta_keys as $key => $value) {
         array_push($custom_fields, $post_id, $key, $value);
         $place_holders[] = "('%d', '%s', '%s')";
      }
      $query_string .= implode(', ', $place_holders);
      $wpdb->query( $wpdb->prepare("$query_string ", $custom_fields));
      return true;
    }