sql >> Database >  >> RDS >> Mysql

Hoe te repareren Bericht:SQLSTATE [08004] [1040] Te veel verbindingen

Omdat uw Model class start een nieuwe Database object in zijn constructor, elke keer dat u een Model instant instantieert (of een klasse die deze uitbreidt), je opent in feite een nieuwe database verbinding. Als u meerdere Model objecten, elk heeft dan zijn eigen onafhankelijke databaseverbinding, wat ongebruikelijk is, meestal onnodig, geen goed gebruik van bronnen, maar ook actief schadelijk omdat het alle beschikbare verbindingen van de server heeft opgebruikt.

Bijvoorbeeld een lus maken om een ​​array van Model . te maken objecten:

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

Gebruik afhankelijkheidsinjectie:

De oplossing is om afhankelijkheidsinjectie te gebruiken en pass de Database object in het Model::__construct() in plaats van het toe te staan ​​zijn eigen te instantiëren.

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

Om het dan te gebruiken, moet de controlerende code (de code die uw modellen zal instantiëren) zelf new Database() aanroepen slechts één keer. Dat object dat door de controlerende code is gemaakt, moet dan worden doorgegeven aan de constructeurs van alle modellen.

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

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

Voor het gebruik waarbij u voor een model daadwerkelijk een andere onafhankelijke databaseverbinding nodig heeft, kunt u deze een andere geven. Dit is vooral handig voor testen . U kunt een testdatabase-object of een nepobject vervangen.

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

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

Persistente verbindingen:

Zoals vermeld in de opmerkingen, is het gebruik van een permanente verbinding mogelijk een oplossing, maar niet de oplossing die ik zou aanbevelen . PDO zal proberen een bestaande verbinding opnieuw te gebruiken met dezelfde inloggegevens (zoals die van u allemaal zullen hebben), maar u wilt niet per se dat de verbinding in de cache wordt opgeslagen tijdens het uitvoeren van scripts. Als je hebt besloten om het op deze manier te doen, moet je het attribuut doorgeven aan de Database constructeur.

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

De relevante documentatie is hier:http://php.net/manual /en/pdo.connections.php#example-950

Singleton-oplossing:

Met behulp van een singletonpatroon (ook niet aanbevolen), zou je dit in ieder geval kunnen reduceren tot een zoeken/vervangen in de modelcode. De Database class heeft een statische eigenschap nodig om een ​​verbinding voor zichzelf te houden. Modellen roepen vervolgens Database::getInstance() . aan in plaats van new Database() om de verbinding op te halen. U moet zoeken en vervangen in de modelcode om Database::getInstance() te vervangen .

Hoewel het goed werkt en niet moeilijk te implementeren is, zou het in jouw geval het testen een beetje moeilijker maken omdat je de hele Database zou moeten vervangen klasse met een testklasse met dezelfde naam. U kunt een testklasse niet gemakkelijk van instantie tot instantie vervangen.

Singleton-patroon toepassen op 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;
   }
}

Nu hoeft u alleen het Model . te wijzigen code om Database::getInstance() . te gebruiken :

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



  1. SQLiteException:tabel bestaat al

  2. MySQL datetime-velden en zomertijd -- hoe verwijs ik naar het extra uur?

  3. Scala Spark-type komt niet overeen Eenheid, vereist rdd.RDD

  4. Wat is PDO, hoe is het gerelateerd aan SQL-injectie en waarom zou ik dit gebruiken?