대형 XML 파일로 작업 할 때 단일 스레드 구문 분석은 과도한 메모리 사용 또는 과도한 실행 시간을 유발할 수 있습니다. PHP 자체는 "실제"멀티 스레딩 ( PTHREADS 또는 SWOOLE과 같은 확장을 사용하지 않는 한)을 기본적으로 지원하지는 않지만 (예 : Proc_Open 사용) 구문 분석 효율을 향상시켜 큰 XML 파일을 병렬로 처리 할 수 있습니다.
이 기사에서는 XML_PARSE 기능과 Proc_Open을 결합하여 큰 XML 파일의 의사-다일 리드 파싱을 구현하는 방법을 보여줍니다.
XML_PARSE 는 PHP의 기본 구문 분석 기능 중 하나이며 국외 파서의 일부입니다. 이벤트 기반 구문 분석을 지원하며 대형 XML 데이터 스트림을 스트리밍하는 데 이상적입니다. XML_PARSE는 전체 문서를 메모리에로드하는 것보다 더 많은 리소스를 저장합니다.
여러 스레드가 XML_PARSER 객체를 직접 공유하게 할 수는 없지만 다음을 수행 할 수 있습니다.
큰 XML 파일을 차단 (노드로 나눈) ;
Proc_open () 또는 Shell_Exec ()를 사용하여 여러 PHP 자식 프로세스를 시작하십시오.
각 어린이는 자체 XML 블록을 구문 분석합니다.
주요 프로세스는 결과를 수집하여 병합합니다.
다음 구조가있는 큰 XML 파일 /data/huge.xml 이 있다고 가정합니다.
<items>
<item><id>1</id><name>Item 1</name></item>
<item><id>2</id><name>Item 2</name></item>
...
</items>
<?php
$sourceFile = '/data/huge.xml';
$tempDir = '/tmp/xml_chunks/';
$chunkSize = 1000; // 각 어린이 프로세스 구문 분석 1000 개별 <item>
$urls = [];
// 임시 디렉토리가 있는지 확인하십시오
if (!is_dir($tempDir)) {
mkdir($tempDir, 0777, true);
}
// 나뉘다 XML 문서
$handle = fopen($sourceFile, 'r');
$chunkIndex = 0;
$buffer = '';
$itemCount = 0;
while (($line = fgets($handle)) !== false) {
if (strpos($line, '<item>') !== false) {
$itemCount++;
}
$buffer .= $line;
if ($itemCount >= $chunkSize || feof($handle)) {
$chunkFile = $tempDir . "chunk_{$chunkIndex}.xml";
file_put_contents($chunkFile, "<items>\n" . $buffer . "\n</items>");
$urls[] = "http://m66.net/worker.php?file=" . urlencode($chunkFile);
$chunkIndex++;
$buffer = '';
$itemCount = 0;
}
}
fclose($handle);
// 병렬 호출 worker 파서(변경할 수 있습니다 curl_multi_exec 효율성을 향상시킵니다)
foreach ($urls as $url) {
shell_exec("php worker.php '{$url}' > /dev/null &");
}
echo "시작 " . count($urls) . " 개별解析任务。\n";
<?php
if ($argc < 2) {
exit("제발 통과하십시오 XML 문서路径参数\n");
}
$xmlFile = urldecode($argv[1]);
if (!file_exists($xmlFile)) {
exit("문서不存在: $xmlFile\n");
}
$parser = xml_parser_create();
xml_set_element_handler($parser, "startElement", "endElement");
xml_set_character_data_handler($parser, "characterData");
$currentTag = '';
$currentItem = [];
function startElement($parser, $name, $attrs) {
global $currentTag;
$currentTag = strtolower($name);
}
function endElement($parser, $name) {
global $currentTag, $currentItem;
if (strtolower($name) == 'item') {
// 예:将解析结果保存到문서或数据库
file_put_contents('/tmp/parsed_result.txt', json_encode($currentItem) . PHP_EOL, FILE_APPEND);
$currentItem = [];
}
$currentTag = '';
}
function characterData($parser, $data) {
global $currentTag, $currentItem;
if (trim($data)) {
$currentItem[$currentTag] = trim($data);
}
}
$fp = fopen($xmlFile, 'r');
while ($data = fread($fp, 4096)) {
xml_parse($parser, $data, feof($fp)) or
die(sprintf("XML 실수: %s", xml_error_string(xml_get_error_code($parser))));
}
fclose($fp);
xml_parser_free($parser);
echo "분석이 완료되었습니다: $xmlFile\n";
성능 개선 : 멀티 코어 CPU에서 각 어린이 프로세스는 독립적으로 실행되므로 전체 구문 분석 속도를 동시에 속도로 높일 수 있습니다.
메모리 제어 : 각 어린이 프로세스에서 처리 한 데이터의 양은 메모리 파열을 피하기 위해 제어 할 수 있습니다.
보안 : 파일 경로가 프로덕션 환경에서 URL 매개 변수를 직접 전달하지 않도록하고 화이트리스트 검증을 추가해야합니다.
프로세스 관리 : PCNTL_FORK 또는 SWOOLE을 사용하여 Shell_Exec을 대체하여보다 안정적인 아동 프로세스 관리를 달성 할 수 있습니다.
PHP 자체는 동시 처리에 이상적인 언어는 아니지만 XML_PARSE 및 프로세스 제어 기술을 통해 큰 XML 파일을 효율적으로 구문 분석 할 수 있습니다. 이 방법은 로그 처리 및 데이터 가져 오기와 같은 효율성이 필요한 작업 시나리오에 특히 적합합니다.
추가 개선이 필요한 경우, Go/Python과 같은 동시성 친화적 인 언어로 구문 분석 모듈을 다시 작성한 다음 PHP를 통해 예약하는 것이 좋습니다.