MySQL Native Driver 메모리 관리

소개

MySQL 네이티브 드라이버는 MySQL 클라이언트 라이브러리와 다른 메모리를 관리합니다. 라이브러리는 메모리가 할당 및 해제되는 방식, MySQL에서 결과를 읽는 동안 메모리가 청크로 할당되는 방식, 디버그 및 개발 옵션이 존재하는 방식, MySQL에서 읽은 결과가 PHP 사용자 변수에 연결되는 방식이 다릅니다.

다음 참고 사항은 C 코드 수준에서 MySQL 기본 드라이버를 이해하는 데 관심이 있는 사용자를 위한 소개 및 요약을 위한 것입니다.

사용된 메모리 관리 함수

모든 메모리 할당 및 할당 해제는 PHP 메모리 관리 함수를 사용하여 수행됩니다. 따라서 mysqlnd의 메모리 소비는 memory_get_usage()와 같은 PHP API 호출을 사용하여 추적할 수 있습니다. PHP 메모리 관리를 사용하여 메모리가 할당 및 해제되기 때문에 변경 사항이 운영 체제 수준에서 즉시 표시되지 않을 수 있습니다. PHP 메모리 관리는 시스템에 대한 메모리 해제를 지연시킬 수 있는 프록시 역할을 합니다. 이 때문에 MySQL Native Driver와 MySQL Client Library의 메모리 사용량을 비교하는 것은 어렵다. MySQL 클라이언트 라이브러리는 운영 체제 메모리 관리 호출을 직접 사용하므로 운영 체제 수준에서 즉시 효과를 관찰할 수 있습니다.

PHP가 적용하는 모든 메모리 제한은 MySQL 네이티브 드라이버에도 영향을 미칩니다. 이로 인해 PHP에서 사용할 수 있는 나머지 메모리 크기를 초과하는 큰 결과 집합을 가져올 때 메모리 부족 오류가 발생할 수 있습니다. MySQL 클라이언트 라이브러리는 PHP 메모리 관리 함수를 사용하지 않기 때문에 PHP 메모리 제한 설정을 준수하지 않습니다. MySQL 클라이언트 라이브러리를 사용하는 경우 배포 모델에 따라 PHP 프로세스의 메모리 공간이 PHP 메모리 제한을 초과하여 커질 수 있습니다. 그러나 PHP 스크립트는 결과 집합을 유지하기 위해 할당된 메모리의 일부가 PHP 엔진의 제어 범위를 벗어나기 때문에 더 큰 결과 집합을 처리할 수도 있습니다.

PHP 메모리 관리 함수는 경량 래퍼를 통해 MySQL 네이티브 드라이버에 의해 호출됩니다. 무엇보다도 래퍼를 사용하면 디버깅이 더 쉬워집니다.

결과 집합 처리

다양한 MySQL 서버와 다양한 클라이언트 API는 버퍼링된 결과 세트와 버퍼링되지 않은 결과 세트를 구별합니다. 클라이언트가 결과를 반복할 때 버퍼링되지 않은 결과 세트는 MySQL에서 클라이언트로 행별로 전송됩니다. 버퍼링된 결과는 클라이언트에 전달하기 전에 클라이언트 라이브러리에서 전체를 가져옵니다.

MySQL 네이티브 드라이버는 MySQL 서버와의 네트워크 통신을 위해 PHP 스트림을 사용하고 있습니다. MySQL이 보낸 결과는 PHP Streams 네트워크 버퍼에서 mysqlnd의 결과 버퍼로 가져옵니다. 결과 버퍼는 zval로 구성됩니다. 두 번째 단계에서 결과는 PHP 스크립트에서 사용할 수 있습니다. 결과 버퍼에서 PHP 변수로의 이 최종 전송은 메모리 소비에 영향을 미치며 버퍼링된 결과 세트를 사용할 때 대부분 눈에 띕니다.

기본적으로 MySQL 네이티브 드라이버는 버퍼링된 결과를 메모리에 두 번 유지하는 것을 방지하려고 합니다. 결과는 내부 결과 버퍼와 해당 zval에 한 번만 보관됩니다. PHP 스크립트에 의해 PHP 변수로 결과를 가져올 때 변수는 내부 결과 버퍼를 참조합니다. 데이터베이스 쿼리 결과는 복사되지 않고 메모리에 한 번만 유지됩니다. 사용자가 데이터베이스 결과를 보유하는 변수의 내용을 수정하는 경우 참조된 내부 결과 버퍼가 변경되지 않도록 쓰기 시 복사를 수행해야 합니다. 사용자가 결과 집합을 두 번째로 읽기로 결정할 수 있으므로 버퍼의 내용을 수정해서는 안 됩니다. 기록 중 복사 메커니즘은 추가 참조 관리 목록과 표준 zval 참조 카운터를 사용하여 구현됩니다. 사용자가 PHP 변수로 결과 집합을 읽고 변수가 설정 해제되기 전에 결과 집합을 해제하는 경우 쓰기 시 복사도 수행해야 합니다.

일반적으로 이 패턴은 결과 세트를 한 번 읽고 결과를 보유하는 변수를 수정하지 않는 스크립트에 적합합니다. 주요 단점은 추가 참조 관리로 인해 발생하는 메모리 오버헤드입니다. 이는 주로 mysqlnd 참조 관리가 참조를 중지할 때까지 결과를 보유하는 사용자 변수를 완전히 해제할 수 없다는 사실에서 비롯됩니다. MySQL Native 드라이버는 결과 세트가 해제되거나 copy-on-write가 수행될 때 사용자 변수에 대한 참조를 제거합니다. 관찰자는 결과 집합이 릴리스될 때까지 총 메모리 소비가 증가하는 것을 볼 수 있습니다. 통계를 사용하여 스크립트가 결과 세트를 명시적으로 릴리스하는지 또는 드라이버가 암시적 릴리스를 수행하는지 여부를 확인하여 메모리가 필요 이상으로 오래 사용되는지 확인하십시오. 통계는 또한 얼마나 많은 기록 중 복사 작업이 발생했는지 확인하는 데 도움이 됩니다.

while ($row = $res->fetch_assoc()) { ... }와 같거나 동등한 코드 조각을 사용하여 버퍼링된 결과 집합의 많은 작은 행을 읽는 PHP 스크립트는 참조 대신 복사본을 요청하여 메모리 소비를 최적화할 수 있습니다. 복사본을 요청한다는 것은 결과를 메모리에 두 번 유지하는 것을 의미하지만 결과 집합이 반복되고 결과 집합 자체를 해제하기 전에 PHP가 $row에 포함된 복사본을 해제할 수 있습니다. 로드된 서버에서 최대 메모리 사용을 최적화하면 전체 시스템 성능을 향상시키는 데 도움이 될 수 있지만 개별 스크립트의 경우 추가 할당 및 메모리 복사 작업으로 인해 복사 방식이 느려질 수 있습니다.

복사 모드는 mysqlnd.fetch_data_copy=1을 설정하여 적용할 수 있습니다.

모니터링 및 디버깅

MySQL 네이티브 드라이버의 메모리 사용량을 추적하는 방법에는 여러 가지가 있습니다. 목표가 높은 수준의 빠른 개요를 얻거나 PHP 스크립트의 메모리 효율성을 확인하는 것이라면 라이브러리에서 수집한 통계를 확인하십시오. 예를 들어 통계를 통해 PHP 스크립트에서 처리하는 것보다 더 많은 결과를 생성하는 SQL 문을 포착할 수 있습니다.

디버그 추적 로그는 메모리 관리 호출을 기록하도록 구성할 수 있습니다. 이것은 메모리가 할당되거나 해제되는 시점을 확인하는 데 도움이 됩니다. 그러나 요청된 메모리 청크의 크기가 나열되지 않을 수 있습니다.

일부 최신 버전의 MySQL Native Driver는 임의의 메모리 부족 상황을 에뮬레이션합니다. 이 기능은 라이브러리의 C 개발자 또는 mysqlnd 플러그인 작성자만 사용하도록 되어 있습니다. 해당 PHP 구성 설정 및 자세한 내용은 소스 코드를 검색하십시오. 이 기능은 비공개로 간주되며 사전 통지 없이 언제든지 수정될 수 있습니다.