PDO에서 영구 연결을 사용할 때의 단점은 무엇입니까


181

PDO에서 PDO::ATTR_PERSISTENT속성을 사용하여 연결을 지속시킬 수 있습니다 . PHP 매뉴얼에 따르면-

지속적인 연결은 스크립트 끝에서 닫히지 않지만 다른 스크립트가 동일한 자격 증명을 사용하여 연결을 요청하면 캐시되고 재사용됩니다. 영구 연결 캐시를 사용하면 스크립트가 데이터베이스와 통신해야 할 때마다 새 연결을 설정하는 오버 헤드를 피할 수있어 웹 응용 프로그램이 더 빨라집니다.

또한 설명서는 PDO ODBC 드라이버를 사용하는 동안 영구 연결을 사용하지 않는 것이 좋습니다. ODBC 연결 풀링 프로세스를 방해 할 수 있습니다.

따라서 마지막 경우를 제외하고 PDO에서 영구 연결을 사용하는 데 따른 결점은없는 것 같습니다. 그러나이 메커니즘을 사용하는 데있어 다른 단점, 즉이 메커니즘으로 인해 성능이 저하되는 상황이나 그와 같은 것이 있는지 알고 싶습니다.


와우, 당신은이 간단한 질문에 대해 1000 회 현상금을 지불 했습니까?
Pacerier

@Pacerier, 아니, 다른 사람 이었다.
Charles

답변:


287

여기에 설명 된 문제를 완화하는 방법을 자세히 설명하는 아래이 답변 을 읽으 십시오.


영구 연결을 수행하는 다른 PHP 데이터베이스 인터페이스와 마찬가지로 PDO를 사용하는 경우와 동일한 단점이 존재합니다. 데이터베이스 작업 중에 스크립트가 예기치 않게 종료되면 연결을 끊는 다음 요청이 죽은 스크립트가 중단 된 위치에서 처리됩니다. 연결은 PHP 수준이 아닌 프로세스 관리자 수준 (mod_php의 경우 Apache, FastCGI를 사용하는 경우 현재 FastCGI 프로세스 등)에서 열린 상태로 유지되며, PHP는 부모 프로세스에게 연결이 죽을 때 연결을 끊도록 지시하지 않습니다. 스크립트가 비정상적으로 종료됩니다.

사용 불능 스크립트 잠금 테이블 인 경우 해당 테이블은 연결이 끊어 지거나 연결을받는 다음 스크립트가 테이블 자체를 잠금 해제 할 때까지 잠금 상태로 유지됩니다.

교착 상태 스크립트가 트랜잭션 중간에있는 경우 교착 상태 타이머가 시작될 때까지 여러 테이블을 차단할 수 있으며 교착 상태 타이머는 문제를 일으키는 이전 요청 대신 새 요청을 종료 할 수 있습니다.

데드 스크립트가 트랜잭션 중간에있는 경우 해당 연결을받는 다음 스크립트도 트랜잭션 상태를 가져옵니다. 응용 프로그램 디자인에 따라 다음 스크립트가 실제로 기존 트랜잭션을 커밋하려고 시도하지 않을 수도 있고, 그렇지 않은 경우 커밋하거나하지 말아야 할 때 롤백 할 수도 있습니다.

이것은 빙산의 일각에 불과합니다. 모든 단일 스크립트 요청에 대해 더티 연결 후 항상 정리를 시도하면 어느 정도까지 완화 할 수 있지만 데이터베이스에 따라 고통이 될 수 있습니다. 스크립트에서 병목 현상을 일으키는 것으로 데이터베이스 연결 작성을 식별하지 않은 경우 ( xdebug 및 / 또는 xhprof를 사용하여 코드 프로파일 링을 수행 했음을 의미 함 ) 지속적인 연결을 솔루션으로 고려 해서는 안됩니다 .

또한 대부분의 최신 데이터베이스 (PostgreSQL 포함)에는 일반 바닐라 PHP 기반 영구 연결과 달리 즉각적인 단점이없는 연결 풀링을 선호하는 고유 한 방법이 있습니다.


요점을 명확히하기 위해 직장에서 지속적인 연결을 사용하지만 선택하지는 않습니다. 앱 서버에서 데이터베이스 서버로의 초기 연결이 정확히 3 초가 걸리는 이상한 연결 동작 이 발생 했습니다. 우리는 이것이 커널 버그라고 생각합니다. 우리는 문제가 무작위로 발생하여 요청시 재생산 할 수 없었기 때문에 문제 해결을 포기했고, 아웃소싱 한 IT 부서는이를 추적 할 수있는 구체적인 능력이 없었습니다.

어쨌든 창고의 직원들이 수백 개의 들어오는 부품을 처리 할 때 각 부품이 0.5 초가 아닌 3 초 반 걸리는 경우, 우리를 모두 납치하고 우리를 돕기 전에 조치를 취해야했습니다. 그래서 우리는 집에서 자란 ERP / CRM / CMS 괴물에서 약간의 비트를 뒤집었고 지속적인 연결의 모든 공포를 직접 경험했습니다. 겉보기에 무작위로 발생한 모든 미묘한 작은 문제와 기괴한 행동을 추적하는 데 몇 주가 걸렸습니다 . 사용자가 앱에서 부지런히 짜낸 일주일에 한 번 치명적인 오류가 잠긴 테이블, 버려진 트랜잭션 및 기타 불행한 이상한 상태를 떠나는 것으로 나타났습니다.

이 Sob-Story는 한 가지 중요한 점을 가지고 있습니다. 그것은 우리가 결코 깨지 않을 것으로 예상했던 것들을 모두 성능의 이름으로 깨뜨 렸습니다. 트레이드 오프는 그만한 가치가 없었으며 사용자의 폭동 없이도 정상적인 연결로 다시 전환 할 수있는 날을 간절히 기다리고 있습니다.


2
달리기 전에이 답변을 읽었 SELECT orders.* FROM orders LEFT JOIN items USING(item_id)
기를

31
나는 거의 10 년 동안 지속적인 연결을 사용하고있는 큰 웹 사이트를 알고 있습니다. 트릭은 DB 확장 위의 계층을 사용하고를 사용하여 정리해야 할 사항을 기억하는 것입니다 register_shutdown_function(). 프로세스가 종료되면 연결도 종료됩니다. 연결되지 않으면 연결이 깨끗한 상태로 다시 설정됩니다 (예 : 열린 트랜잭션이 롤백 됨). 이것이 실패하면 연결이 닫히고 다음 프로세스에서 동일한 프로세스에 대한 새 연결이 열립니다. 지속적인 연결을 해제 할 필요는 없습니다.
Walter Tross

@Charles가 궁금합니다 ... 문제가 해결 되었습니까?
Tschallacka

@MichaelDibbets 몇 달 전에 애플리케이션 서버를 교체하고 pconnect를 사용하여 세 번째 버그가 여전히 남아 있는지 확인했습니다. 아니었다. 프록시로 해결되었습니다. 상태 문제를 처리하도록 설계되지 않은 응용 프로그램에서 지속적인 연결을 수행 해야하는mysqli_change_user 사람들 에게는 아래의 답변 이 여전히 최선의 해결 방법 일 것입니다 .
Charles

5
연결 시간이 5 초 지연되어 DNS + IPv6 문제로 분리했습니다. 서버가 v6 주소를 찾고 실패한 후 IPv4 주소를 사용하고있었습니다.
Nigel Atkinson

45

위의 Charles의 문제에 대한 응답으로

올린 사람 : http://www.php.net/manual/en/mysqli.quickstart.connections.php를 -

지속적인 연결에 대한 일반적인 불만은 재사용하기 전에 상태가 재설정되지 않는다는 것입니다. 예를 들어, 열려 있거나 완료되지 않은 트랜잭션은 자동으로 롤백되지 않습니다. 또한 연결을 풀에 넣고 재사용하는 사이에 발생한 권한 부여 변경 사항도 반영되지 않습니다. 이것은 원하지 않는 부작용으로 보일 수 있습니다. 반대로, 영속이라는 이름은 국가가 지속된다는 약속으로 이해 될 수 있습니다.

mysqli 확장은 지속적인 연결에 대한 해석 : 상태 지속 및 재사용 전 상태 재설정을 모두 지원합니다. 기본값은 재설정입니다. 지속적인 연결이 재사용되기 전에 mysqli 확장은 암시 적으로 mysqli_change_user()상태 재설정을 호출 합니다. 영구 연결은 마치 방금 연 것처럼 사용자에게 나타납니다. 이전 사용법의 아티팩트가 표시되지 않습니다.

mysqli_change_user()기능은 값 비싼 작업입니다. 최상의 성능을 위해 사용자는 컴파일 플래그 MYSQLI_NO_CHANGE_USER_ON_PCONNECT가 설정된 상태 에서 확장을 다시 컴파일 할 수 있습니다 .

안전한 행동과 최상의 성능 중에서 선택하는 것은 사용자의 몫입니다. 둘 다 유효한 최적화 목표입니다. 사용 편의성을 위해 최대 성능을 희생시키면서 안전한 동작이 기본값으로 설정되었습니다.


+1, 우리가 다른 방식으로 혼란을 정리했다는 사실이 아니라면 change_user를 수동으로 호출하여 기괴한 알 수없는 상태 문제를 해결 했는지 확인하고 싶습니다 .
찰스

PDO Postgres 영구 연결에 해당하는 것은 무엇입니까? @Charles와 비슷한 문제가있어 잠시 후 사용자가 fetch sql과 같은 오류가 발생합니다.
Carmageddon

1
@Carmageddon, 그것은 새로운 질문에 더 적합하지만, tl; dr은 Postgres가 pconnect를 수행하지 않으며 대신 외부 연결 풀 중 하나를 사용해야한다는 것입니다.
Charles

@ 찰스, 그게 무슨 소리 야? PDO의 지속적 연결을 사용하는 것이 "외부 연결 풀"을 사용하는 것과 같지 않습니까? 또는 무슨 뜻입니까?
Carmageddon

@Carmageddon은 Postgres 커뮤니티가 pconnect보다 더 나은 솔루션으로 연결 풀링을 설정했다는 것을 의미합니다. pgbouncer 또는 pgpool-II를 확인하십시오. 어쨌든 PDO가 Postgres pconnect를 수행하는지 확실하지 않지만 로커에서 완전히 벗어날 수 있습니다.
찰스

13

영구 연결은 데이터베이스에 연결하는 데 시간이 오래 걸리는 경우에만 좋습니다. 요즘에는 거의 그렇지 않습니다. 지속적인 연결의 가장 큰 단점은 사이트를 탐색 할 수있는 사용자 수를 제한한다는 것입니다. MySQL이 한 번에 10 개의 동시 연결 만 허용하도록 구성된 경우 11 명이 사이트를 탐색하려고 할 때 작동하지 않습니다. .

PDO는 지속성을 관리하지 않습니다. MySQL 드라이버는 그렇지 않습니다. a) 연결이 가능하고 호스트 / 사용자 / 암호 / 데이터베이스가 일치하면 연결을 재사용합니다. 변경 사항이 있으면 연결을 재사용하지 않습니다. 가장 좋은 방법은 사이트에 다른 사용자가 있고 영구적으로 만드는 것이 좋지 않기 때문에 이러한 연결이 자주 시작되고 중지된다는 것입니다.

지속적인 연결에 대해 이해해야 할 핵심 사항은 대부분의 웹 응용 프로그램에서 사용하지 않아야한다는 것입니다. 그들은 유혹하는 것처럼 들리지만 위험하고 거의 쓸모가 없습니다.

나는 이것에 다른 스레드가 있다고 확신하지만 지속적인 연결은 요청간에 지속되므로 위험합니다. 예를 들어, 요청하는 동안 테이블을 잠근 다음 잠금을 해제하지 못하면 해당 테이블은 무기한으로 잠금 상태를 유지합니다. 영구 연결은 다른 요청간에 동일한 연결이 사용되는지 알 방법이 없으므로 99 %의 앱에서 거의 쓸모가 없습니다. 각 웹 스레드에는 자체 영구 연결 세트가 있으며 어떤 스레드가 어떤 요청을 처리할지 제어 할 방법이 없습니다.

PHP의 절차 적 mysql 라이브러리에는 mysql_connect에 대한 후속 호출이 다른 연결을 열지 않고 동일한 링크를 반환하는 기능이 있습니다 (예상대로). 이것은 지속적인 연결과 관련이 없으며 mysql 라이브러리에만 해당됩니다. PDO는 그러한 행동을 보이지 않습니다


자료 링크 : 링크

일반적으로 이것을 대략적인 "규칙 세트"로 사용할 수 있습니다.

, 다음과 같은 경우 영구 연결을 사용하십시오.

  • 데이터베이스에 액세스하는 응용 프로그램 / 사용자는 거의 없습니다. 즉, 동일한 호스트에 200 명의 서로 다른 사용자가 공유되므로 200 개의 열린 (그러나 유휴 상태 인) 연결이 발생하지 않습니다.
  • 데이터베이스가 네트워크를 통해 액세스하는 다른 서버에서 실행 중입니다

  • 하나의 응용 프로그램이 데이터베이스에 자주 액세스합니다

다음 과 같은 경우 영구 연결을 사용하지 마십시오.

  • 응용 프로그램은 데이터베이스를 한 시간에 100 번만 액세스하면됩니다.

  • 하나의 데이터베이스 서버에 액세스하는 수많은 웹 서버가 있습니다.

특히 네트워크를 통해 데이터베이스에 액세스하는 경우 영구 연결을 사용하는 것이 훨씬 빠릅니다. 데이터베이스가 동일한 컴퓨터에서 실행되는 경우 큰 차이는 없지만 여전히 조금 더 빠릅니다. 그러나 이름에서 알 수 있듯이 연결은 영구적입니다. 즉, 사용하지 않더라도 연결이 유지됩니다.

문제는 "기본 구성"에서 MySQL은 1000 개의 병렬 "오픈 채널"만 허용한다는 것입니다. 그런 다음 새 연결이 거부됩니다 (이 설정을 조정할 수 있음). 따라서 각 100 개의 클라이언트가있는 20 대의 웹 서버가 있고 각 서버마다 시간당 한 번의 페이지 액세스 권한 만있는 경우 간단한 수학은 데이터베이스에 2000 개의 병렬 연결이 필요하다는 것을 보여줍니다. 작동하지 않습니다.

Ergo : 요청이 많은 애플리케이션에만 사용하십시오.


4
줄에 당신의 대답은 stackoverflow.com/a/51583/718224
Tony Stark

1
"예, 영구 연결을 사용하는 경우 : [...] 데이터베이스에 액세스하는 응용 프로그램 / 사용자가 거의 없습니다"는 "요청이 많은 응용 프로그램에만 사용하십시오"와 모순됩니다. 그러나 후자는 정확하다. 상황 : 초당 수천 개의 요청으로 수백 개의 활성 데이터베이스 연결이 발생합니다. 시스템이 선형으로 확장되면 데이터베이스에 대한 연결 양도 선형으로 확장됩니다. 따라서 더 많은 요청 (더 많은 사용자)은 더 많은 연결을 초래합니다. 당신은 그래서 필요가 제한 아직 많은 활성 연결은 요청을 많이 (사용자)이있을 때 (!)
켄 반 Hoeylandt

12

내 테스트에서 로컬 호스트에 1 초 이상의 연결 시간이 있었으므로 영구 연결을 사용해야한다고 가정합니다. 추가 테스트는 'localhost'에 문제가 있음을 보여주었습니다.

초 단위의 테스트 결과 (php microtime으로 측정) :

  • 호스팅 된 웹 : connectDB : 0.0038912296295166
  • localhost : connectDB : 1.0214691162109 (1 초 이상 : localhost를 사용하지 마십시오!)
  • 127.0.0.1 : connectDB : 0.00097203254699707

흥미롭게도 : 다음 코드는 127.0.0.1을 사용하는 것만 큼 빠릅니다.

$host = gethostbyname('localhost');
// echo "<p>$host</p>";
$db = new PDO("mysql:host=$host;dbname=" . DATABASE . ';charset=utf8', $username, $password,
    array(PDO::ATTR_EMULATE_PREPARES => false,
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

PDO가 도메인 이름을 번역하는 데 어려움이있는 것 같습니다! 감사합니다. 쿼드 코어 머신에서 각 연결이 오래 걸리는 이유가 궁금합니다!
Mustafa

@ Gunnar Bernstein +1 좋은 발견. "localhost"는 확실히 더 오래 걸리고 이것은 웹 앱의 속도를 어느 정도 향상 시켰습니다 (많은 연결을 만듭니다).
imperium2335

1
대단하다. 내 개발 머신의 해상도에 문제가 있습니다. IP를 사용하면 스크립트가 6.1에서 1.1로 바뀌 었습니다
Pete

localhost소켓 연결을 사용하는 경우 소켓 연결은 많은 양의 연결에 나쁜 것으로 유명합니다.
mente

@mente 그 사실을 증명할 수있는 참고 자료가 있습니까? TCP보다 UDS가 선호된다고 생각하는 경향이 있습니다. 감사.
Nuxwin

6

지속적인 연결은 상당한 성능 향상을 제공해야합니다. 나는 당신이 "지속하지 말아야한다"라는 주장에 동의하지 않는다.

위의 불만은 MyIASM 테이블을 사용하고 테이블 잠금을 잡아서 자체 버전의 트랜잭션을 해킹하는 사람이 주도한 것처럼 들립니다. 물론 교착 상태에 빠질 것입니다! PDO의 beginTransaction ()을 사용하고 테이블을 InnoDB로 이동하십시오.


2
1 년 늦게 알고 있지만, 그 기록에 따르면, 내 이야기는 전체 텍스트 인덱싱 지원을 위해 MyISAM의 수렁에 갇힌 소수의 비정규 화 된 클론을 제외하고는 전체적으로 InnoDB 테이블 로 구성된 데이터베이스에서 나온 것입니다 .
Charles

Pfft, Sphinx는 오래되고 파열되었습니다. ElasticSearch 는 새로운 인기 입니다. 어느 날, 우리는 실제로 새로운 앱 대신에 오래된 앱에 사용합니다.
Charles

PostgreSQL의 전체 텍스트 검색이 진정한 승자입니다. 놀랍다. 작업을 수행하기 위해 다른 도구 / 서버를 실행할 필요가 없습니다. 데이터를 동기화 상태로 유지하는 것에 대해 걱정할 필요가 없습니다. 매우 세분화 된 컨트롤. 여러 사전 또는 직접 작성하십시오. 또한 PostgreSQL은 자동으로 다중 인덱스 쿼리를 사용하므로 실행중인 다른 쿼리와 함께 사용할 수 있습니다.
brightball

2
MySQL 5.6은 InnoDB 테이블에 대한 전체 텍스트 지원을 제공합니다.
timetofly

2

지속적인 연결을 유지하면 더 많은 시스템 리소스를 사용하게 될 것 같습니다. 아마도 사소한 금액이지만 여전히 ...


컴퓨터 시간의 마이크로에 대한 인간의 많은 시간의 종종 무역
앤디 체이스

1

영구 연결 사용에 대한 설명은 다른 데이터베이스에 비해 MySQL을 사용하는 것이 훨씬 빠르다는 사실에도 불구하고 비용이 많이 드는 연결 수를 분명히 줄입니다.

지속적인 연결의 첫 번째 문제는 ...

초당 1000 개의 연결을 작성하는 경우 일반적으로 연결이 매우 오랫동안 열려 있지는 않지만 운영 체제는 그렇지 않습니다. TCP / IP 프로토콜을 기반으로 포트는 즉시 재활용 할 수 없으며 재활용되기 전에 "FIN"단계에 시간을 투자해야합니다.

두 번째 문제는 많은 MySQL 서버 연결을 사용하는 것입니다.

많은 사람들은 단순히 * max_connections * 변수를 늘리고 MySQL과 100 개가 넘는 동시 연결을 얻을 수 있다는 사실을 깨닫지 못합니다. 다른 사람들은 MySQL과의 1024 개 이상의 연결을 전달할 수 없다는 오래된 Linux 문제로 인해 구타를당했습니다.

mysqli 확장에서 영구 연결이 비활성화 된 이유에 대해 설명 할 수 있습니다. 지속적인 연결을 잘못 사용하고 주된 이유가 아닌 성능 저하를 얻을 수 있다는 사실에도 불구하고. 실제 이유는 – 더 많은 문제를 얻을 수 있다는 것입니다.

MySQL이 그다지 어렵지 않은 MySQL 3.22 / 3.23의 경우에 걸쳐 지속적인 연결이 PHP에 적용되었으므로 문제없이 쉽게 연결을 재활용 할 수 있습니다. 그러나 이후 버전에서는 많은 문제가 발생했습니다. 커밋되지 않은 트랜잭션이있는 연결을 재활용해야하는 경우 문제가 발생합니다. 사용자 정의 문자 집합 구성을 사용하여 연결을 재활용하면 다시 위험에 처할 수 있고 세션 변수 당 변환 될 가능성이 있습니다.

지속적인 연결을 사용하는 데있어 한 가지 문제는 실제로 확장이 잘되지 않는다는 것입니다. 5000 명이 연결되어있는 사용자의 경우 5000 개의 영구 연결이 필요합니다. 지속성 요구 사항을 없애기 위해 연결이 없을 때 개인 연결을 공유 할 수있는 위치에 있기 때문에 비슷한 수량의 연결을 가진 10000 명에게 서비스를 제공 할 수 있습니다.


0

부분 솔루션이 일회용 연결 풀을 갖는지 궁금합니다. 시스템 사용량이 적을 때 연결 풀을 만드는 데 시간을 할애하여 최대 한도까지 넘겨주고 완료하거나 시간이 초과되면 시스템을 종료하고 종료 할 수 있습니다. 백그라운드에서 새 연결이 작성 될 때 작성 중입니다. 최악의 경우 링크를 설정하는 것이 제한 요인이라고 가정 할 때 풀없이 연결을 만드는 것만 큼 느려 야합니까?

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.