zor*_*anc 4 php mysql csv wordpress performance
我的脚本有一个问题,该脚本从 csv 文件导入条目并将它们作为 WordPress 自定义帖子插入(每行都是一个帖子)...最初,我在自己的类中设置了导入功能,但这几乎不起作用...根据我收集的信息,问题是全局变量没有被缓存,每次我调用实例时都会消耗更多内存,直到进程耗尽内存并崩溃...所以我删除了该类并设置如下面的代码所述启动导入功能。
通过这个设置,我达到了可以正常处理多达 17k 个帖子的程度,但是如果我尝试导入比这更多的帖子,它就会在没有任何错误的情况下退出(我的 php 错误日志或中没有报告任何错误) WordPress 调试.日志文件)
该脚本成功插入了 17k 个帖子,打印出回显信息,直到它过早地停止在“剩余的 XXX 项”处,并且完成加载页面,此时不再输出任何内容......它永远不会进入最终语句echo "Done!";。 。
这会发生在本地主机开发环境和托管开发服务器上。我密切关注内存使用情况,在我的本地主机上它从未超过 60%(从 ~50% 开始),并且我没有看到逐步内存爬升表明内存泄漏...
我还尝试使用 ini_set('memory_limit', '64M'); 和 set_time_limit(0);
根据我读到的有关此问题的其他一些类似问题,
我可以对下面的代码进行什么样的优化/改进才能使该脚本在这种规模上工作?
或者可能跳过 wordpress 内置功能并使用 LOAD DATA INFILE 处理所有内容,如 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();
}
}
Run Code Online (Sandbox Code Playgroud)
import.php 包含在插件类之外的 WordPress 插件中
即,这是有关如何在导入页面上获取脚本的相关信息:
define( 'MY_PLUGIN_ROOT' , dirname(__FILE__) );
include_once( MY_PLUGIN_ROOT . 'import.php');
class my_plugin () {
function __construct() {
add_action( 'init', array( &$this, 'admin_menu_init' ) );
}
function admin_menu_init() {
if(is_admin()) {
//Add the necessary pages for the plugin
add_action('admin_menu', array(&$this, 'add_menu_items'));
}
}
function add_menu_items() {
add_submenu_page( 'edit.php?post_type=songs', 'Import Songs', 'Import Songs', 'manage_options', 'import-songs', 'import_page' );
}
}
Run Code Online (Sandbox Code Playgroud)
任何想法、意见或建议将不胜感激。
因此,经过长时间的分析会话,与 XDEBUG、phpmyadmin 和朋友一起,我终于缩小了瓶颈的范围:mysql 查询
我启用了
define('SAVEQUERIES', true);
更详细地检查查询并将查询数组输出到我的 debug.log 文件
global $wpdb;
error_log( print_r( $wpdb->queries, true ) );
Run Code Online (Sandbox Code Playgroud)
检查对数据库的每个 INSERT 调用
wp_insert_post()
[ 0 ] = >插入wp_posts( ,,,,,,,,,,,,,,,,,,,,,,,,,,,,, )值( ... )post_authorpost_datepost_date_gmtpost_contentpost_content_filteredpost_titlepost_excerptpost_statuspost_typecomment_statusping_statuspost_passwordpost_nameto_pingpingedpost_modifiedpost_modified_gmtpost_parentmenu_orderguid
[1] => 0.10682702064514
update_post_meta()
[0] => 插入wp_postmeta( post_id, meta_key, meta_value) 值 (...)
[1] => 0.10227680206299
我发现在我的本地主机服务器上,对数据库的每一次调用平均花费约 0.1 秒
但是,由于我要更新每个条目 6 个自定义字段,因此结果是
6 cf 插入调用/条目 * ~0.1s/cf 插入调用 *20 000 总条目 * 1 分钟/60 秒 = 200 分钟
仅处理 2 万条帖子的自定义字段就需要大约 2.5 小时
现在,由于所有自定义字段都插入到同一个表 wp_post_meta 中,因此我希望将所有 update_post_meta 调用合并到一个调用中,从而显着提高此导入脚本的执行时间方面的性能。
我查看了 wp 核心中的 update_post_meta 函数,发现没有本地选项来传递数组而不是单个 cf 键和值,因此我浏览了相关代码来精确定位 SQL INSERT 行,并查看如何执行某些操作的行:
`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 ) ...`
Run Code Online (Sandbox Code Playgroud)
对于我的例子中的所有 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;
}
Run Code Online (Sandbox Code Playgroud)
还有中提琴!在尝试处理多个自定义字段并迭代大量帖子时,我最终没有对每个自定义帖子进行 7 次 INSERT 调用,而是进行了 2 次数据库调用,从而产生了巨大的差异 - 在我现在的情况下,20k 条目在 15 天内处理完成-20 分钟(相比之下,在大约 17k 个帖子和几个小时的处理时间后我会遇到退出的情况)...
所以我从整个经历中学到的关键是
请注意您的数据库调用 - 它们可以快速累加!
| 归档时间: |
|
| 查看次数: |
5316 次 |
| 最近记录: |