sql >> Databasteknik >  >> RDS >> Mysql

Så här fixar du Meddelande:SQLSTATE[08004] [1040] För många anslutningar

Eftersom din Model klass instansierar en ny Database objekt i dess konstruktor, varje gång du instansierar en Model (eller någon klass som utökar den), öppnar du i praktiken en ny databasanslutning. Om du skapar flera Model objekt, var och en har sedan sin egen oberoende databasanslutning, vilket är ovanligt, vanligtvis onödigt, inte en bra användning av resurser, men också aktivt skadlig eftersom den har använt upp alla serverns tillgängliga anslutningar.

Till exempel, looping för att skapa en array av Model objekt:

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

Använd beroendeinjektion:

Lösningen är att använda beroendeinjektion och pass Database objekt till Model::__construct() snarare än att låta den instansiera sin egen.

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

För att använda den då bör den kontrollerande koden (koden som kommer att instansiera dina modeller) själv anropa new Database() bara en gång. Det objektet som skapas av den styrande koden måste sedan skickas till konstruktörerna av alla modeller.

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

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

För användningsfallet där du faktiskt behöver en annan oberoende databasanslutning för en modell, kan du ge den en annan. Detta är särskilt användbart för testning . Du kan ersätta ett testdatabasobjekt eller ett skenobjekt.

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

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

Ihållande anslutningar:

Som nämnts i kommentarerna är användning av en beständig anslutning möjligen en lösning, men inte den lösning jag skulle rekommendera . PDO kommer att försöka återanvända en befintlig anslutning med samma referenser (som alla dina kommer att ha), men du vill inte nödvändigtvis att anslutningen ska cachelagras under skriptkörning. Om du bestämde dig för att göra det på detta sätt måste du skicka attributet till Database konstruktör.

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

Den relevanta dokumentationen finns här:http://php.net/manual /en/pdo.connections.php#example-950

Singleton lösning:

Genom att använda ett singelmönster (rekommenderas inte heller) kan du åtminstone reducera detta till en sökning/ersätt i modellkoden. Database klass behöver en statisk egenskap för att hålla en anslutning för sig själv. Modeller anropar sedan Database::getInstance() istället för new Database() för att hämta anslutningen. Du skulle behöva göra en sökning och ersätta i modellkoden för att ersätta Database::getInstance() .

Även om det fungerar bra och inte är svårt att implementera, skulle det i ditt fall göra testningen lite svårare eftersom du skulle behöva ersätta hela Database klass med en testklass med samma namn. Du kan inte enkelt ersätta en testklass på en instans för instans.

Tillämpa singelmönster på 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 skulle du bara behöva ändra Model kod för att använda Database::getInstance() :

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



  1. Kategorisera mysql-data i separata html-tabeller?

  2. Hur man tar bort den senaste posten (på villkor) från en tabell i MySql

  3. JDBC returnerar MySQLSyntaxError Undantag med korrekt sats

  4. hur man sorterar ordning på LEFT JOIN i SQL-fråga?