在面临内存耗尽和查询耗时的问题后,我尝试了另一种批量导入方法。这个想法并不新鲜,但为了展示一种可能的方式,我想分享我的解决方案。请记住,我不是PHP/MySQL方面的专业人员,所以也许有一种更有效或更好的方法可以以不同的方式或更好的性能完成此任务
由于@kuchenundkakao向我提出了一些新想法,使用临时表导入整个XML文件,并在第二次查询中处理其中的数据,因此使用$wpdb实现了大容量插入的主要功能。总之,bulk\\u insert函数没有检查或更新任何现有数据,表得到truncated
处理完成后每24小时一次。
它是在PHP类中使用的,所以如果您试图复制和粘贴,请注意-检查语法!
// Table, including your prefix!
$table = $wpdb->prefix.\'table_name\';
$rows = array(
array(\'price\' => \'12.00\',\'vat\' => \'7.7\'),
array(\'price\' => \'230.00\', \'vat\' => \'7.7\')
);
我的内容
bulk_insert
作用
function bulk_insert($table, $rows) {
global $wpdb;
/* Extract column list from first row of data! */
$columns = array_keys($rows[0]);
asort($columns);
$columnList = \'`\' . implode(\'`, `\', $columns) . \'`\';
/* Start building SQL, initialise data and placeholder arrays */
$sql = "INSERT INTO `$table` ($columnList) VALUES\\n";
$placeholders = array();
$data = array();
/* Build placeholders for each row, and add values to data array */
foreach ($rows as $row) {
ksort($row);
$rowPlaceholders = array();
foreach ($row as $key => $value) {
$data[] = $value;
/* differentiate values and set placeholders */
if (is_numeric($value)) {
$rowPlaceholders[] = is_float($value) ? \'%f\' : \'%d\';
} else {
$rowPlaceholders[] = \'%s\';
}
}
$placeholders[] = \'(\' . implode(\', \', $rowPlaceholders) . \')\';
}
/* Stitching all rows together */
$sql .= implode(",\\n", $placeholders);
// Run the query. Returning number of affected rows for this chunk
return $wpdb->query($wpdb->prepare($sql, $data));
}
对XML数据进行分块(Chunking XML Data)您可以将多少数据推送到
bulk_insert()
而且很大程度上取决于您的主机/服务器,因此我通过在发送到之前限制数据量,使其变得灵活且易于调整
bulk_insert()
.
What is this function doing?它使用XMLReader和SimpleXMLElement逐行解析XML文档,而不是解析整个文档(大部分以耗尽内存结束)。在给定金额后,由$limit
中的XML元素$array[]
到达时,函数将此XML文件块作为数组发送到bulk_insert()
-> 将整个数组标记到数据库中。
我正在将文件名传递给load_price_xml2mysql
出于灵活性目的,如myfile.xml
function load_price_xml2mysql($filename) {
global $wpdb;
/* get xml file */
$file = WP_UPLOAD_DIR.$filename;
$xml = new XMLReader();
$xml->open($file);
/* set your limit here, define the limit in your plugin or just add $limit = 1000 */
$limit = MYSQL_PRICE_INSERTLIMIT;
$array = array();
$table = $wpdb->prefix.\'temp_api_price\';
/* counting entries, so we have to set zero before while */
$i = 0;
while($xml->read() && $xml->name != \'item\'){ ; }
while ( $xml->name === \'item\' ) {
++$i;
$element = new SimpleXMLElement( $xml->readOuterXML() );
$array[] = array(
\'sku\' => intval($element->SKU),
\'buying\' => floatval($element->price->BUYING),
\'buying_ex_vat\' => floatval($element->price->BUYING_EX),
\'vat\' => floatval($element->price->VAT),
\'selling\' => floatval($element->price->SELLING)
);
/* start chunking the while routine by $limit
passing the array to bulk_insert() if the limits gets reached. */
if (count($array) == $limit) {
$this->bulk_insert($table, $array);
unset($array);
}
unset($element);
$xml->next(\'item\');
}
/* don\'t miss the last chunk, if it didn\'t reach the given limit, send it now! */
$this->bulk_insert($table, $array);
/* return the total amount of entries */
return $i;
}
结论你必须找到你的工作
$limit
, 仅供参考:
product_item.xml
- 140MB
- 9.1 Million rows in document
- 142.000 items
$limit = 5000;
$runtime = 43.93240232324 [average]
运行时=包括解析(&N);正在插入MySQL数据库。
每排6500行是最大工作限制,所以我决定多留一点空间,然后去$limit = 5000
并且仍然达到相同的平均运行时间。可以肯定的是,大部分时间都需要解析XML文档,也许我会对每个函数分别进行基准测试和跟踪微时间。
非常感谢@phatskat和@kuchenandkakao把我推向了正确的方向。