PHP 개발에서 Pdostatement :: FetchObject는 특히 객체 지향 데이터 구조를 처리 할 때 숫자를 얻는 매우 일반적인 방법입니다. 그러나 쿼리 결과가 매우 커지면 (수십만 개의 레코드와 같은) 잘못된 사용으로 인해 심각한 문제가 발생하고 PHP 스크립트가 직접 충돌 할 수 있습니다.
이 기사는이 문제의 원인을 자세히 분석하고 실용적인 솔루션을 제공합니다.
FetchObject를 사용하면 PDO는 각 데이터 행에 새 개체를 인스턴스화합니다. 기본적으로 이러한 객체는 PHP 스크립트 수명주기 동안 제 시간에 해제되지 않습니다. 올바르게 처리하지 않으면 메모리가 계속 커지고 결국 다 떨어집니다.
샘플 코드 :
<?php
$pdo = new PDO('mysql:host=localhost;dbname=testdb', 'username', 'password');
$stmt = $pdo->query('SELECT * FROM large_table');
while ($row = $stmt->fetchObject()) {
// 여기서 간단한 처리를한다고 가정 해 봅시다
processRow($row);
}
function processRow($row) {
// 처리 로직
// 예를 들어, 특정으로 보내십시오API
file_get_contents('https://api.gitbox.net/handle', false, stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\n",
'content' => json_encode($row),
]
]));
}
?>
위의 코드는 큰 테이블을 처리 할 때 막대한 메모리 소비를 유발합니다. 각 $ 행 객체는 프로세스 로우 직후에 파괴되지 않기 때문에 메모리 축적이 발생합니다.
FetchObject는 객체 참조를 반환합니다.
객체가 루프에서 수동으로 파괴되지 않거나 객체가 외부 참조 (예 : 글로벌 어레이로 수집 됨)에서 고정 된 경우 쓰레기 수집기는 메모리를 제 시간에 재활용하지 않습니다.
PHP의 쓰레기 수집 (GC)은 원형 참조를 처리 할 수 있지만 주파수는 제한되어 있으며 대규모 객체를 즉시 해제하기 위해 의존 할 수 없습니다.
물체를 강제로하지 않으면 대신 Fetch (pdo :: fetch_assoc)를 사용할 수 있습니다. 배열은 훨씬 작은 메모리를 차지하며 제어하기가 더 쉽습니다.
<?php
$stmt = $pdo->query('SELECT * FROM large_table');
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
processRow((object)$row); // 필요한 경우 일시적으로 객체로 변환 할 수 있습니다.
}
?>
이것은 메모리 압력을 효과적으로 줄일 수 있습니다.
객체를 사용해야하는 경우 각 루프 후에 객체 참조를 수동으로 해제 할 수 있습니다.
<?php
$stmt = $pdo->query('SELECT * FROM large_table');
while ($row = $stmt->fetchObject()) {
processRow($row);
unset($row); // 지금 파괴하십시오
}
?>
Unset ($ 행)는 PHP 엔진이 다음 루프 라운드 동안 메모리를 낮게 유지하도록 객체 참조를 방출하도록 강제합니다.
초대형 데이터 테이블의 경우 한 번에 과도한 데이터로드를 피하기 위해 배치 (예 : 쿼리 당 1,000 개의 항목)로 쿼리 할 수 있습니다.
<?php
$batchSize = 1000;
$offset = 0;
do {
$stmt = $pdo->prepare('SELECT * FROM large_table LIMIT :limit OFFSET :offset');
$stmt->bindValue(':limit', $batchSize, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$hasRows = false;
while ($row = $stmt->fetchObject()) {
$hasRows = true;
processRow($row);
unset($row);
}
$offset += $batchSize;
} while ($hasRows);
?>
단일 배치의 쿼리 볼륨을 제어함으로써 메모리 서지를 완전히 피할 수 있습니다.
데이터베이스 드라이버가이를 지원하면 생성기와 함께 우아하고 효율적인 코드를 모두 작성할 수 있습니다.
<?php
function fetchRows(PDO $pdo) {
$stmt = $pdo->query('SELECT * FROM large_table');
while ($row = $stmt->fetchObject()) {
yield $row;
}
}
foreach (fetchRows($pdo) as $row) {
processRow($row);
unset($row); // 선택 과목
}
?>
생성기를 통해 한 번에 하나의 레코드 만 처리 할 수 있으므로 메모리 사용량이 크게 줄어 듭니다.
실제 프로젝트에서 FetchObject를 사용한 후에도 지속적으로 메모리 상승 문제가 발생하면 놀라지 마십시오. 이것은 일반적인 함정입니다. 응답 전략을 요약하겠습니다.
배열을 사용할 수있는 경우 배열을 사용하십시오.
객체가 필요한 경우 설정하지 않습니다 .
많은 양의 데이터가 필요한 경우 배치로 쿼리 해야합니다.
고급 응용 프로그램 시나리오는 생성기 사용을 고려할 수 있습니다.
이 팁을 올바르게 사용하면 모든 규모의 데이터 처리 요구를 침착하게 처리 할 수 있습니다!