Mysql
 sql >> Datenbank >  >> RDS >> Mysql

So beheben Sie die Meldung:SQLSTATE[08004] [1040] Zu viele Verbindungen

Weil Ihr Model Klasse instanziiert eine neue Database -Objekt in seinem Konstruktor jedes Mal, wenn Sie ein Model instanziieren (oder jede Klasse, die es erweitert), öffnen Sie tatsächlich ein neues Datenbankverbindung. Wenn Sie mehrere Model erstellen Objekte, hat dann jedes seine eigene unabhängige Datenbankverbindung, was ungewöhnlich, normalerweise unnötig, keine gute Nutzung von Ressourcen, aber auch aktiv schädlich ist, da es alle verfügbaren Verbindungen des Servers verbraucht hat.

Beispielsweise eine Schleife zum Erstellen eines Arrays von Model Objekte:

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

Verwenden Sie Abhängigkeitsinjektion:

Die Lösung besteht darin, Abhängigkeitsinjektion und pass zu verwenden die Database Objekt in das Model::__construct() anstatt ihm zu erlauben, sich selbst zu instanziieren.

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

Um es dann zu verwenden, sollte der steuernde Code (der Code, der Ihre Modelle instanziiert) selbst new Database() aufrufen nur einmal. Dieses vom steuernden Code erstellte Objekt muss dann an die Konstruktoren aller Modelle weitergegeben werden.

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

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

Für den Anwendungsfall, dass Sie tatsächlich eine andere unabhängige Datenbankverbindung für ein Modell benötigen, können Sie ihm eine andere geben. Dies ist insbesondere zum Testen nützlich . Sie können ein Testdatenbankobjekt oder ein Scheinobjekt ersetzen.

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

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

Permanente Verbindungen:

Wie in den Kommentaren erwähnt, ist die Verwendung einer dauerhaften Verbindung möglicherweise eine Lösung, aber nicht die Lösung, die ich empfehlen würde . PDO versucht, eine vorhandene Verbindung mit denselben Anmeldeinformationen wiederzuverwenden (wie alle Ihre), aber Sie möchten nicht unbedingt, dass die Verbindung über die Skriptausführung hinweg zwischengespeichert wird. Wenn Sie sich dafür entschieden haben, müssen Sie das Attribut an die Database übergeben 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'");
}

Die entsprechende Dokumentation finden Sie hier:http://php.net/manual /en/pdo.connections.php#example-950

Singleton-Lösung:

Mit einem Singleton-Pattern (ebenfalls nicht empfohlen) könnten Sie dies zumindest auf ein Suchen/Ersetzen im Modellcode reduzieren. Die Database Die Klasse benötigt eine statische Eigenschaft, um eine Verbindung für sich selbst aufrechtzuerhalten. Modelle rufen dann Database::getInstance() auf statt new Database() um die Verbindung abzurufen. Sie müssten im Modellcode suchen und ersetzen, um Database::getInstance() zu ersetzen .

Obwohl es gut funktioniert und nicht schwer zu implementieren ist, würde es in Ihrem Fall das Testen etwas erschweren, da Sie die gesamte Database ersetzen müssten Klasse mit einer gleichnamigen Testklasse. Sie können eine Testklasse nicht einfach von Instanz zu Instanz ersetzen.

Singleton-Muster auf Database anwenden :

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

Jetzt müssten Sie nur noch das Model ändern Code zur Verwendung von Database::getInstance() :

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