Как исправить сообщение: SQLSTATE[08004] [1040] слишком много соединений
Я использую ниже код для подключения к базе данных
class Database extends PDO{
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();
}
}
}
каждая вещь, как логин, получение данных работала нормально . Теперь внезапно у меня появляется сообщение об ошибке исключения
Message: SQLSTATE[08004] [1040] Too many connections
Code: 1040
как исправить эту ошибку ?
у меня есть класс модели есть я создаю новую базу данных.
class Model {
protected $_db;
public function __construct(){
//connect to PDO here.
$this->_db = new Database();
}
}
и каждая модель, которую я делаю, я расширяюсь от класса модели.
1 ответов
потому что Model
класс создает новый Database
объект в своем конструкторе, каждый раз, когда вы инстанцировать Model
(или любой класс, расширяющий его), вы фактически открываете новая подключение к базе данных. Если вы создадите несколько Model
объекты, каждый из которых имеет свое собственное независимое соединение с базой данных, что является необычным, обычно ненужным, не хорошим использованием ресурсов, но и активно вредным, поскольку он использовал все доступные сервера подключение.
например, цикл для создания массива Model
объекты:
// 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)
использовать инъекцию зависимостей:
решение заключается в использовании инъекции зависимостей и передать the Database
объект в Model::__construct()
, а не позволить ему создать свой собственный.
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;
}
}
чтобы использовать его, управляющий код (код, который будет создавать экземпляры ваших моделей) должен сам вызывать new Database()
только один раз. Объект, созданный затем управляющий код должен быть передан конструкторам всех моделей.
// Instantiate one Database
$db = new Database();
// Pass it to models
$model = new Model($db);
для случая использования, когда вам действительно нужно другое независимое соединение с базой данных для модели,вы можете передать его другим. В частности, это полезно тестирование. Можно заменить объект тестовой базы данных или макет объекта.
// Instantiate one Database
$db = new Database();
$another_db = new Database();
// Pass it to models
$model = new Model($db);
$another_model = new Model($another_db);
постоянные соединения:
как упоминалось в комментариях, использование постоянного соединения возможно, решение, но не решение, которое я бы рекомендовал. PDO попытается повторно использовать существующее соединение с теми же учетными данными (как и все ваши), но вы не обязательно хотите, чтобы соединение кэшировалось при выполнении сценария. Если вы решили сделать это таким образом, вам нужно передать атрибут Database
конструктор.
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'");
}
соответствующая документация здесь: http://php.net/manual/en/pdo.connections.php#example-950
Синглтон решение:
используя одноэлементный шаблон (также не рекомендуется), вы можете по крайней мере уменьшить это до поиска/замены в коде модели. The Database
class нуждается в статическом свойстве, чтобы сохранить соединение для себя. Модели тогда звоните Database::getInstance()
вместо new Database()
для получения соединения. Вам нужно будет выполнить поиск и заменить в коде модели, чтобы заменить Database::getInstance()
.
хотя он работает хорошо и не сложно реализовать, в вашем случае это сделает тестирование немного сложнее, так как вам придется заменить весь Database
класс с одноименным классом тестирования. Вы не можете легко заменить тестовый класс на экземпляр на основе экземпляра.
применить шаблон singleton к 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;
}
}
теперь вам нужно будет изменить только Model
код Database::getInstance()
:
class Model {
protected $_db;
public function __construct(){
// Retrieve the database singleton
$this->_db = Database::getInstance();
}
}