Как исправить сообщение: SQLSTATE[08004] [1040] слишком много соединений

Я использую ниже код для подключения к базе данных

class Database extends PDO{

    function __construct(){

        try {
            parent::__construct(DB_TYPE.':host='.DB_HOST.';dbname='.DB_NAME,DB_USER,DB_PASS);
            $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
        } catch(PDOException $e){
            Logger::newMessage($e);
            logger::customErrorMsg();
        }

    }
}

каждая вещь, как логин, получение данных работала нормально . Теперь внезапно у меня появляется сообщение об ошибке исключения

Message: SQLSTATE[08004] [1040] Too many connections

Code: 1040

как исправить эту ошибку ?

у меня есть класс модели есть я создаю новую базу данных.

 class Model {

protected $_db;

public function __construct(){
    //connect to PDO here.
    $this->_db = new Database();
    }
  }

и каждая модель, которую я делаю, я расширяюсь от класса модели.

1 ответов


потому что Model класс создает новый Database объект в своем конструкторе, каждый раз, когда вы инстанцировать Model (или любой класс, расширяющий его), вы фактически открываете новая подключение к базе данных. Если вы создадите несколько Model объекты, каждый из которых имеет свое собственное независимое соединение с базой данных, что является необычным, обычно ненужным, не хорошим использованием ресурсов, но и активно вредным, поскольку он использовал все доступные сервера подключение.

например, цикл для создания массива Model объекты:

// If a loop creates an array of Model objects
while ($row = $something->fetch()) {
  $models[] = new Model();
}
// each object in $models has an independent database connection
// the number of connections now in use by MySQL is now == count($models)

использовать инъекцию зависимостей:

решение заключается в использовании инъекции зависимостей и передать the Database объект в Model::__construct(), а не позволить ему создать свой собственный.

class Model {

  protected $_db;

  // Accept Database as a parameter
  public function __construct(Database $db) {
    // Assign the property, do not instantiate a new Database object
    $this->_db = $db;
  }
}

чтобы использовать его, управляющий код (код, который будет создавать экземпляры ваших моделей) должен сам вызывать new Database() только один раз. Объект, созданный затем управляющий код должен быть передан конструкторам всех моделей.

// Instantiate one Database
$db = new Database();

// Pass it to models
$model = new Model($db);

для случая использования, когда вам действительно нужно другое независимое соединение с базой данных для модели,вы можете передать его другим. В частности, это полезно тестирование. Можно заменить объект тестовой базы данных или макет объекта.

// Instantiate one Database
$db = new Database();
$another_db = new Database();

// Pass it to models
$model = new Model($db);
$another_model = new Model($another_db);

постоянные соединения:

как упоминалось в комментариях, использование постоянного соединения возможно, решение, но не решение, которое я бы рекомендовал. PDO попытается повторно использовать существующее соединение с теми же учетными данными (как и все ваши), но вы не обязательно хотите, чтобы соединение кэшировалось при выполнении сценария. Если вы решили сделать это таким образом, вам нужно передать атрибут Database конструктор.

try {
  // Set ATTR_PERSISTENT in the constructor:
  parent::__construct(DB_TYPE.':host='.DB_HOST.';dbname='.DB_NAME,DB_USER,DB_PASS, array(PDO::ATTR_PERSISTENT => true));
  $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  $this->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
}

соответствующая документация здесь: http://php.net/manual/en/pdo.connections.php#example-950

Синглтон решение:

используя одноэлементный шаблон (также не рекомендуется), вы можете по крайней мере уменьшить это до поиска/замены в коде модели. The Database class нуждается в статическом свойстве, чтобы сохранить соединение для себя. Модели тогда звоните Database::getInstance() вместо new Database() для получения соединения. Вам нужно будет выполнить поиск и заменить в коде модели, чтобы заменить Database::getInstance().

хотя он работает хорошо и не сложно реализовать, в вашем случае это сделает тестирование немного сложнее, так как вам придется заменить весь Database класс с одноименным классом тестирования. Вы не можете легко заменить тестовый класс на экземпляр на основе экземпляра.

применить шаблон singleton к Database:

class Database extends PDO{
   // Private $connection property, static
   private static $connection;

   // Normally a singleton would necessitate a private constructor
   // but you can't make this private while the PDO 
   // base class exposes it as public
   public function __construct(){
        try {
            parent::__construct(DB_TYPE.':host='.DB_HOST.';dbname='.DB_NAME,DB_USER,DB_PASS);
            $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
        } catch(PDOException $e){
            Logger::newMessage($e);
            logger::customErrorMsg();
        }

    }

   // public getInstance() returns existing or creates new connection
   public static function getInstance() {
     // Create the connection if not already created
     if (self::$connection == null) {
        self::$connection = new self();
     } 
     // And return a reference to that connection
     return self::$connection;
   }
}

теперь вам нужно будет изменить только Model код Database::getInstance():

class Model {

  protected $_db;

   public function __construct(){
     // Retrieve the database singleton
     $this->_db = Database::getInstance();
   }
}