检查大型 XML 文件(100k+ 条目)每天更新 PHP 中更改的最快、最有效的方法
Fastest, most efficient way to check for changes in a large XML file (100k+ entries) updated daily with PHP
我正在处理 xml 个包含 50-150k+ 个条目的文件,大小约为 50-100MB+,每天都在变化。所有条目都是唯一的,每个条目有 10-15 个元素(id、title 等)。我目前正在将 xml 文件拉成一个字符串,使用简单的 xml 来解析它,然后遍历每个条目以检查更改。
这是基本代码...
$data = file_get_contents("test.xml");
$data = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $data);
$xml = simplexml_load_string($data);
$item_count = count($xml->entry);
foreach (range(0, $item_count - 1, 1) as $num) {
$id = (string)$xml->entry[$num]->id;
$title = (string)$xml->entry[$num]->title;
// ... etc. ...
我在 VPS 服务器上(1CPU,4GB RAM,40GB)。没有任何其他代码,仅迭代具有 70k 个条目的 xml 文件,大约 80MB 需要 25-30 分钟。性能随着时间的推移而下降,CPU 几分钟后达到 98%(RAM 很好)。前 30K+ 条目需要 10 分钟,而第二个 40K 大约需要 20 分钟。
是否有比简单xml更快、更有效的方法来执行此操作?...或者检查每天更改的大型 XML 文件的更好方法? (即 MySQL import/queries 等)
我看过使用 SAX 解析器的建议,但我确实喜欢 simplexml 提供的对元素的轻松访问。如果我可以流式传输 xml 并节省 RAM,那也是更可取的。
最后一点,如果有帮助的话,我当前检查更改的逻辑如下:
- 创建一个以前导入的条目数组,以条目 ID 作为键,以要检查的条目值字符串作为值
- 遍历 xml 文件并检查当前条目 id/value 对是否存在...
// Check if current entry is in array
if ($previously_imported_entries[$id] === $value1 . " ---- " . $value2 . " --- " . $value3) {
// Item is in array, and values are the same
// No changes
// etc...
} else {
// Item isn't in the array or values have changed
// Import entry either way
}
The XMLReader class 可能会解决问题
“如果您必须处理大量 XML 文档,请使用 XMLReader 来处理它们。不要尝试将整个 XML 文档收集到 PHP 数据结构使用 XMLReader 和 PHP xml2assoc() 函数,您正在重新发明 SimpleXML 轮子。
使用 XMLReader 解析大量 XML 文档时,收集执行操作所需的数据,然后在跳到下一个节点之前执行它。"
XML读者 + 简单XML.
通过 XMLReader 流式传输 xml,然后将每个条目加载到 SimpleXML 中以解析并轻松访问元素。
不可思议!!相同的概念,相同的文件,新的结果时间:6-10 秒。
图片来源:Bartosz Pachołek,
Linkedin Post "Parsing huge XML files with PHP"
*也感谢 Whosebug 用户 Maharramoff 将我指向 XMLReader
这是 Bartosz 的示例代码:
<?php
//include "memcheck.php";
$start = time();
$xml = XMLReader::open('random5.xml');
//go to the first 'object' element
while ($xml->name !== 'object') {
$xml->read();
}
do {
$object = simplexml_load_string($xml->readOuterXml());
$id = (string) $object->id;
$name = (string) $object->name;
$features = [];
foreach($object->features->feature as $feature) {
$features[(string)$feature->id] = (string) $feature->name;
}
$services = [];
foreach($object->services->service as $service) {
$services[(string)$service->id] = (string) $service->service;
}
//here again we have all data of an object
} while ($xml->next('object'));
//var_dump("Mem in MiB: " . round((processPeakMemUsage() / 1024)));
var_dump("Time in seconds: " . (time() - $start));
这对我来说太棒了。
只是将第 7 行和第 4 行中的 'object' 从底部替换为 'entry' 以循环遍历我的 xml 文件中的所有条目标签,并替换了特定元素 names/variables 来匹配我的 xml 文件(即 $title = (string) $object->title 而不是 'name',等等)。
*我注释掉了 memcheck.php 和倒数第二行,因为我没有使用它。
注意:对于我使用的 xml 文件,我首先必须删除所有无效字符以避免 PCDATA 无效字符错误。如果对任何人有帮助,这是我使用的脚本:
$data = file_get_contents('test.xml');
$data = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $data);
file_put_contents('new_file.xml', $data);
Bartosz 的文章对 4GB 和其他 XML 文件进行了多项测试,比较了 SimpleXML、DOM、SAX Expat Parser、XMLReader、等非常有帮助。如果您对他的其他发现感兴趣,请查看。
对于我需要的速度和效率,加上我喜欢 SimpleXML 的简单性,这个解决方案是最好的。
测试看看它是否适合你。
我正在处理 xml 个包含 50-150k+ 个条目的文件,大小约为 50-100MB+,每天都在变化。所有条目都是唯一的,每个条目有 10-15 个元素(id、title 等)。我目前正在将 xml 文件拉成一个字符串,使用简单的 xml 来解析它,然后遍历每个条目以检查更改。
这是基本代码...
$data = file_get_contents("test.xml");
$data = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $data);
$xml = simplexml_load_string($data);
$item_count = count($xml->entry);
foreach (range(0, $item_count - 1, 1) as $num) {
$id = (string)$xml->entry[$num]->id;
$title = (string)$xml->entry[$num]->title;
// ... etc. ...
我在 VPS 服务器上(1CPU,4GB RAM,40GB)。没有任何其他代码,仅迭代具有 70k 个条目的 xml 文件,大约 80MB 需要 25-30 分钟。性能随着时间的推移而下降,CPU 几分钟后达到 98%(RAM 很好)。前 30K+ 条目需要 10 分钟,而第二个 40K 大约需要 20 分钟。
是否有比简单xml更快、更有效的方法来执行此操作?...或者检查每天更改的大型 XML 文件的更好方法? (即 MySQL import/queries 等)
我看过使用 SAX 解析器的建议,但我确实喜欢 simplexml 提供的对元素的轻松访问。如果我可以流式传输 xml 并节省 RAM,那也是更可取的。
最后一点,如果有帮助的话,我当前检查更改的逻辑如下:
- 创建一个以前导入的条目数组,以条目 ID 作为键,以要检查的条目值字符串作为值
- 遍历 xml 文件并检查当前条目 id/value 对是否存在...
// Check if current entry is in array
if ($previously_imported_entries[$id] === $value1 . " ---- " . $value2 . " --- " . $value3) {
// Item is in array, and values are the same
// No changes
// etc...
} else {
// Item isn't in the array or values have changed
// Import entry either way
}
The XMLReader class 可能会解决问题
“如果您必须处理大量 XML 文档,请使用 XMLReader 来处理它们。不要尝试将整个 XML 文档收集到 PHP 数据结构使用 XMLReader 和 PHP xml2assoc() 函数,您正在重新发明 SimpleXML 轮子。 使用 XMLReader 解析大量 XML 文档时,收集执行操作所需的数据,然后在跳到下一个节点之前执行它。"
XML读者 + 简单XML.
通过 XMLReader 流式传输 xml,然后将每个条目加载到 SimpleXML 中以解析并轻松访问元素。
不可思议!!相同的概念,相同的文件,新的结果时间:6-10 秒。
图片来源:Bartosz Pachołek, Linkedin Post "Parsing huge XML files with PHP"
*也感谢 Whosebug 用户 Maharramoff 将我指向 XMLReader
这是 Bartosz 的示例代码:
<?php
//include "memcheck.php";
$start = time();
$xml = XMLReader::open('random5.xml');
//go to the first 'object' element
while ($xml->name !== 'object') {
$xml->read();
}
do {
$object = simplexml_load_string($xml->readOuterXml());
$id = (string) $object->id;
$name = (string) $object->name;
$features = [];
foreach($object->features->feature as $feature) {
$features[(string)$feature->id] = (string) $feature->name;
}
$services = [];
foreach($object->services->service as $service) {
$services[(string)$service->id] = (string) $service->service;
}
//here again we have all data of an object
} while ($xml->next('object'));
//var_dump("Mem in MiB: " . round((processPeakMemUsage() / 1024)));
var_dump("Time in seconds: " . (time() - $start));
这对我来说太棒了。
只是将第 7 行和第 4 行中的 'object' 从底部替换为 'entry' 以循环遍历我的 xml 文件中的所有条目标签,并替换了特定元素 names/variables 来匹配我的 xml 文件(即 $title = (string) $object->title 而不是 'name',等等)。
*我注释掉了 memcheck.php 和倒数第二行,因为我没有使用它。
注意:对于我使用的 xml 文件,我首先必须删除所有无效字符以避免 PCDATA 无效字符错误。如果对任何人有帮助,这是我使用的脚本:
$data = file_get_contents('test.xml');
$data = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $data);
file_put_contents('new_file.xml', $data);
Bartosz 的文章对 4GB 和其他 XML 文件进行了多项测试,比较了 SimpleXML、DOM、SAX Expat Parser、XMLReader、等非常有帮助。如果您对他的其他发现感兴趣,请查看。
对于我需要的速度和效率,加上我喜欢 SimpleXML 的简单性,这个解决方案是最好的。
测试看看它是否适合你。