2010-02-09 2 views
31

야간에 많은 일을 처리하는 스크립트가 있습니다.PDO : MySQL 서버가 사라졌습니다.

루프에서 실행되는 PDO prepared statement를 사용합니다.

처음 몇 개는 정상적으로 작동하지만 오류가 발생하는 지점에 도달합니다. "MySQL 서버가 없어졌습니다".

우리는 MySQL 5.0.77을 실행합니다.

PHP 버전 5.2.12

나머지 사이트는 정상적으로 실행됩니다.

+0

mysql 로그 란 무엇입니까? – prodigitalson

+0

uhg uhg 우리는 현재 mysql을 로그하지 않습니다. 왜냐하면 우리는 너무 많은 일이 일어나기 때문입니다. –

+0

MySQL의 문제가 PDO/PHP가 아닌 경우 문제를 해결하기가 어렵습니다. 밤새 실행되는 로그의 샘플 배치를 실행 한 다음 로그를 검토합니다 .-) – prodigitalson

답변

23

MySQL 설명서의 B.5.2.9. MySQL server has gone away 섹션에는이 오류의 가능한 원인 목록이 있습니다.

어쩌면 그 상황에 처했을까요? - 특히 긴 작업을 실행하는 것을 고려하면 wait_timeout에 대한 요점은 흥미로울 수 있습니다 ...

+0

당신이 옳다고 생각합니다. 내 대기 시간 제한은 90 초이며 실행하기 전에 잠시 시간이 걸릴 수 있습니다. PHP를 통해 스크립트 1 개만 동적으로 변경하는 방법이 있습니까? –

+4

또는 php PDO를 통해 "ping"mysql하는 방법? –

+0

문서를 보면 가장 간단한 방법은 다시 연결하는 것 같습니다. ;; 즉, 오류가 발생하면 PDO 클래스를 다시 인스턴스화하면 –

2

을 (를) 귀하의 광고 보드 인스턴스에서 사용해보십시오. 그게 도움이 될지 모르지만 로그 데이터가 없어도 그게 전부입니다.

2

(예 : wait_timeout 또는 KILL 명령을 실행하는 다른 스레드에 의해) 연결이 끊어 졌거나 서버가 충돌했거나 어떤 방식 으로든 mysql 프로토콜을 위반 한 것 같습니다.

후자는 서버 측 준비된 명령문 또는 다중 결과를 사용하는 경우 매우 가능성이 PDO의 버그가 될 가능성이 높습니다 : 서버 사고가 필요합니다

(힌트는하지 마십시오) 조사되다; 서버 로그를보십시오.

여전히 진행 상황을 모르는 경우 네트워크 패킷 덤퍼 (예 : tcpdump)를 사용하여 연결 내용을 덤프하십시오.

일반 쿼리 로그를 사용하도록 설정할 수도 있지만 프로덕션 환경에서는 매우주의 깊게 수행해야합니다.

15

대부분 허용 된 최대 패킷보다 긴 패킷을 서버로 보냈습니다.

서버의 최대 패킷 크기를 초과하는 BLOB를 삽입하려고하면 로컬 서버에서도 클라이언트 측에서 "MySQL 서버가 사라졌습니다"라는 메시지가 표시되고 "오류 1153은 'max_allowed_packet 서버 로그에서 '바이트'(오류 로깅이 사용 가능한 경우). 예를 들어, 당신은 당신이 이제까지 삽입 할 수 있습니다 최대 BLOB의 크기 무엇인지를 결정해야이 문제를 해결하고 그에 따라 my.inimax_allowed_packet을 설정하려면 :

[mysqld] 
... 
max_allowed_packet = 200M 
... 
+0

설정 max_allowed_packet = 16M 고정 된 내 문제 –

+1

+1이 오류는 내가 (서버가 사라 졌다고 말하는) 오류를 수정했다. – Nate

+0

나는 이것을' my.ini 대신 my.cnf' * 당신이 말했듯이,하지만 그렇게 했어. –

0

을 나는 똑같은 문제를 가지고 있었다. PDO 개체를 NULL로 설정하는 대신 unset을 수행하여이 문제를 해결했습니다. 예를 들어

: 또한

function connectdb($dsn,$username,$password,$driver_options) { 
    try { 
     $dbh = new PDO($dsn,$username,$password,$driver_options); 
     return $dbh; 
    } 
    catch(PDOException $e) 
    { 
     print "DB Error: ".$e->getMessage()."<br />"; 
     die(); 
    } 

} 

function closedb($dbh) { 
    unset($dbh);    // use this line instead of $dbh = NULL; 
} 

, 그것은 매우 모든 PDO 개체를 해제하는 것이 좋습니다. 여기에는 준비된 명령문을 포함하는 변수가 포함됩니다.

+4

'closedb'내부에서 지역 변수를 설정 해제 하시겠습니까? 즉 그 함수는 아무것도하지 않습니다. '$ dbh = null'도 함수 서명에서'&'를 치지 않으면 아무 일도하지 않습니다. – mpen

-3
$pdo = new PDO(
    $dsn, 
    $config['username'], 
    $config['password'], 
    array(
     PDO::ATTR_PERSISTENT => true, 
     PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION 
    ) 
); 

시도해보십시오.작동하지 않을 수 있습니다.

3

타임 아웃이있는 경우 호스팅 서버 관리가 연결을 끊는 동일한 문제가있었습니다.

중요한 부분으로 쿼리를 사용했기 때문에 PDO 클래스 대신 아래 클래스를 포함하고 "ConnectionManagerPDO"클래스 이름을 바꿀 수있는 코드를 작성했습니다. 방금 PDO 클래스를 래핑했습니다.

final class ConnectionManagerPDO 
{ 

    private $dsn; 
    private $username; 
    private $passwd; 
    private $options; 
    private $db; 
    private $shouldReconnect; 

    const RETRY_ATTEMPTS = 3; 

    public function __construct($dsn, $username, $passwd, $options = array()) 
    { 
     $this->dsn = $dsn; 
     $this->username = $username; 
     $this->passwd = $passwd; 
     $this->options = $options; 
     $this->shouldReconnect = true; 
     try { 
      $this->connect(); 
     } catch (PDOException $e) { 
      throw $e; 
     } 
    } 

    /** 
    * @param $method 
    * @param $args 
    * @return mixed 
    * @throws Exception 
    * @throws PDOException 
    */ 
    public function __call($method, $args) 
    { 
     $has_gone_away = false; 
     $retry_attempt = 0; 
     try_again: 
     try { 

      if (is_callable(array($this->db, $method))) { 

       return call_user_func_array(array($this->db, $method), $args); 
      } else { 

       trigger_error("Call to undefined method '{$method}'"); 
       /* 
       * or 
       * 
       * throw new Exception("Call to undefined method."); 
       * 
       */ 
      } 
     } catch (\PDOException $e) { 

      $exception_message = $e->getMessage(); 

      if (
       ($this->shouldReconnect) 
       && strpos($exception_message, 'server has gone away') !== false 
       && $retry_attempt <= self::RETRY_ATTEMPTS 
      ) { 
       $has_gone_away = true; 
      } else { 
       /* 
       * What are you going to do with it... Throw it back.. FIRE IN THE HOLE 
       */ 
       throw $e; 
      } 
     } 

     if ($has_gone_away) { 
      $retry_attempt++; 
      $this->reconnect(); 
      goto try_again; 
     } 
    } 


    /** 
    * Connects to DB 
    */ 
    private function connect() 
    { 
     $this->db = new PDO($this->dsn, $this->username, $this->passwd, $this->options); 
     /* 
     * I am manually setting to catch error as exception so that the connection lost can be handled. 
     */ 
     $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
    } 

    /** 
    * Reconnects to DB 
    */ 
    private function reconnect() 
    { 
     $this->db = null; 
     $this->connect(); 
    } 
} 

그런 다음 PDO에서와 마찬가지로 위의 클래스를 사용하여 시작할 수 있습니다.

try { 
    $db = new ConnectionManagerPDO("mysql:host=localhost;dbname=dummy_test", "root", ""); 
    $query = $db->query("select * from test"); 
    $query->setFetchMode(PDO::FETCH_ASSOC); 
} 
catch(PDOException $e){ 
    /* 
     handle the exception throw in ConnectionManagerPDO 
    */ 
} 
+0

자동 재 연결이 작동하지 않았습니다. 그래서 __call 함수를 제거하고 함수를 다시 public으로 만들었습니다. 나는 try/catch 블록에서 쿼리를 할 때 그것을 호출한다. –

+0

pdo가 "서버가 사라졌습니다"예외를 반환하는 경우에만 다시 연결됩니다. 예외 상황이 무엇인지 말해 주시겠습니까? – mysticmo

+0

나는 다시 시도했다. 예외는 "MySQL 서버가 사라졌다"였고 재 연결을 만들었다. 하지만 예외가 발생한 순간에 실행 된 쿼리가 손실되어 다시 연결 한 후에 쿼리를 다시 실행해야합니다. –

1

아래의 Nathan H는 pdo 재 연결 + 코드 사용 샘플을위한 php 클래스입니다. Screenshot이 첨부되어 있습니다.

<?php 

# set errors reporting level 
error_reporting(E_ALL^E_NOTICE^E_WARNING); 

# set pdo connection 
include('db.connection.pdo.php'); 

/* # this is "db.connection.pdo.php" content 
define('DB_HOST', 'localhost'); 
define('DB_NAME', ''); 
define('DB_USER', ''); 
define('DB_PWD', ''); 
define('DB_PREFIX', ''); 
define('DB_SHOW_ERRORS', 1); 

# connect to db 
try { 
    $dbh = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PWD); 
    $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); 
    $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, TRUE); 
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
} 
catch(PDOException $e) { 
    # echo $e->getMessage()."<br />"; 
    # exit; 
    exit("Site is temporary unavailable."); # 
} 
*/ 

$reconnection = new PDOReconnection($dbh); 

$reconnection->getTimeout(); 

echo $dbh->query('select 1')->fetchColumn(); 
echo PHP_EOL; 
echo 'sleep 10 seconds..'.PHP_EOL; 

sleep(10); 

$dbh = $reconnection->checkConnection(); 
echo $dbh->query('select 1')->fetchColumn(); 
echo PHP_EOL; 
echo 'sleep 35 seconds..'.PHP_EOL; 

sleep(35); 

$dbh = $reconnection->checkConnection(); 
echo $dbh->query('select 1')->fetchColumn(); 
echo PHP_EOL; 
echo 'sleep 55 seconds..'.PHP_EOL; 

sleep(55); 

$dbh = $reconnection->checkConnection(); 
echo $dbh->query('select 1')->fetchColumn(); 
echo PHP_EOL; 

echo 'sleep 300 seconds..'.PHP_EOL; 
sleep(300); 
$dbh = $reconnection->checkConnection(); 
echo $dbh->query('select 1')->fetchColumn(); 
echo PHP_EOL; 

# ************************************************************************************************* 
# Class for PDO reconnection 
class PDOReconnection 
{ 
    private $dbh; 

    # constructor 
    public function __construct($dbh) 
    { 
     $this->dbh = $dbh; 
    } 

    # ************************************************************************************************* 

    # get mysql variable "wait_timeout" value 
    public function getTimeout() 
    { 
     $timeout = $this->dbh->query('show variables like "wait_timeout"')->fetch(); # print_r($timeout); 
     echo '========================'.PHP_EOL.'mysql variable "wait_timeout": '.$timeout['Value'].' seconds.'.PHP_EOL.'========================'.PHP_EOL; 
    } 

    # ************************************************************************************************* 

    # check mysql connection 
    public function checkConnection() 
    { 
     try { 
      $this->dbh->query('select 1')->fetchColumn(); 
      echo 'old connection works..'.PHP_EOL.'========================'.PHP_EOL; 
     } catch (PDOException $Exception) { 
      # echo 'there is no connection.'.PHP_EOL; 
      $this->dbh = $this->reconnect(); 
      echo 'connection was lost, reconnect..'.PHP_EOL.'========================'.PHP_EOL; 
     } 

     return $this->dbh; 
    } 

    # ************************************************************************************************* 

    # reconnect to mysql 
    public function reconnect() 
    { 
     $dbh = new PDO('mysql:host=' . DB_HOST . ';dbname=' . DB_NAME, DB_USER, DB_PWD); 
     $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); 
     $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, TRUE); 
     $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);  
     return $dbh; 
    } 
} 
# /Class for PDO reconnection 
# ************************************************************************************************* 
관련 문제