{"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\/it\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya","title":{"rendered":"La storia di un'inchiesta SQL","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Lo scorso dicembre ho ricevuto un interessante rapporto su un errore dal team di supporto VWO. Il tempo di caricamento di uno dei rapporti analitici per un grande cliente aziendale sembrava eccessivamente lungo. E dato che questo rientra nelle mie responsabilit\u00e0, mi sono subito concentrato sulla risoluzione del problema.<\/p>\n<p><\/p>\n<h2>Antefatti<\/h2>\n<p><\/p>\n<p>Per chiarire di cosa stiamo parlando, racconter\u00f2 brevemente di VWO. \u00c8 una piattaforma che consente di lanciare diverse campagne targetizzate sui propri siti: condurre esperimenti A\/B, monitorare i visitatori e le conversioni, analizzare il funnel di vendita, mostrare mappe di calore e riprodurre registrazioni delle visite.<\/p>\n<p><\/p>\n<p>Ma la cosa pi\u00f9 importante della piattaforma \u00e8 la creazione di report. Tutte le funzioni sopra menzionate sono collegate tra loro. E per i clienti aziendali, un enorme insieme di informazioni sarebbe semplicemente inutile senza una potente piattaforma che le rappresenti in forma analitica.<\/p>\n<p><\/p>\n<p>Utilizzando la piattaforma, \u00e8 possibile effettuare richieste arbitrarie su un grande insieme di dati. Ecco un semplice esempio:<\/p>\n<p><\/p>\n<pre>Mostra tutti i clic sulla pagina \"abc.com\"\nDAL &lt;data d1&gt; AL &lt;data d2&gt;\nper le persone che\nhanno utilizzato Chrome O\n(si trovavano in Europa E hanno utilizzato iPhone)<\/pre>\n<p><\/p>\n<p>Fai attenzione agli operatori booleani. Sono disponibili per i clienti nell'interfaccia di query, per creare richieste complesse per ottenere campioni.<\/p>\n<p><\/p>\n<h2>Richiesta lenta<\/h2>\n<p><\/p>\n<p>Il cliente in questione cercava di fare qualcosa che intuitivamente doveva funzionare rapidamente:<\/p>\n<p><\/p>\n<pre>Mostra tutte le registrazioni delle sessioni\nper gli utenti che hanno visitato qualsiasi pagina\ncon un URL che contiene \"\/jobs\"<\/pre>\n<p><\/p>\n<p>Questo sito aveva un'enorme quantit\u00e0 di traffico e conservavamo oltre un milione di URL unici solo per esso. E volevano trovare un formato di URL piuttosto semplice legato al loro modello di business.<\/p>\n<p>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Indagine preliminare<\/h2>\n<p><\/p>\n<p>Diamo un'occhiata a cosa succede nel database. Di seguito \u00e8 riportata l'originale query SQL lenta:<\/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 &lt; to_timestamp(1545177599) \n    AND recording_data.duration &gt;= 5 \n    AND recording_data.num_of_pages &gt; 0 ;<\/code><\/pre>\n<p><\/p>\n<p>Ecco i tempi:<\/p>\n<p><\/p>\n<pre>Tempo pianificato: 1,480 ms\nTempo di esecuzione: 1.431.924,650 ms<\/pre>\n<p><\/p>\n<p>La query ha esaminato 150 mila righe. Il pianificatore di query ha mostrato un paio di dettagli interessanti, ma nessun collo di bottiglia evidente.<\/p>\n<p><\/p>\n<p>Esploriamo ulteriormente la query. Come si vede, essa effettua <code>JOIN<\/code> di tre tabelle:<\/p>\n<p><\/p>\n<ol>\n<li><strong>sessions<\/strong>: per visualizzare le informazioni sulle sessioni: browser, user agent, paese e cos\u00ec via.<\/li>\n<li><strong>recording_data<\/strong>: URL registrati, pagine, durata delle visite<\/li>\n<li><strong>urls<\/strong>: per evitare la duplicazione di URL estremamente lunghi, li memorizziamo in una tabella separata.<\/li>\n<\/ol>\n<p><\/p>\n<p>Inoltre, si noti che tutte le nostre tabelle sono gi\u00e0 divise per <code>account_id<\/code>. In questo modo si esclude la situazione in cui un singolo account particolarmente grande causa problemi agli altri.<\/p>\n<p><\/p>\n<h2>In cerca di indizi<\/h2>\n<p><\/p>\n<p>All'esame pi\u00f9 attento, vediamo che qualcosa in una specifica query non va. \u00c8 opportuno prestare attenzione a questa riga:<\/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>La prima idea era che forse, a causa di <code>ILIKE<\/code> su tutti questi URL lunghi (abbiamo pi\u00f9 di 1,4 milioni di <strong>URL unici, raccolti per questo account) la performance potrebbe deteriorarsi.\u00a0<\/strong>Ma no \u2014 non \u00e8 questo il problema!<\/p>\n<p><\/p>\n<p>SELECT id FROM urls WHERE url ILIKE '%enterprise_customer.com\/jobs%';\n  id\n--------\n ...\n(198661 righe)\n\nTempo: 5.231,765 ms<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">La query di ricerca per modello impiega solo 5 secondi. La ricerca per modello su un milione di URL unici non \u00e8 chiaramente un problema.<\/code><\/pre>\n<p><\/p>\n<p>Il prossimo sospettato in lista \u00e8 \u2014 alcuni<\/p>\n<p><\/p>\n<p>. Forse il loro uso eccessivo ha portato a un rallentamento? Di solito <code>JOIN<\/code>\u2018s sono i candidati pi\u00f9 ovvi per problemi di performance, ma non credevo che il nostro caso fosse tipico. <code>JOIN<\/code>I &#8216; jsou i candidati evidenti per problemi di prestazioni, ma non credevo che il nostro caso fosse tipico.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">E anche questo non era il nostro caso.<\/code><\/pre>\n<p><\/p>\n<p>\u2018s si sono rivelati piuttosto veloci. <code>JOIN<\/code>I &#8216; si sono rivelati piuttosto veloci.<\/p>\n<p><\/p>\n<h2>Ero pronto a modificare la query per ottenere eventuali miglioramenti di performance. Io e il mio team abbiamo sviluppato 2 idee principali:<\/h2>\n<p><\/p>\n<p>Utilizzare EXISTS per la sottoquery degli URL<\/p>\n<p><\/p>\n<ul>\n<li><strong>: Volevamo verificare ancora una volta se ci fossero problemi con la sottoquery per gli URL. Uno dei modi per ottenerlo \u00e8 semplicemente usare<\/strong>EXISTS <code>ESISTE<\/code>. <code>ESISTE<\/code> <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/functions-subquery.html#FUNCTIONS-SUBQUERY-EXISTS\">pu\u00f2<\/a><\/noindex> migliorare notevolmente le prestazioni poich\u00e9 termina immediatamente non appena trova una sola riga che soddisfa la condizione.<\/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>S\u00ec. Una sottoquery, quando \u00e8 incapsulata in\u00a0<code>ESISTE<\/code>, rende tutto super veloce. La prossima domanda logica \u00e8 perch\u00e9 la query con <code>JOIN<\/code>- e la sottoquery siano veloci separatamente, ma rallentino terribilmente insieme?<\/p>\n<p><\/p>\n<ul>\n<li><strong>Spostiamo la sottoquery in CTE <\/strong>: se la query \u00e8 veloce da sola, possiamo semplicemente calcolare prima il risultato veloce e poi fornirlo alla query principale<\/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>Ma anche questo era ancora molto lento.<\/p>\n<p><\/p>\n<h2>Identifichiamo il colpevole<\/h2>\n<p><\/p>\n<p>Per tutto questo tempo, una piccola cosa mi \u00e8 balzata agli occhi, dalla quale ho continuamente tentato di distolgermi. Ma poich\u00e9 non c'era altro da fare, ho deciso di dare un'occhiata. Parlo di <code>&amp;&amp;<\/code> operatore. Finora <code>ESISTE<\/code> ha semplicemente migliorato le prestazioni, <code>&amp;&amp;<\/code> era l'unico fattore comune rimanente in tutte le versioni della query lenta.<\/p>\n<p><\/p>\n<p>Guardando <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/9.1\/functions-array.html\">la documentazione<\/a><\/noindex>, vediamo che <code>&amp;&amp;<\/code> viene utilizzato quando \u00e8 necessario trovare elementi comuni tra due array.<\/p>\n<p><\/p>\n<p>Nella query originale questo \u00e8:<\/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>Il che significa che stiamo cercando tramite un pattern nei nostri url, quindi troviamo l'intersezione con tutti gli url che hanno record comuni. \u00c8 un po' confuso, poich\u00e9 \"urls\" qui non si riferisce a una tabella contenente tutti gli URL, ma alla colonna \"urls\" nella tabella <code>recording_data<\/code>.<\/p>\n<p><\/p>\n<p>Con l'aumentare dei sospetti riguardo <code>&amp;&amp;<\/code>, ho provato a trovarne conferma in termini di query generata da <code>EXPLAIN ANALYZE<\/code> (avevo gi\u00e0 un piano salvato, ma di solito mi \u00e8 pi\u00f9 comodo sperimentare in SQL piuttosto che cercare di capire le opacit\u00e0 dei pianificatori di query).<\/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 with time zone) E (r_time = '5'::double precision) E (num_of_pages &gt; 0))\n                           Righe rimosse dal filtro: 52710<\/code><\/pre>\n<p><\/p>\n<p>C'erano diverse righe di filtri solo da <code>&amp;&amp;<\/code>. Ci\u00f2 significava che quest'operazione non solo era costosa, ma veniva anche eseguita pi\u00f9 volte.<\/p>\n<p><\/p>\n<p>L'ho controllato isolando la condizione<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">SELECT 1\nDA \n    acc_{account_id}.urls come recordings_urls, \n    acc_{account_id}.recording_data_30 come recording_data_30, \n    acc_{account_id}.sessions_30 come sessions_30 \nDOVE \n\turls &amp;&amp;  array(select id da acc_{account_id}.urls dove url  ILIKE  '%enterprise_customer.com\/jobs%')::text[]<\/code><\/pre>\n<p><\/p>\n<p>Questa query veniva eseguita lentamente. Poich\u00e9 <code>JOIN<\/code>- le subquery sono veloci e le subquery sono veloci, rimaneva solo <code>&amp;&amp;<\/code> l'operatore.<\/p>\n<p><\/p>\n<p>Questa \u00e8 l'operazione chiave. Dobbiamo sempre cercare in tutta la tabella principale degli URL per cercare nel modello, e dobbiamo sempre trovare intersezioni. Non possiamo cercare direttamente nei record degli URL, poich\u00e9 sono semplicemente ID che fanno riferimento a <code>urls<\/code>.<\/p>\n<p><\/p>\n<h2>Sulla strada per la soluzione<\/h2>\n<p><\/p>\n<p><code>&amp;&amp;<\/code> \u00e8 lento, poich\u00e9 entrambi i set sono enormi. L'operazione sar\u00e0 relativamente veloce se sostituisco <code>urls<\/code> in <code>{ \"http:\/\/google.com\/\", \"http:\/\/wingify.com\/\" }<\/code>.<\/p>\n<p><\/p>\n<p>Ho iniziato a cercare un modo per fare in Postgres l'intersezione degli insiemi senza utilizzare <code>&amp;&amp;<\/code>, ma senza particolare successo.<\/p>\n<p><\/p>\n<p>Alla fine, abbiamo deciso di risolvere il problema in modo isolato: dammi tutte le <code>urls<\/code> righe per cui l'URL corrisponde al modello. Senza ulteriori condizioni, ci\u00f2 sar\u00e0 &#8212;\u00a0<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">SELECT urls.url\nDA \n\tacc_{account_id}.urls come urls,\n\t(SELECT unnest(recording_data.urls) AS id) AS unrolled_urls\nDOVE\n\turls.id = unrolled_urls.id E\n\turls.url  ILIKE  '%jobs%'<\/code><\/pre>\n<p><\/p>\n<p>Invece di\u00a0<code>JOIN<\/code> sintassi ho semplicemente usato una subquery e ho srotolato <code>recording_data.urls<\/code> l'array, in modo da poter applicare direttamente la condizione in <code>DOVE<\/code>.<\/p>\n<p><\/p>\n<p>La cosa pi\u00f9 importante qui \u00e8 che <code>&amp;&amp;<\/code> viene utilizzato per verificare se il record d\u00e0 corrispondenza con l'URL specificato. Strizzando un po' gli occhi, si pu\u00f2 vedere in questa operazione il movimento tra gli elementi dell'array (o righe della tabella) e l'interruzione al soddisfacimento della condizione (corrispondenza). Ti ricorda qualcosa? Ah, <code>ESISTE<\/code>.<\/p>\n<p><\/p>\n<p>Poich\u00e9 su <code>recording_data.urls<\/code> pu\u00f2 essere fatto riferimento dall'esterno del contesto della subquery, quando accade, possiamo tornare al nostro vecchio amico <code>ESISTE<\/code> e racchiuderlo in una subquery.<\/p>\n<p><\/p>\n<p>Unendo tutto insieme, otteniamo la query finale ottimizzata:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">SELEZIONA \n    count(*) \nDA \n    acc_{account_id}.urls come recordings_urls, \n    acc_{account_id}.recording_data come recording_data, \n    acc_{account_id}.sessions come sessions \nDOVE \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 ESISTE(\n        SELEZIONA urls.url\n        DA \n            acc_{account_id}.urls come urls,\n            (SELEZIONA unnest(urls) COME rec_url_id DA acc_{account_id}.recording_data) \n            COME unrolled_urls\n        DOVE\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 il tempo totale di esecuzione <code>Tempo: 1898.717 ms<\/code> \u00c8 ora di festeggiare?!?<\/p>\n<p><\/p>\n<p>Non cos\u00ec in fretta! Prima devi controllare la correttezza. Ero estremamente sospettoso riguardo a <code>ESISTE<\/code> ottimizzazione, poich\u00e9 modifica la logica per una conclusione pi\u00f9 rapida. Dobbiamo essere certi di non aver introdotto un errore non evidente nella query.<\/p>\n<p><\/p>\n<p>Una semplice verifica consisteva nell'eseguire <code>count(*)<\/code> sia su query lente che veloci per un gran numero di diversi set di dati. Poi, per un piccolo sottoinsieme di dati, ho verificato manualmente la correttezza di tutti i risultati.<\/p>\n<p><\/p>\n<p>Tutte le verifiche hanno dato risultati costantemente positivi. Abbiamo sistemato tutto!<\/p>\n<p><\/p>\n<h2>Lezioni apprese<\/h2>\n<p><\/p>\n<p>Da questa storia si possono estrarre molte lezioni:<\/p>\n<p><\/p>\n<ol>\n<li>I piani di query non raccontano tutta la storia, ma possono fornire spunti<\/li>\n<li>I principali sospettati non sono sempre i veri colpevoli<\/li>\n<li>Le query lente possono essere separati per isolare i colli di bottiglia<\/li>\n<li>Non tutte le ottimizzazioni sono per loro natura riduttive<\/li>\n<li>Utilizzo <code>ESISTE<\/code>, dove possibile, pu\u00f2 portare a un aumento netto delle prestazioni<\/li>\n<\/ol>\n<p><\/p>\n<h2>Conclusione<\/h2>\n<p><\/p>\n<p>Siamo passati da un tempo di query di ~24 minuti a 2 secondi \u2014 un aumento di prestazioni piuttosto significativo! Anche se questo articolo \u00e8 stato lungo, tutti gli esperimenti che abbiamo effettuato si sono svolti in un giorno e, a nostra stima, hanno preso da 1,5 a 2 ore per ottimizzazioni e test.<\/p>\n<p><\/p>\n<p>SQL \u00e8 un linguaggio meraviglioso, se non ne hai paura, ma cerchi di conoscerlo e utilizzarlo. Avendo una buona comprensione di come vengono eseguite le query SQL, di come i DB generano i piani di query, di come funzionano gli indici e semplicemente delle dimensioni dei dati con cui hai a che fare, puoi davvero eccellere nell'ottimizzazione delle query. Tuttavia, \u00e8 altrettanto importante continuare a provare approcci diversi e a scomporre lentamente il problema, identificando i colli di bottiglia.<\/p>\n<p><\/p>\n<p>La parte migliore nel raggiungere risultati simili \u00e8 il miglioramento visibile e tangibile della velocit\u00e0 \u2014 quando un rapporto che prima non si caricava nemmeno, ora si carica quasi istantaneamente.<\/p>\n<p><\/p>\n<p><strong>Un grazie speciale\u00a0<\/strong>ai miei colleghi\u00a0<em>del team di 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>per il brainstorming e\u00a0<em>Dinkar Pandir\u00a0<\/em>per aver trovato un errore importante nella nostra richiesta finale, prima che ci separassimo definitivamente da essa!<\/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.1.1 - 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\/it\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"it_IT\" \/>\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\/it\/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\udd47La storia di un'indagine SQL | ProHoster","description":"A dicembre dello scorso anno ho ricevuto un interessante rapporto sugli errori dal team di supporto di VWO. Il tempo di caricamento di uno dei rapporti analitici per un grande cliente corporate sembrava eccessivamente lungo.","canonical_url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"it_IT","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\/it\/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\/it\/wp-json\/wp\/v2\/posts\/35259","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/comments?post=35259"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/35259\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=35259"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=35259"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=35259"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}