sql >> Databáze >  >> RDS >> Mysql

Jak opravit Zpráva:SQLSTATE[08004] [1040] Příliš mnoho připojení

Protože váš Model class vytvoří instanci nové Database objekt ve svém konstruktoru, pokaždé, když vytvoříte instanci Model (nebo jakoukoli třídu, která ji rozšiřuje), ve skutečnosti otevíráte novou připojení k databázi. Pokud vytvoříte několik Model objekty, každý pak má své vlastní nezávislé databázové připojení, což je neobvyklé, obvykle zbytečné, není to dobré využití zdrojů, ale také aktivně škodlivé, protože vyčerpalo všechna dostupná připojení serveru.

Například smyčkování k vytvoření pole Model objekty:

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

Použít vložení závislosti:

Řešením je použít vkládání závislostí a pass Database objekt do Model::__construct() místo toho, aby mu umožnil vytvořit vlastní instanci.

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

Chcete-li to potom použít, řídicí kód (kód, který vytvoří instanci vašich modelů) by měl sám volat new Database() pouze jednou. Tento objekt vytvořený řídicím kódem pak musí být předán konstruktérům všech modelů.

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

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

Pro případ použití, kdy skutečně potřebujete pro model jiné nezávislé databázové připojení, mu můžete předat jiné. Zejména je to užitečné pro testování . Můžete nahradit objekt testovací databáze nebo falešný objekt.

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

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

Trvalá připojení:

Jak je uvedeno v komentářích, použití trvalého připojení je možná řešení, ale ne řešení, které bych doporučil . PDO se pokusí znovu použít existující připojení se stejnými pověřeními (jako všechna vaše), ale nemusíte nutně chtít, aby se připojení ukládalo do mezipaměti při provádění skriptu. Pokud jste se rozhodli to udělat tímto způsobem, musíte atribut předat 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'");
}

Příslušná dokumentace je zde:http://php.net/manual /en/pdo.connections.php#example-950

Singleton řešení:

Pomocí jednobarevného vzoru (také se nedoporučuje) to můžete alespoň zredukovat na hledání/nahrazení v kódu modelu. Database class potřebuje statickou vlastnost, aby si udržela připojení pro sebe. Modely pak volají Database::getInstance() místo new Database() pro obnovení připojení. Chcete-li nahradit Database::getInstance(), museli byste provést vyhledávání a nahrazení v kódu modelu .

Ačkoli to funguje dobře a není obtížné jej implementovat, ve vašem případě by to trochu ztížilo testování, protože byste museli nahradit celou Database třída se stejnojmennou testovací třídou. Testovací třídu nelze snadno nahradit instancí po instanci.

Použijte jednoduchý vzor na 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;
   }
}

Nyní byste potřebovali změnit pouze Model kód pro použití Database::getInstance() :

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



  1. Jak odstranit řádky z tabulky spojení (ManyToMany) v Doctrine?

  2. Úvod do speciálních dotazů

  3. Získání seznamu dat v rozsahu v PostgreSQL

  4. Chyba tabulky MySQL neexistuje, ale existuje