{"id":35259,"date":"2019-10-31T22:03:17","date_gmt":"2019-10-31T19:03:17","guid":{"rendered":"https:\/\/prohoster.info\/blog\/istoriya-odnogo-sql-rassledovaniya\/"},"modified":"2019-10-31T22:03:17","modified_gmt":"2019-10-31T19:03:17","slug":"istoriya-odnogo-sql-rassledovaniya","status":"publish","type":"post","link":"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya","title":{"rendered":"A hist\u00f3ria de uma investiga\u00e7\u00e3o SQL","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Em dezembro do ano passado, recebi um relat\u00f3rio interessante sobre um erro da equipe de suporte da VWO. O tempo de carregamento de um dos relat\u00f3rios anal\u00edticos para um grande cliente corporativo parecia excessivamente alto. Como essa \u00e9 minha \u00e1rea de responsabilidade, imediatamente me concentrei em resolver o problema.<\/p>\n<p><\/p>\n<h2>Antecedentes<\/h2>\n<p><\/p>\n<p>Para que fique claro sobre o que estou falando, vou contar um pouco sobre a VWO. \u00c9 uma plataforma que permite lan\u00e7ar diferentes campanhas segmentadas em seus sites: realizar testes A\/B, rastrear visitantes e convers\u00f5es, fazer an\u00e1lise de funil de vendas, exibir mapas de calor e reproduzir grava\u00e7\u00f5es de visitas.<\/p>\n<p><\/p>\n<p>Mas o mais importante na plataforma \u00e9 a gera\u00e7\u00e3o de relat\u00f3rios. Todas as fun\u00e7\u00f5es mencionadas est\u00e3o interligadas. Para clientes corporativos, uma enorme quantidade de informa\u00e7\u00f5es seria simplesmente in\u00fatil sem uma plataforma poderosa que as apresentasse em uma forma \u00fatil para an\u00e1lise.<\/p>\n<p><\/p>\n<p>Usando a plataforma, \u00e9 poss\u00edvel fazer consultas arbitr\u00e1rias em um grande conjunto de dados. Aqui est\u00e1 um exemplo simples:<\/p>\n<p><\/p>\n<pre>Mostrar todos os cliques na p\u00e1gina \"abc.com\" DE &lt;data d1&gt; AT\u00c9 &lt;data d2&gt; para pessoas que usaram Chrome OU (estiveram na Europa E usaram iPhone)<\/pre>\n<p><\/p>\n<p>Observe os operadores booleanos. Eles est\u00e3o dispon\u00edveis para clientes na interface de consulta, permitindo fazer consultas complexas para obter amostras.<\/p>\n<p><\/p>\n<h2>Consulta lenta<\/h2>\n<p><\/p>\n<p>O cliente em quest\u00e3o estava tentando fazer algo que intuitivamente deveria funcionar rapidamente:<\/p>\n<p><\/p>\n<pre>Mostre todos os registros de sess\u00f5es para usu\u00e1rios que visitaram qualquer p\u00e1gina com a URL contendo \"\/jobs\"<\/pre>\n<p><\/p>\n<p>Este site tinha um enorme volume de tr\u00e1fego, e armazen\u00e1vamos mais de um milh\u00e3o de URLs \u00fanicas apenas para ele. E eles queriam encontrar um padr\u00e3o de URL bastante simples relacionado ao seu modelo de neg\u00f3cios.<\/p>\n<p>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Investiga\u00e7\u00e3o preliminar<\/h2>\n<p><\/p>\n<p>Vamos ver o que est\u00e1 acontecendo no banco de dados. Abaixo est\u00e1 a consulta SQL lenta original:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">SELECT \n    count(*) \nFROM \n    acc_{account_id}.urls as recordings_urls, \n    acc_{account_id}.recording_data as recording_data, \n    acc_{account_id}.sessions as sessions \nWHERE \n    recording_data.usp_id = sessions.usp_id \n    AND sessions.referrer_id = recordings_urls.id \n    AND  (  urls &amp;&amp;  array(select id from acc_{account_id}.urls where url  ILIKE  '%enterprise_customer.com\/jobs%')::text[]   ) \n    AND r_time &gt; to_timestamp(1542585600) \n    AND r_time = 5 \n    AND recording_data.num_of_pages &gt; 0 ;<\/code><\/pre>\n<p><\/p>\n<p>Aqui est\u00e3o os tempos:<\/p>\n<p><\/p>\n<pre>Tempo estimado: 1.480 ms\nTempo de execu\u00e7\u00e3o: 1431924.650 ms<\/pre>\n<p><\/p>\n<p>A consulta percorreu 150 mil linhas. O planejador de consultas destacou alguns detalhes interessantes, mas n\u00e3o havia gargalos \u00f3bvios.<\/p>\n<p><\/p>\n<p>Vamos continuar analisando a consulta. Como pode ser visto, ela faz <code>JOIN<\/code> tr\u00eas tabelas:<\/p>\n<p><\/p>\n<ol>\n<li><strong>sessions<\/strong>: para exibir informa\u00e7\u00f5es da sess\u00e3o: navegador, agente do usu\u00e1rio, pa\u00eds e assim por diante.<\/li>\n<li><strong>recording_data<\/strong>: URLs gravados, p\u00e1ginas, dura\u00e7\u00e3o das visitas<\/li>\n<li><strong>urls<\/strong>: para evitar a duplica\u00e7\u00e3o de URLs extremamente longas, as armazenamos em uma tabela separada.<\/li>\n<\/ol>\n<p><\/p>\n<p>Observe tamb\u00e9m que todas as nossas tabelas j\u00e1 est\u00e3o divididas por <code>account_id<\/code>. Dessa forma, elimina-se a situa\u00e7\u00e3o em que um \u00fanico conta muito grande causa problemas para as outras.<\/p>\n<p><\/p>\n<h2>Em busca de pistas<\/h2>\n<p><\/p>\n<p>Ao analisar mais de perto, vemos que algo est\u00e1 errado em uma consulta espec\u00edfica. Vale a pena prestar aten\u00e7\u00e3o nesta linha:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">urls &amp;&amp; array(\n\tselect id from acc_{account_id}.urls \n\twhere url ILIKE '%enterprise_customer.com\/jobs%'\n)::text[]<\/code><\/pre>\n<p><\/p>\n<p>O primeiro pensamento foi que talvez, por causa do <code>ILIKE<\/code> em todas essas URLs longas (temos mais de 1,4 milh\u00e3o de <strong>URLs \u00fanicas, coletadas para esta conta) o desempenho poderia estar comprometido.\u00a0<\/strong>Mas n\u00e3o, n\u00e3o \u00e9 isso!<\/p>\n<p><\/p>\n<p>SELECT id FROM urls WHERE url ILIKE '%enterprise_customer.com\/jobs%';\n  id\n--------\n ...\n(198661 linhas)\n\nTempo: 5231.765 ms<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">A pr\u00f3pria consulta de busca por padr\u00e3o leva apenas 5 segundos. A busca por padr\u00e3o em um milh\u00e3o de URLs \u00fanicas claramente n\u00e3o \u00e9 um problema.<\/code><\/pre>\n<p><\/p>\n<p>O pr\u00f3ximo suspeito na lista s\u00e3o alguns<\/p>\n<p><\/p>\n<p>. Ser\u00e1 que seu uso excessivo levou a uma desacelera\u00e7\u00e3o? Normalmente <code>JOIN<\/code>\u2018as s\u00e3o os candidatos mais \u00f3bvios para problemas de desempenho, mas eu n\u00e3o acreditava que nosso caso fosse t\u00edpico. <code>JOIN<\/code>\u2018Eles s\u00e3o os candidatos mais \u00f3bvios a problemas de desempenho, mas eu n\u00e3o acreditava que o nosso caso fosse t\u00edpico.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">E esse tamb\u00e9m n\u00e3o era o nosso caso.<\/code><\/pre>\n<p><\/p>\n<p>\u2018as se mostraram bastante r\u00e1pidas. <code>JOIN<\/code>\u2018Eles se mostraram bastante r\u00e1pidos.<\/p>\n<p><\/p>\n<h2>Eu estava preparado para come\u00e7ar a modificar a consulta para alcan\u00e7ar quaisquer melhorias de desempenho poss\u00edveis. N\u00f3s da equipe desenvolvemos duas ideias principais:<\/h2>\n<p><\/p>\n<p>Usar EXISTS para a subconsulta de URL<\/p>\n<p><\/p>\n<ul>\n<li><strong>: Quer\u00edamos verificar mais uma vez se havia problemas com a subconsulta das URLs. Uma das maneiras de conseguir isso \u00e9 simplesmente usar<\/strong>: Quer\u00edamos verificar mais uma vez se h\u00e1 problemas com a subconsulta para URLs. Uma das maneiras de fazer isso \u00e9 simplesmente usar <code>EXISTS<\/code>. <code>EXISTS<\/code> <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/functions-subquery.html#FUNCTIONS-SUBQUERY-EXISTS\">pode<\/a><\/noindex> melhorar significativamente o desempenho, pois termina imediatamente assim que encontra uma \u00fanica linha que atende \u00e0 condi\u00e7\u00e3o.<\/li>\n<\/ul>\n<p><\/p>\n<pre><code class=\"plaintext\">SELECT\n\tcount(*) \nFROM \n    acc_{account_id}.urls as recordings_urls,\n    acc_{account_id}.recording_data as recording_data,\n    acc_{account_id}.sessions as sessions\nWHERE\n    recording_data.usp_id = sessions.usp_id\n    AND  (  1 = 1  )\n    AND sessions.referrer_id = recordings_urls.id\n    AND  (exists(select id from acc_{account_id}.urls where url  ILIKE '%enterprise_customer.com\/jobs%'))\n    AND r_time &gt; to_timestamp(1547585600)\n    AND r_time =5\n    AND recording_data.num_of_pages &gt; 0 ;\n count\n 32519\n(1 row)\nTime: 1636.637 ms<\/code><\/pre>\n<p><\/p>\n<p>Sim. A subconsulta, quando envolta em\u00a0<code>EXISTS<\/code>, torna tudo super r\u00e1pido. A pr\u00f3xima pergunta l\u00f3gica \u00e9 por que a consulta com <code>JOIN<\/code>-s e a pr\u00f3pria subconsulta s\u00e3o r\u00e1pidas separadamente, mas funcionam muito lentamente juntas?<\/p>\n<p><\/p>\n<ul>\n<li><strong>Movemos a subconsulta para CTE <\/strong>: se a consulta \u00e9 r\u00e1pida por si s\u00f3, podemos primeiro calcular o resultado r\u00e1pido e, em seguida, fornec\u00ea-lo \u00e0 consulta principal.<\/li>\n<\/ul>\n<p><\/p>\n<pre><code class=\"plaintext\">WITH matching_urls AS (\n    select id::text from acc_{account_id}.urls where url  ILIKE  '%enterprise_customer.com\/jobs%'\n)\n\nSELECT \n    count(*) FROM acc_{account_id}.urls as recordings_urls, \n    acc_{account_id}.recording_data as recording_data, \n    acc_{account_id}.sessions as sessions,\n    matching_urls\nWHERE \n    recording_data.usp_id = sessions.usp_id \n    AND  (  1 = 1  )  \n    AND sessions.referrer_id = recordings_urls.id\n    AND (urls &amp;&amp; array(SELECT id from matching_urls)::text[])\n    AND r_time &gt; to_timestamp(1542585600) \n    AND r_time =5 \n    AND recording_data.num_of_pages &gt; 0;<\/code><\/pre>\n<p><\/p>\n<p>Mas isso ainda estava muito lento.<\/p>\n<p><\/p>\n<h2>Encontramos o culpado<\/h2>\n<p><\/p>\n<p>Durante todo esse tempo, uma pequena coisa ficou piscando na minha frente, da qual eu sempre tentei me desfazer. Mas como n\u00e3o havia mais nada a fazer, decidi dar uma olhada nela. Estou falando do <code>&amp;&amp;<\/code> operador. Enquanto <code>EXISTS<\/code> apenas melhorou o desempenho, <code>&amp;&amp;<\/code> era o \u00fanico fator comum restante em todas as vers\u00f5es da consulta lenta.<\/p>\n<p><\/p>\n<p>Olhando para <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/9.1\/functions-array.html\">documenta\u00e7\u00e3o<\/a><\/noindex>, vemos que <code>&amp;&amp;<\/code> \u00e9 usado quando precisamos encontrar elementos comuns entre dois arrays.<\/p>\n<p><\/p>\n<p>Na consulta original, isso \u00e9:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">AND  (  urls &amp;&amp; array(select id from acc_{account_id}.urls where url  ILIKE  '%enterprise_customer.com\/jobs%')::text[]   )<\/code><\/pre>\n<p><\/p>\n<p>O que significa que estamos fazendo uma busca por padr\u00e3o em nossos URLs, e ent\u00e3o encontramos a interse\u00e7\u00e3o com todos os URLs com registros comuns. Isso \u00e9 um pouco confuso, pois 'urls' aqui n\u00e3o se refere \u00e0 tabela que cont\u00e9m todos os URLs, mas \u00e0 coluna 'urls' na tabela <code>recording_data<\/code>.<\/p>\n<p><\/p>\n<p>Com o aumento das suspeitas sobre <code>&amp;&amp;<\/code>, tentei encontrar confirma\u00e7\u00e3o delas no plano de consulta gerado <code>EXPLAIN ANALYZE<\/code> (eu j\u00e1 tinha um plano salvo, mas geralmente prefiro experimentar no SQL do que tentar entender as opacidades dos otimizadores de consultas).<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">Filtro: ((urls &amp;&amp; ($0)::text[]) E (r_time &gt; '2018-12-17 12:17:23+00'::timestamp com fuso hor\u00e1rio) E (r_time = '5'::double precision) E (num_of_pages &gt; 0))\n                           Linhas removidas pelo filtro: 52710<\/code><\/pre>\n<p><\/p>\n<p>Havia v\u00e1rias linhas de filtros somente de <code>&amp;&amp;<\/code>. O que significava que essa opera\u00e7\u00e3o n\u00e3o apenas era cara, mas tamb\u00e9m era executada v\u00e1rias vezes.<\/p>\n<p><\/p>\n<p>Verifiquei isso, isolando a condi\u00e7\u00e3o<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">SELECT 1\nFROM \n    acc_{account_id}.urls como recordings_urls, \n    acc_{account_id}.recording_data_30 como recording_data_30, \n    acc_{account_id}.sessions_30 como sessions_30 \nWHERE \n\turls &amp;&amp; array(select id from acc_{account_id}.urls where url ILIKE '%enterprise_customer.com\/jobs%')::text[]<\/code><\/pre>\n<p><\/p>\n<p>Essa consulta estava sendo executada lentamente. Como <code>JOIN<\/code>-s s\u00e3o r\u00e1pidos e subconsultas s\u00e3o r\u00e1pidas, s\u00f3 restava <code>&amp;&amp;<\/code> o operador.<\/p>\n<p><\/p>\n<p>Apenas essa \u00e9 a opera\u00e7\u00e3o chave. Precisamos sempre pesquisar em toda a tabela principal de URLs para buscar por padr\u00e3o, e sempre precisamos encontrar interse\u00e7\u00f5es. N\u00e3o podemos pesquisar registros de URLs diretamente, porque s\u00e3o apenas IDs que se referenciam a <code>urls<\/code>.<\/p>\n<p><\/p>\n<h2>No caminho para a solu\u00e7\u00e3o<\/h2>\n<p><\/p>\n<p><code>&amp;&amp;<\/code> lento, porque ambos os conjuntos s\u00e3o enormes. A opera\u00e7\u00e3o ser\u00e1 relativamente r\u00e1pida se eu substituir <code>urls<\/code> para <code>{ \"http:\/\/google.com\/\", \"http:\/\/wingify.com\/\" }<\/code>.<\/p>\n<p><\/p>\n<p>Comecei a procurar uma maneira de fazer em Postgres interse\u00e7\u00f5es de conjuntos sem usar <code>&amp;&amp;<\/code>, mas sem muito sucesso.<\/p>\n<p><\/p>\n<p>No final, decidimos simplesmente resolver o problema isoladamente: me d\u00ea todas as <code>urls<\/code> linhas, para as quais a url corresponde ao padr\u00e3o. Sem condi\u00e7\u00f5es adicionais, ser\u00e1 \u2014\u00a0<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">SELECT urls.url\nFROM \n\tacc_{account_id}.urls como urls,\n\t(SELECT unnest(recording_data.urls) AS id) COMO unrolled_urls\nWHERE\n\turls.id = unrolled_urls.id E\n\turls.url ILIKE '%jobs%'<\/code><\/pre>\n<p><\/p>\n<p>Em vez de\u00a0<code>JOIN<\/code> sem\u00e2ntica, eu simplesmente usei uma subconsulta e desenrolei <code>recording_data.urls<\/code> o array, para que pud\u00e9ssemos aplicar a condi\u00e7\u00e3o diretamente em <code>WHERE<\/code>.<\/p>\n<p><\/p>\n<p>O que \u00e9 mais importante aqui \u00e9 que <code>&amp;&amp;<\/code> \u00e9 usado para verificar se o registro cont\u00e9m a URL correspondente. Piscar os olhos um pouco pode permitir ver nesta opera\u00e7\u00e3o o deslocamento pelos elementos do array (ou linhas da tabela) e a parada quando a condi\u00e7\u00e3o (correspond\u00eancia) \u00e9 atendida. Isso soa familiar? Aham, <code>EXISTS<\/code>.<\/p>\n<p><\/p>\n<p>Como <code>recording_data.urls<\/code> pode ser referenciado fora do contexto da subconsulta, quando isso acontece, podemos voltar ao nosso velho amigo <code>EXISTS<\/code> e envolv\u00ea-lo na subconsulta.<\/p>\n<p><\/p>\n<p>Juntando tudo, obtemos a consulta otimizada final:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">SELECIONE \n    count(*) \nDE \n    acc_{account_id}.urls como recordings_urls, \n    acc_{account_id}.recording_data como recording_data, \n    acc_{account_id}.sessions como sessions \nONDE \n    recording_data.usp_id = sessions.usp_id \n    E  (  1 = 1  )  \n    E sessions.referrer_id = recordings_urls.id \n    E r_time &gt; to_timestamp(1542585600) \n    E r_time =5 \n    E recording_data.num_of_pages &gt; 0\n    E EXISTE(\n        SELECIONE urls.url\n        DE \n            acc_{account_id}.urls como urls,\n            (SELECIONE unnest(urls) COMO rec_url_id DE acc_{account_id}.recording_data) \n            COMO unrolled_urls\n        ONDE\n            urls.id = unrolled_urls.rec_url_id E\n            urls.url  ILIKE  '%enterprise_customer.com\/jobs%'\n    );\n<\/code><\/pre>\n<p><\/p>\n<p>E o tempo final de execu\u00e7\u00e3o <code>Tempo: 1898.717 ms<\/code> \u00c9 hora de comemorar?!?<\/p>\n<p><\/p>\n<p>N\u00e3o t\u00e3o r\u00e1pido! Primeiro, \u00e9 preciso verificar a precis\u00e3o. Eu estava extremamente c\u00e9tico em rela\u00e7\u00e3o \u00e0 <code>EXISTS<\/code> otimiza\u00e7\u00e3o, pois muda a l\u00f3gica para um t\u00e9rmino mais antecipado. Precisamos ter certeza de que n\u00e3o adicionamos um erro n\u00e3o \u00f3bvio na consulta.<\/p>\n<p><\/p>\n<p>Uma verifica\u00e7\u00e3o simples consistiu em executar <code>count(*)<\/code> tanto em consultas lentas quanto r\u00e1pidas para um grande n\u00famero de diferentes conjuntos de dados. Em seguida, para um pequeno subconjunto de dados, verifiquei a corre\u00e7\u00e3o de todos os resultados manualmente.<\/p>\n<p><\/p>\n<p>Todas as verifica\u00e7\u00f5es resultaram consistentemente positivas. N\u00f3s consertamos tudo!<\/p>\n<p><\/p>\n<h2>Li\u00e7\u00f5es Aprendidas<\/h2>\n<p><\/p>\n<p>Podem ser extra\u00eddas muitas li\u00e7\u00f5es dessa hist\u00f3ria:<\/p>\n<p><\/p>\n<ol>\n<li>Os planos de consulta n\u00e3o contam toda a hist\u00f3ria, mas podem dar dicas<\/li>\n<li>Os principais suspeitos nem sempre s\u00e3o os verdadeiros culpados<\/li>\n<li>Consultas lentas podem ser divididas para isolar gargalos<\/li>\n<li>Nem todas as otimiza\u00e7\u00f5es s\u00e3o, por natureza, redutivas<\/li>\n<li>Uso <code>EXIST<\/code>, onde poss\u00edvel, pode levar a um crescimento exponencial no desempenho<\/li>\n<\/ol>\n<p><\/p>\n<h2>Sa\u00edda<\/h2>\n<p><\/p>\n<p>Passamos de um tempo de consulta de ~24 minutos para 2 segundos \u2014 um aumento de desempenho bastante significativo! Embora este artigo tenha se tornado extenso, todos os experimentos que realizamos ocorreram em um \u00fanico dia, e estimamos que levaram de 1,5 a 2 horas para otimiza\u00e7\u00f5es e testes.<\/p>\n<p><\/p>\n<p>SQL \u00e9 uma linguagem maravilhosa, se voc\u00ea n\u00e3o tiver medo dela, mas tentar compreend\u00ea-la e us\u00e1-la. Com uma boa compreens\u00e3o de como as consultas SQL s\u00e3o executadas, como o banco de dados gera planos de consulta, como os \u00edndices funcionam e apenas do tamanho dos dados que voc\u00ea est\u00e1 lidando, voc\u00ea pode ter um grande sucesso na otimiza\u00e7\u00e3o de consultas. N\u00e3o menos importante, no entanto, continuar experimentando diferentes abordagens e gradualmente fragmentar o problema, encontrando gargalos.<\/p>\n<p><\/p>\n<p>A melhor parte de alcan\u00e7ar resultados como este \u00e9 a melhoria vis\u00edvel e not\u00e1vel na velocidade de funcionamento \u2014 quando um relat\u00f3rio, que antes n\u00e3o carregava, agora \u00e9 carregado quase instantaneamente.<\/p>\n<p><\/p>\n<p><strong>Agradecimentos especiais\u00a0<\/strong>aos meus colegas\u00a0<em>da equipe Aditya Mishra<\/em>,\u00a0<em>Aditya Gaur\u00a0<\/em>e\u00a0<em><noindex><a rel=\"nofollow\" href=\"https:\/\/twitter.com\/s0ftvar\">Varun Malhotra\u00a0<\/a><\/noindex><\/em>pela tempestade de ideias e\u00a0<em>Dinkar Pandir\u00a0<\/em>por encontrar um erro crucial em nossa solicita\u00e7\u00e3o final, antes que nos desped\u00edssemos dela!<\/p>\n<p>Fonte: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/455832\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0412 \u0434\u0435\u043a\u0430\u0431\u0440\u0435 \u043f\u0440\u043e\u0448\u043b\u043e\u0433\u043e \u0433\u043e\u0434\u0430 \u044f \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u043e\u0442\u0447\u0435\u0442 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435 \u043e\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b\u00a0\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 VWO. \u0412\u0440\u0435\u043c\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0442\u0447\u0435\u0442\u043e\u0432 \u0434\u043b\u044f \u043a\u0440\u0443\u043f\u043d\u043e\u0433\u043e \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u043d\u0435\u043f\u043e\u043c\u0435\u0440\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u0438\u043c. \u0410 \u0442\u0430\u043a \u043a\u0430\u043a \u044d\u0442\u043e \u0441\u0444\u0435\u0440\u0430 \u043c\u043e\u0435\u0439 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438, \u044f \u0442\u0443\u0442 \u0436\u0435 \u0441\u043e\u0441\u0440\u0435\u0434\u043e\u0442\u043e\u0447\u0438\u043b\u0441\u044f \u043d\u0430 \u0440\u0435\u0448\u0435\u043d\u0438\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b. \u041f\u0440\u0435\u0434\u044b\u0441\u0442\u043e\u0440\u0438\u044f \u0427\u0442\u043e\u0431\u044b \u0431\u044b\u043b\u043e \u043f\u043e\u043d\u044f\u0442\u043d\u043e \u043e \u0447\u0451\u043c \u0440\u0435\u0447\u044c, \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043e VWO. \u042d\u0442\u043e \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430, [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-35259","post","type-post","status-publish","format-standard","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.3 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0412 \u0434\u0435\u043a\u0430\u0431\u0440\u0435 \u043f\u0440\u043e\u0448\u043b\u043e\u0433\u043e \u0433\u043e\u0434\u0430 \u044f \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u043e\u0442\u0447\u0435\u0442 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435 \u043e\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 VWO. \u0412\u0440\u0435\u043c\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0442\u0447\u0435\u0442\u043e\u0432 \u0434\u043b\u044f \u043a\u0440\u0443\u043f\u043d\u043e\u0433\u043e \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u043d\u0435\u043f\u043e\u043c\u0435\u0440\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u0438\u043c.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"pt_PT\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0418\u0441\u0442\u043e\u0440\u0438\u044f \u043e\u0434\u043d\u043e\u0433\u043e SQL \u0440\u0430\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0412 \u0434\u0435\u043a\u0430\u0431\u0440\u0435 \u043f\u0440\u043e\u0448\u043b\u043e\u0433\u043e \u0433\u043e\u0434\u0430 \u044f \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u043e\u0442\u0447\u0435\u0442 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435 \u043e\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 VWO. \u0412\u0440\u0435\u043c\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0442\u0447\u0435\u0442\u043e\u0432 \u0434\u043b\u044f \u043a\u0440\u0443\u043f\u043d\u043e\u0433\u043e \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u043d\u0435\u043f\u043e\u043c\u0435\u0440\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u0438\u043c.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T19:03:17+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T19:03:17+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47A hist\u00f3ria de uma investiga\u00e7\u00e3o SQL | ProHoster","description":"Em dezembro do ano passado, recebi um relat\u00f3rio interessante sobre um erro da equipe de suporte da VWO. O tempo de carregamento de um dos relat\u00f3rios anal\u00edticos para um grande cliente corporativo parecia excessivamente longo.","canonical_url":"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pt_PT","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0418\u0441\u0442\u043e\u0440\u0438\u044f \u043e\u0434\u043d\u043e\u0433\u043e SQL \u0440\u0430\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f | ProHoster","og:description":"\u0412 \u0434\u0435\u043a\u0430\u0431\u0440\u0435 \u043f\u0440\u043e\u0448\u043b\u043e\u0433\u043e \u0433\u043e\u0434\u0430 \u044f \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u043e\u0442\u0447\u0435\u0442 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435 \u043e\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 VWO. \u0412\u0440\u0435\u043c\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0442\u0447\u0435\u0442\u043e\u0432 \u0434\u043b\u044f \u043a\u0440\u0443\u043f\u043d\u043e\u0433\u043e \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u043d\u0435\u043f\u043e\u043c\u0435\u0440\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u0438\u043c.","og:url":"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T19:03:17+00:00","article:modified_time":"2019-10-31T19:03:17+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"35259","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-21 22:33:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 02:07:28","updated":"2026-01-21 22:33:19","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts\/35259","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/comments?post=35259"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts\/35259\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media?parent=35259"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/categories?post=35259"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/tags?post=35259"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}