Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
Tenho um script relativamente simples que está retornando o erro Too Many Connections do MySql e não sei mais o que posso fazer.
Tentei reproduzir o erro juntando as rotinas que apresentam o erro. O código é esse:
foreach($res as $key=>$value){
$resposta->setIdPesquisa($res[$key]['id']);
$pergunta->setIdPesquisa($res[$key]['id']);
$questionario->setIdPesquisa($res[$key]['id']);
$crud = new Crud();
$arrQuestionariosIniciados = $crud->selectWithCriteria('qresposta_basico',array(new TFilter('id_pesquisa', '=', $pergunta->getIdPesquisa())), null);
$arrPerguntas = $pergunta->retrieveAll('cad_questionario_pergunta');
$total = 0;
foreach($arrQuestionariosIniciados as $Qkey=>$Qvalue){
foreach($arrPerguntas as $key=>$value){
AQUI ESTÁ O ERRO ===>$arrRespostas = $resposta->retrieveWithCriteria(array('id_pergunta'=>$arrPerguntas[$key]['id'], 'id_basico'=>$arrQuestionariosIniciados[$Qkey]['id']));
$total = (int)count($arrRespostas);
}
}
//$ret = $questionario->numQuestionariosRespondidos($pergunta, $resposta);
$research->getAmostraTotal($res[$key]['id']);
echo "teste";
}
Se eu comento a linha do erro, os loops acontecem normalmente.
Este script está rodando em um servidor com 1Gb de RAM.
O mysql está com 150 conexões máximas. Já tentei aumentar para 1000, mas o erro continua. Alguém tem alguma pista do que posso fazer?
Oi Bruno, o método da classe resposta que está sendo utilizado é este:
public function retrieveWithCriteria($arrCriteria){
$sql = new TSqlSelect();
$sql->setEntity('qresposta');
$sql->addColumn('*');
$criteria = new TCriteria();
foreach($arrCriteria as $key=>$value){
$criteria->add(new TFilter($key, '=', $value));
}
$sql->setCriteria($criteria);
$this->query = $sql->getInstruction();
$this->res = TSql::selectAll($this->query);
if (PEAR::isError($this->res))
{
print_r($this->res);
return false;
}
return $this->res;
}
e o método da classe CRUD que está sendo utilizado é basicamente o mesmo.
public function selectWithCriteria($entity, array $tFilter, $arrProp, $debug=false){
$sql = new TSqlSelect();
$sql->setEntity($entity);
$sql->addColumn('*');
$criteria = new TCriteria();
if(isset($arrProp)){
foreach($arrProp as $key=>$value)
$criteria->setProperty($key, $value);
}
if(isset($tFilter)){
foreach($tFilter as $key=>$value)
$criteria->add($value);
}
$sql->setCriteria($criteria);
$this->query = $sql->getInstruction();
if($debug)
echo "<pre>".$this->query ."<BR></pre>";
$this->res = TSql::selectAll($this->query);
return $this->res;
}Oi Igor,
como vai?
Desculpe, mas não entendi suua colocação.
Uso o pacote MDB2 do PEAR para abstração do banco de dados. É amplamente utilizado (inclusive em meus outros projetos) e nunca tive problemas com ele. Não acredito que o erro esteja no PEAR, mas sim em como o estou utilizando, apenas não consigo encontrar esta falha que está sobrecarregando o mysql.
Poste o construtor da classe Crud, por favor.
Oi William,
obrigado pelo interesse. A Crud não tem construtor.
Abs.
Ok, é que olha o seguinte:
foreach($res as $key=>$value){
foreach($arrQuestionariosIniciados as $Qkey=>$Qvalue){
foreach($arrPerguntas as $key=>$value){
caracaa!!!! 3 loops encaixados!!
de onde eu venho e qndo aprendi a programar, me diziam algo como: se você precisa de mais que 2 loops encaixados, então provavelmente o teu algoritmo está errado(existem ressalvas).
O meu chute, é que dentro de algum desses loops você esteja se conectando ao banco, assim a cada iteração criando uma nova conexão. Entendeu ?
Somente conhecendo todo o projeto, e todos os códigos atrelados é que eu poderia te dizer algo com mais precisão. Mas este é o meu feeling com os dados que você forneceu.
Oi William,
realmente está confuso, mas esse script foi feito apenas para reproduzir o erro do projeto, por isso os loops.
Classe de conexão:
class Conn{
private function __construct() {}
public static function open()
{
/Local/
$db_tipo="mysqli";
$user="root";
$senha="";
$host="xxxx";
$dbName="xxxx";
$dsn = "$db_tipo://$user:$senha@$host/$dbName?charset=utf8";
$db = MDB2::factory($dsn);
//$db->setOption('debug', 1);
if (MDB2::isError($db)) {
die($db->getMessage().' - '.$db->getUserinfo());
}
$db->setFetchMode(MDB2_FETCHMODE_ASSOC);
if(MDB2::isError($db)) die($db->getMessage());
return $db;
}
}
Classe que lida com o SQL.
class TSql{
private static $conn;
private function __construct()
{
if(empty(self::$conn))
{
self::$conn = Conn::open();
}
return self::$conn;
}
public function select($sth){
$conn = Conn::open();
$res = $conn->query($sth);
$ret = $res->fetchRow();
if(PEAR::isError($ret))
return false;
else
return $ret;
}
public function selectAll($sth){
$conn = Conn::open();
$res = $conn->query($sth);
if(PEAR::isError($res))
return false;
$ret = array(); // Variável de retorno
while ($result = $res->fetchRow()){
$ret[] = $result;
}
return $ret;
}
public function insert($sth, $retLastId=false){
$conn = Conn::open();
$ret = $conn->exec($sth);
if(PEAR::isError($ret)){
return $ret;
}else{
if($retLastId){
$id = (int)$conn->lastInsertID($tabela, $campo);
return $id;
}else{
return $ret;
}
}
}
public function update($sth){
$conn = Conn::open();
$ret = $conn->exec($sth);
if(PEAR::isError($ret))
return false;
else
return $ret;
}
public function delete($sth){
$conn = Conn::open();
$ret = $conn->exec($sth);
if(PEAR::isError($ret))
return false;
else
return $ret;
}
}Oi @Juliano,
Agora o erro está evidente, e se encaixa com oque eu achei que fosse:
public function select($sth){
$conn = Conn::open();
//
}
public function selectAll($sth){
$conn = Conn::open();
//
}
public function insert($sth, $retLastId=false){
$conn = Conn::open();
//
}
public function update($sth){
$conn = Conn::open();
//
}
public function delete($sth){
$conn = Conn::open();
//
}
ou seja, todos os métodos dessa Class, chamam diretamente o método Conn::open()
Ok, mas ai vemos que:
public static function open()
{
//
$dsn = "$db_tipo://$user:$senha@$host/$dbName?charset=utf8";
$db = MDB2::factory($dsn);
ou seja, o método open, abre uma nova conexão cada vez que é chamado!!!!
Logo, se você precisar fazer isso aqui na aplicação:
$TSql->select();
$TSql->select();
$TSql->select();
o seu conjunto de classes vai abrir 3 conexões com o banco. Desnecessariamente. Com uma única conexão tudo seria resolvido.
Assim fica fácil conseguir um "too many connections", concorda ?
:lol:
como eu disse, tinha a haver com o pacote do pear...
checkmate
>
como eu disse, tinha a haver com o pacote do pear...
checkmate
? não tem absolutamente nada a ver com o pacote do PEAR.
E sim com um uso indiscriminado e incoerente de objetos e métodos. Você realmente entendeu a minha resposta ?
Mas o construtor checa se já existe conexão aberta, não é?
private function __construct()
{
if(empty(self::$conn))
{
self::$conn = Conn::open();
}
return self::$conn;
}
Se existir conexão ele retorna a conexão que está aberta, ou estou errado?
Fiz dessa forma justamente para aproveitar a conexão já aberta pelo script.
Você vê algum erro nessa lógica.
Obrigado e um abraço.
>
? não tem absolutamente nada a ver com o pacote do PEAR.
E sim com um uso indiscriminado e incoerente de objetos e métodos. Você realmente entendeu a minha resposta ?
sim, entendi, ele abre toda hora a conexao com o banco, mas você eh q nao entendeu a minha, como nao tem nada a ver com o pacote do pear?
kem faz as conexoes ao banco de dados? nao eh o mysqli...eh o mdb2, do pear, q ele usa se o erro diz q sao muitas conexoes ao banco, e eh o mdb2 q faz a conexao, o erro vai estar onde?
Mas o construtor checa se já existe conexão aberta, não é?
sim, ele checa, mas você não usa ele!
você usa o open direto!!
kem faz as conexoes ao banco de dados?
no caso dele é o mdb2
eh o mdb2 q faz a conexao, o erro vai estar onde?
o erro está na forma que ele está usando. E não no mdb2.Se ele usar dessa forma, então não importa se é mysqli, mysql_, pdo.. whatever, vai acontecer o mesmo problema.
logo, o "erro", a "culpa", não é do mdb2, e sim da forma que ele está usando.
Entendeu o ponto ?
Oque você disse, é o mesmo que dizer: "(O erro está no) |(A culpa é do) PHP".
Então qual seria a maneira correta de eu tratar o acesso ao banco?
Obrigado.
Essa "checagem" que você fez no construtor, você deveria ter feito no metodo ::open()
Isso tecnicamente "resolve" a questão.
Ou então, sei lá, em vez de usar:
$conn = Conn::open();
passar a usar:
$conn = self::$conn;//acho q era isso q quem criou o script queria
@opiniao_pessoal:
Não gostei do conjunto de classes, e nem da utilização delas.
Também acho que posso mudar o MDB2 de factory para singleton, não é?
Obrigado pela ajuda. Vou fazer alguns testes.
William, ainda estou no caminho do aprendizado e me bato muito na questão de orientação a objetos. Um dia chego lá.
Valeu ela ajuda.
abraços.
Tranquilo cara.
Use dessa forma aqui:
$conn = self::$conn;
assim você realmente usa a tua verificação. Não precisa checar nada no open não.
Basta não acessa-lo diretamente denovo.
Oi William.
Estou tentando fazer da forma que você disse, mas estou recebendo este erro:
Fatal error: Call to a member function query() on a non-object in xxxx.
class TSql{
private static $conn;
private function __construct()
{
if(empty(self::$conn))
{
self::$conn = Conn::open();
}
return self::$conn;
}
public function select($sth){
//$conn = Conn::open();
$conn = self::$conn;
$res = $conn->query($sth);
$ret = $res->fetchRow();
if(PEAR::isError($ret))
return false;
else
return $ret;
}
}
É uma propriedade estática. Não entendo o motivo do erro...
primeiro, construtor nao retorna nada...segundo, você nao vai conseguir usar uma propriedade estatica estando como um objeto comum...eu faria isto
class TSql{
private $conn = null;
private function __construct()
{
if(is_null($this->conn))
{
$this->conn = Conn::open();
}
}
public function select($sth){
$res = $this->conn->query($sth);
$ret = $res->fetchRow();
if(PEAR::isError($ret))
return false;
else
return $ret;
}
}
Posta a classe cuja instância está armazenada em $resposta e, adicionalmente a classe Crud.