sql >> Base de Datos >  >> RDS >> Mysql

Cómo arreglar Mensaje:SQLSTATE[08004] [1040] Demasiadas conexiones

Porque tu Model la clase instancia una nueva Database objeto en su constructor, cada vez que instancias un Model (o cualquier clase que la amplíe), en realidad está abriendo un nuevo conexión de base de datos. Si crea varios Model objetos, cada uno tiene su propia conexión de base de datos independiente, lo cual es poco común, generalmente innecesario, no es un buen uso de los recursos, pero también es activamente dañino ya que ha usado todas las conexiones disponibles del servidor.

Por ejemplo, hacer un bucle para crear una matriz de Model objetos:

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

Usar inyección de dependencia:

La solución es usar inyección de dependencia y pasar la Database objeto en el Model::__construct() en lugar de permitirle que cree una instancia propia.

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

Entonces, para usarlo, el código de control (el código que creará una instancia de sus modelos) debería llamar a new Database() sólo una vez. Ese objeto creado por el código de control debe luego pasarse a los constructores de todos los modelos.

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

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

Para el caso de uso en el que realmente necesita una conexión de base de datos independiente diferente para un modelo, puede entregarle una diferente. En particular, esto es útil para probar . Puede sustituir un objeto de base de datos de prueba o un objeto simulado.

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

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

Conexiones persistentes:

Como se menciona en los comentarios, usar una conexión persistente es posiblemente una solución, pero no es la solución que recomendaría . PDO intentará reutilizar una conexión existente con las mismas credenciales (como todas las suyas), pero no necesariamente desea que la conexión se almacene en caché durante la ejecución del script. Si decidió hacerlo de esta manera, debe pasar el atributo a la Database constructor.

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

La documentación relevante está aquí:http://php.net/manual /es/pdo.conexiones.php#ejemplo-950

Solución única:

Usando un patrón singleton (tampoco recomendado), al menos podría reducir esto a una búsqueda/reemplazo en el código del modelo. La Database la clase necesita una propiedad estática para mantener una conexión por sí misma. Luego, los modelos llaman a Database::getInstance() en lugar de new Database() para recuperar la conexión. Debería hacer una búsqueda y reemplazar en el código del modelo para sustituir Database::getInstance() .

Aunque funciona bien y no es difícil de implementar, en su caso dificultaría un poco más las pruebas, ya que tendría que reemplazar toda la Database clase con una clase de prueba del mismo nombre. No puede sustituir fácilmente una clase de prueba caso por caso.

Aplicar patrón singleton a 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;
   }
}

Ahora necesitaría cambiar solo el Model código para usar Database::getInstance() :

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