Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Jak naprawić komunikat:SQLSTATE[08004] [1040] Zbyt wiele połączeń

Ponieważ Twój Model klasa tworzy instancję nowej Database w swoim konstruktorze, za każdym razem, gdy tworzysz instancję Model (lub dowolną klasę ją rozszerzającą), w efekcie otwierasz nową połączenie z bazą danych. Jeśli utworzysz kilka Model obiektów, każdy ma wtedy własne niezależne połączenie z bazą danych, co jest rzadkie, zwykle niepotrzebne, nie jest dobrym wykorzystaniem zasobów, ale także jest aktywnie szkodliwe, ponieważ wykorzystuje wszystkie dostępne połączenia serwera.

Na przykład zapętlenie w celu utworzenia tablicy Model obiekty:

// 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)

Użyj wstrzykiwania zależności:

Rozwiązaniem jest użycie wstrzykiwania zależności i pass Database do obiektu Model::__construct() zamiast pozwalać mu na tworzenie własnego wystąpienia.

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;
  }
}

Aby go wtedy użyć, kod kontrolujący (kod, który będzie tworzył instancje twoich modeli) powinien sam wywołać new Database() tylko raz. Ten obiekt utworzony przez kod kontrolujący musi następnie zostać przekazany do konstruktorów wszystkich modeli.

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

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

W przypadku użycia, w którym faktycznie potrzebujesz innego niezależnego połączenia z bazą danych dla modelu, możesz przekazać mu inne. Jest to szczególnie przydatne do testowania . Możesz podstawić testowy obiekt bazy danych lub obiekt próbny.

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

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

Stałe połączenia:

Jak wspomniano w komentarzach, używanie stałego połączenia jest prawdopodobnie rozwiązaniem, ale nie rozwiązaniem, które polecam . PDO spróbuje ponownie użyć istniejącego połączenia z tymi samymi poświadczeniami (jak wszystkie twoje), ale niekoniecznie chcesz, aby połączenie było buforowane podczas wykonywania skryptu. Jeśli zdecydowałeś się zrobić to w ten sposób, musisz przekazać atrybut do Database konstruktor.

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'");
}

Odpowiednia dokumentacja jest tutaj:http://php.net/manual /pl/pdo.connections.php#example-950

Rozwiązanie jednotonowe:

Używając wzorca singleton (również niezalecanego), możesz przynajmniej zredukować to do wyszukiwania/zamieniania w kodzie modelu. Database class potrzebuje właściwości statycznej, aby utrzymać połączenie dla siebie. Modele następnie wywołują Database::getInstance() zamiast new Database() aby odzyskać połączenie. Będziesz musiał przeprowadzić wyszukiwanie i zamianę w kodzie modelu, aby zastąpić Database::getInstance() .

Chociaż działa dobrze i nie jest trudna do zaimplementowania, w twoim przypadku sprawiłoby to, że testowanie byłoby trochę trudniejsze, ponieważ musiałbyś wymienić całą Database klasa z klasą testową o tej samej nazwie. Nie możesz łatwo zastąpić klasy testowej na podstawie instancji po instancji.

Zastosuj pojedynczy wzór do 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;
   }
}

Teraz musiałbyś zmienić tylko Model kod do użycia Database::getInstance() :

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



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak mogę powtórzyć identyfikator zasobu #6 z odpowiedzi MySql w PHP?

  2. Indeksuj bazę danych MySQL za pomocą Apache Lucene i synchronizuj je

  3. UnicodeEncodeError:kodek „latin-1” nie może zakodować znaku

  4. Jak opróżnić bazę danych MySQL

  5. CakePHP - pobierz ostatnie uruchomienie zapytania