Quais são as desvantagens do uso de conexão persistente no DOP


181

No PDO, uma conexão pode ser feita persistente usando o PDO::ATTR_PERSISTENTatributo De acordo com o manual php -

As conexões persistentes não são fechadas no final do script, mas são armazenadas em cache e reutilizadas quando outro script solicita uma conexão usando as mesmas credenciais. O cache de conexão persistente permite evitar a sobrecarga de estabelecer uma nova conexão sempre que um script precisar conversar com um banco de dados, resultando em um aplicativo da Web mais rápido.

O manual também recomenda não usar conexão persistente enquanto estiver usando o driver ODBC PDO, pois isso pode dificultar o processo de Pool de Conexão ODBC.

Então, aparentemente, parece não haver desvantagens em usar a conexão persistente no PDO, exceto no último caso. No entanto, gostaria de saber se existem outras desvantagens do uso desse mecanismo, ou seja, uma situação em que esse mecanismo resulta em degradação do desempenho ou algo parecido.


Uau, você pagou uma recompensa de 1000 representantes por essa pergunta simples?
Pacerier

Respostas:


287

Leia esta resposta abaixo , que detalha maneiras de atenuar os problemas descritos aqui.


As mesmas desvantagens existem usando o PDO, como em qualquer outra interface de banco de dados PHP que faz conexões persistentes: se o script terminar inesperadamente no meio das operações do banco de dados, a próxima solicitação que obtiver a conexão restante continuará de onde o script morto parou. A conexão é mantida aberta no nível do gerenciador de processos (Apache para mod_php, o processo FastCGI atual, se você estiver usando FastCGI, etc), não no nível PHP, e o PHP não diz ao processo pai para deixar a conexão morrer quando o script termina de forma anormal.

Se o script inoperante bloqueou as tabelas, essas tabelas permanecerão bloqueadas até que a conexão morra ou o próximo script que obtém a conexão desbloqueie as próprias tabelas.

Se o script morto estiver no meio de uma transação, isso poderá bloquear várias tabelas até que o cronômetro de deadlock entre em ação e, mesmo assim, o cronômetro de deadlock poderá eliminar a solicitação mais recente em vez da solicitação mais antiga que está causando o problema.

Se o script morto estava no meio de uma transação, o próximo script que obtém essa conexão também obtém o estado da transação. É muito possível (dependendo do design do aplicativo) que o próximo script nunca tente confirmar a transação existente, ou confirme quando não deveria, ou reverta quando não deveria.

Esta é apenas a ponta do iceberg. Tudo isso pode ser atenuado até um certo ponto, sempre tentando limpar após uma conexão suja em cada solicitação de script, mas isso pode ser um problema, dependendo do banco de dados. A menos que você tenha identificado a criação de conexões de banco de dados como a única coisa que é um gargalo no seu script (isso significa que você tem o código feito de perfis usando xdebug e / ou xhprof ), você deve não considerar conexões persistentes como uma solução para nada.

Além disso, a maioria dos bancos de dados modernos (incluindo o PostgreSQL) tem suas próprias maneiras preferidas de executar o pool de conexões que não têm as desvantagens imediatas que as conexões persistentes simples baseadas em PHP.


Para esclarecer um ponto, usamos conexões persistentes no meu local de trabalho, mas não por opção. Estávamos encontrando um comportamento estranho de conexão, em que a conexão inicial do servidor de aplicativos com o servidor de banco de dados estava levando exatamente três segundos, quando deveria levar uma fração de uma fração de segundo. Achamos que é um bug do kernel. Desistimos de tentar solucionar o problema, porque isso aconteceu aleatoriamente e não pôde ser reproduzido sob demanda, e nossa TI terceirizada não tinha a capacidade concreta de localizá-lo.

Independentemente disso, quando as pessoas no armazém estão processando algumas centenas de peças recebidas e cada uma delas leva três segundos e meio em vez de meio segundo, tivemos que agir antes que eles nos sequestrassem e nos fizessem ajudá-los. Por isso, lançamos alguns detalhes em nossa monstruosidade ERP / CRM / CMS, criada em casa, e experimentamos todos os horrores de conexões persistentes em primeira mão. Levamos semanas para rastrear todos os pequenos problemas sutis e comportamentos bizarros que aconteciam aparentemente ao acaso. Aconteceu que aqueles erros fatais que uma vez por semana os nossos usuários retiravam diligentemente do nosso aplicativo estavam deixando tabelas bloqueadas, transações abandonadas e outros estados infelizes.

Essa triste história tem um ponto: quebrou coisas que nunca esperávamos quebrar, tudo em nome da performance. A troca não valeu a pena, e estamos aguardando ansiosamente o dia em que possamos voltar às conexões normais sem uma revolta de nossos usuários.


2
Espero ter lido esta resposta antes de executar #SELECT orders.* FROM orders LEFT JOIN items USING(item_id)
214 Ast Derek

31
Conheço um grande site que usa conexões persistentes há quase uma década. O truque é usar uma camada acima da extensão do banco de dados e lembrar as coisas que precisam ser limpas usando register_shutdown_function(). Se o processo morrer, a conexão também morrerá. Caso contrário, a conexão é redefinida para seu estado limpo (por exemplo, transações abertas são revertidas). Se isso falhar, a conexão será fechada e uma nova será aberta pela próxima solicitação ao mesmo processo. Não há necessidade de demonizar conexões persistentes.
Walter Tross 31/03

Estou curioso @ Charles ... seu problema já foi resolvido?
Tschallacka

@MichaelDibbets Substituímos o servidor de aplicativos há alguns meses e desligamos o pconnect para ver se o bug de três segundos ainda estava por aí. Não foi. Foi resolvido por procuração, suponho. A resposta abaixo em relação a mysqli_change_userainda é provavelmente a melhor solução alternativa para pessoas que precisam fazer conexões persistentes em um aplicativo não projetado para lidar com problemas de estado.
Charles

5
Tivemos um atraso de 5 segundos na conexão, que conseguimos isolar como um problema de DNS + IPv6. O servidor estava procurando um endereço v6, com falha e, em seguida, usando o endereço IPv4.
Nigel Atkinson

45

Em resposta ao problema de Charles acima,

De: http://www.php.net/manual/en/mysqli.quickstart.connections.php -

Uma reclamação comum sobre conexões persistentes é que seu estado não é redefinido antes da reutilização. Por exemplo, transações abertas e inacabadas não são revertidas automaticamente. Além disso, as alterações de autorização que ocorreram no período entre colocar a conexão no pool e reutilizá-la não são refletidas. Isso pode ser visto como um efeito colateral indesejado. Pelo contrário, o nome persistente pode ser entendido como uma promessa de que o estado é persistente.

A extensão mysqli suporta as duas interpretações de uma conexão persistente: state persisted e state reset antes da reutilização. O padrão é redefinido. Antes de uma conexão persistente ser reutilizada, a extensão mysqli chama implicitamente mysqli_change_user()para redefinir o estado. A conexão persistente aparece para o usuário como se tivesse acabado de ser aberta. Nenhum artefato de usos anteriores é visível.

A mysqli_change_user()função é uma operação cara. Para um melhor desempenho, os usuários podem recompilar a extensão com o sinalizador de compilação MYSQLI_NO_CHANGE_USER_ON_PCONNECTsendo definido.

Cabe ao usuário escolher entre comportamento seguro e melhor desempenho. Ambos são objetivos de otimização válidos. Para facilitar o uso, o comportamento seguro tornou-se o padrão à custa do desempenho máximo.


+1, se não fosse o fato de termos limpado a bagunça de outras maneiras, eu adoraria ver se chamar manualmente change_user teria corrigido nossos problemas bizarros de estado desconhecido.
Charles

Qual é o equivalente para as conexões persistentes do PDO Postgres? Eu tenho problemas semelhantes ao @Charles, onde, depois de um tempo, os usuários estavam recebendo erros como buscar sql - o servidor encerrou a conexão inesperadamente Isso provavelmente significa que o servidor foi encerrado de forma anormal Ao executar uma simples consulta SELECT (nem mesmo transações).
Carmageddon

1
@ Carmageddon, isso é mais adequado para uma nova pergunta, mas o tl; dr é que o Postgres não desconecta e você deve usar um dos pools de conexões externas.
Charles Charles

@ Charles, o que você quer dizer com isso? usar a conexão persistente do PDO não é equivalente a usar "conjuntos de conexões externas"? ou o que você quis dizer?
Carmageddon

@Carmageddon, o que quero dizer é que a comunidade do Postgres decidiu usar o pool de conexões como uma solução melhor do que o pconnect. Confira pgbouncer ou pgpool-II. Não tenho certeza de que o PDO desconecte o Postgres de qualquer maneira, mas posso estar totalmente doido.
Charles

13

Conexões persistentes são uma boa idéia apenas quando leva um tempo (relativamente) longo para se conectar ao seu banco de dados. Hoje em dia quase nunca é esse o caso. A maior desvantagem das conexões persistentes é que isso limita o número de usuários que você pode ter navegando no seu site: se o MySQL estiver configurado para permitir apenas 10 conexões simultâneas ao mesmo tempo, quando uma 11ª pessoa tentar navegar no site, isso não funcionará para eles. .

O PDO não gerencia a persistência. O driver MySQL faz. Ele reutiliza conexões quando a) elas estão disponíveis e o host / usuário / senha / banco de dados corresponde. Se houver alguma alteração, não reutilizará uma conexão. O melhor efeito líquido é que essas conexões que você tem serão iniciadas e interrompidas com frequência porque você tem usuários diferentes no site e torná-los persistentes não adianta.

A principal coisa a entender sobre conexões persistentes é que você NÃO deve usá-las na maioria dos aplicativos da web. Eles parecem atraentes, mas são perigosos e praticamente inúteis.

Tenho certeza de que existem outros threads, mas uma conexão persistente é perigosa porque persiste entre solicitações. Se, por exemplo, você bloquear uma tabela durante uma solicitação e falhar ao desbloquear, essa tabela permanecerá bloqueada indefinidamente. As conexões persistentes também são praticamente inúteis para 99% dos seus aplicativos, porque você não tem como saber se a mesma conexão será usada entre solicitações diferentes. Cada encadeamento da web terá seu próprio conjunto de conexões persistentes e você não terá como controlar qual encadeamento manipulará quais solicitações.

A biblioteca procedural do mysql do PHP possui um recurso pelo qual chamadas subseqüentes ao mysql_connect retornarão o mesmo link, em vez de abrir uma conexão diferente (como se poderia esperar). Isso não tem nada a ver com conexões persistentes e é específico para a biblioteca mysql. O DOP não exibe esse comportamento


Link do recurso: link

Em geral, você pode usar isso como um "conjunto de regras" aproximado:

SIM , use conexões persistentes, se:

  • Existem apenas alguns aplicativos / usuários acessando o banco de dados, ou seja, você não resultará em 200 conexões abertas (mas provavelmente inativas), porque existem 200 usuários diferentes compartilhados no mesmo host.
  • O banco de dados está sendo executado em outro servidor que você está acessando pela rede

  • Um (um) aplicativo acessa o banco de dados com muita frequência

NÃO , não use conexões persistentes, se:

  • Seu aplicativo precisa acessar o banco de dados apenas 100 vezes por hora.

  • Você tem muitos servidores da web acessando um servidor de banco de dados

O uso de conexões persistentes é consideravelmente mais rápido, especialmente se você estiver acessando o banco de dados através de uma rede. Não faz muita diferença se o banco de dados estiver sendo executado na mesma máquina, mas ainda é um pouco mais rápido. No entanto - como o nome diz - a conexão é persistente, ou seja, permanece aberta, mesmo que não seja usada.

O problema é que, na "configuração padrão", o MySQL permite apenas 1000 "canais abertos" paralelos. Depois disso, novas conexões são recusadas (você pode ajustar essa configuração). Portanto, se você tiver, digamos, 20 servidores da Web com cada 100 clientes, e cada um deles tiver apenas um acesso à página por hora, uma matemática simples mostrará que você precisará de 2000 conexões paralelas ao banco de dados. Isso não vai funcionar.

Ergo: use-o apenas para aplicativos com muitas solicitações.


4
Depois de linha de sua resposta é copiar colar de stackoverflow.com/a/51583/718224
Tony Stark

1
"SIM, use conexões persistentes, se: [...] Existem poucos aplicativos / usuários acessando o banco de dados" contradiz com "Use apenas para aplicativos com muitas solicitações". Este último está correto. Situação: milhares de solicitações por segundo resultarão em centenas de conexões ativas com o banco de dados. Quando um sistema dimensiona linearmente, também dimensiona linearmente a quantidade de conexões com o banco de dados. Portanto, mais solicitações (mais usuários) resultarão em mais conexões. Então você precisa se limitando ainda muitas conexões ativas quando você tem um monte de pedidos (usuários) (!)
Ken Van Hoeylandt

12

Nos meus testes, eu tive um tempo de conexão de mais de um segundo no meu host local, assumindo, portanto, que eu deveria usar uma conexão persistente. Testes adicionais mostraram que havia um problema com o 'localhost':

Resultados do teste em segundos (medido pelo php microtime):

  • Web hospedada: connectDB: 0.0038912296295166
  • localhost: connectDB: 1.0214691162109 (mais de um segundo: não use localhost!)
  • 127.0.0.1: connectDB: 0.00097203254699707

Curiosamente: O código a seguir é tão rápido quanto usar 127.0.0.1:

$host = gethostbyname('localhost');
// echo "<p>$host</p>";
$db = new PDO("mysql:host=$host;dbname=" . DATABASE . ';charset=utf8', $username, $password,
    array(PDO::ATTR_EMULATE_PREPARES => false,
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

Parece que a DOP tem dificuldade em traduzir nomes de domínio! Obrigado, eu queria saber por que cada conexão estava demorando muito na minha máquina quad core!
Mustafa

@Gunnar Bernstein +1 bom achado. O "localhost" certamente leva mais tempo e isso melhorou um pouco a velocidade do meu aplicativo da web (faz muitas conexões).
precisa

1
Isso é ótimo. Algo está errado com resolução em minha máquina de desenvolvimento ... usando um IP tomou meu script de 6.1s para 1.1s
Pete

localhostconexão usa soquetes, conexão de soquete é famoso para ser mau em grande quantidade de conexões
mente

@mente Qualquer referência, recurso que possa provar esse fato? Eu tenderia a pensar que os UDS são preferidos ao TCP. Obrigado.
Nuxwin 27/08

6

Conexões persistentes devem dar um aumento considerável no desempenho. Não concordo com a afirmação de que você deve "Evitar" a persistência.

Parece que as reclamações acima são motivadas por alguém usando tabelas MyIASM e invadindo suas próprias versões de transações, bloqueando os bloqueios de tabelas. Bem, é claro que você vai entrar em conflito! Use o beginTransaction () do PDO e mova suas tabelas para o InnoDB.


2
Sei que estou com um ano de atraso, mas para constar: minha história vem de um banco de dados composto inteiramente de tabelas InnoDB, com a única exceção de um punhado de clones desnormalizados presos no atoleiro do MyISAM para suporte à indexação de texto completo.
Charles

Pfft, Sphinx é antiga e estourada, ElasticSearch é a nova moda. Um belo dia, vamos usá-lo para nossos aplicativos antigos, em vez de apenas os novos ... #
Charles Charles

A pesquisa de texto completo no PostgreSQL é o verdadeiro vencedor. É incrível. Não requer outra ferramenta / servidor em execução para fazer seu trabalho. Não precisa se preocupar em manter os dados sincronizados. Controles muito granulares. Vários dicionários ou escreva o seu. E como o PostgreSQL usa automaticamente consultas com vários índices, você pode inseri-lo em qualquer outra consulta em execução.
brightball

2
O MySQL 5.6 oferece suporte a texto completo para tabelas InnoDB.
timetofly

2

Parece-me que ter uma conexão persistente consumiria mais recursos do sistema. Talvez uma quantia trivial, mas ainda assim ...


Muitas vezes um comércio de lotes do tempo humano para microssegundos de tempo de computador
Andy Chase

1

A explicação para o uso de conexões persistentes está obviamente reduzindo a quantidade de conexões que são bastante caras, apesar do fato de serem consideravelmente mais rápidas com o MySQL em comparação com outros bancos de dados.

O primeiro problema com conexões persistentes ...

Se você estiver criando milhares de conexões por segundo, normalmente não garante que ele permaneça aberto por muito tempo, mas o Sistema Operacional o faz. Com base no protocolo TCP / IP, as portas não podem ser recicladas instantaneamente e também precisam investir um tempo no estágio "FIN" aguardando antes de serem recicladas.

O segundo problema ... usando muitas conexões do servidor MySQL.

Muitas pessoas simplesmente não percebem que você é capaz de aumentar a variável * max_connections * e obter mais de 100 conexões simultâneas com o MySQL, outras foram derrotadas por problemas mais antigos do Linux, devido à incapacidade de transmitir mais de 1024 conexões com o MySQL.

Permite falar agora sobre por que as conexões persistentes foram desativadas na extensão mysqli. Apesar de você poder usar mal as conexões persistentes e obter um desempenho ruim, esse não foi o principal motivo. O motivo real é - você pode obter muito mais problemas com ele.

Conexões persistentes foram colocadas no PHP durante o MySQL 3.22 / 3.23, quando o MySQL não era tão difícil, o que significa que você pode reciclar conexões facilmente sem problemas. No entanto, em versões posteriores, ocorreu a quantidade de problemas - Se você reciclar a conexão que possui transações não confirmadas, terá problemas. Se você reciclar conexões com configurações personalizadas de conjuntos de caracteres, estará em perigo novamente, bem como sobre possíveis variáveis ​​transformadas por sessão.

Um problema com o uso de conexões persistentes é que ele não é dimensionado tão bem. Para quem tem 5.000 pessoas conectadas, você precisará de 5.000 conexões persistentes. Para afastar o requisito de persistência, você pode atender a 10000 pessoas com quantidade semelhante de conexões, porque elas estão em posição de compartilhar conexões individuais quando não estão com elas.


0

Eu só estava me perguntando se uma solução parcial seria ter um pool de conexões de uso único. Você pode gastar tempo criando um pool de conexões quando o sistema estiver com pouco uso, até um limite, entregá-los e eliminá-los quando eles estiverem concluídos ou atingidos o tempo limite. Em segundo plano, você cria novas conexões à medida que elas são tomadas. Na pior das hipóteses, isso deve ser tão lento quanto criar a conexão sem o pool, assumindo que o estabelecimento do link seja o fator limitante?

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.