{"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\/no\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya","title":{"rendered":"Historien om en SQL-etterforskning","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>I fjor i desember mottok jeg en interessant feilrapport fra VWO-st\u00f8tteteamet. Lastetiden for en av analyserapportene for en stor bedriftskunde virket uforholdsmessig lang. Siden dette er mitt ansvar, begynte jeg straks \u00e5 fokusere p\u00e5 \u00e5 l\u00f8se problemet.<\/p>\n<p><\/p>\n<h2>Bakgrunn<\/h2>\n<p><\/p>\n<p>For \u00e5 gi litt kontekst, vil jeg forklare litt om VWO. Dette er en plattform som lar deg kj\u00f8re ulike m\u00e5lrettede kampanjer p\u00e5 nettsidene dine: gjennomf\u00f8re A\/B-tester, spore bes\u00f8kende og konverteringer, analysere salgstrakter, vise varmekart og spille av bes\u00f8kopptak.<\/p>\n<p><\/p>\n<p>Men det viktigste med plattformen er rapporteringen. Alle funksjonene nevnt ovenfor er sammenkoblet. For bedriftskunder ville en enorm mengde informasjon v\u00e6rt helt ubrukelig uten en kraftig plattform som presenterer den for analyse.<\/p>\n<p><\/p>\n<p>Ved \u00e5 bruke plattformen kan du gj\u00f8re foresp\u00f8rsel p\u00e5 et stort datasett. Her er et enkelt eksempel:<\/p>\n<p><\/p>\n<pre>Vis alle klikk p\u00e5 siden \"abc.com\"\nFRA &lt;dato d1&gt; TIL &lt;dato d2&gt;\nfor personer som\nbrukte Chrome ELLER\n(var i Europa OG brukte iPhone)<\/pre>\n<p><\/p>\n<p>Legg merke til de boolske operatorene. De er tilgjengelige for kunder i foresp\u00f8rselgrensesnittet, slik at du kan lage s\u00e5 kompliserte foresp\u00f8rsel som n\u00f8dvendig for \u00e5 f\u00e5 utvalg.<\/p>\n<p><\/p>\n<h2>Langsom foresp\u00f8rsel<\/h2>\n<p><\/p>\n<p>Kunden det er snakk om, pr\u00f8vde \u00e5 gj\u00f8re noe som intuitivt burde fungere raskt:<\/p>\n<p><\/p>\n<pre>Vis alle sesjonsopptak\nfor brukere som bes\u00f8kte hvilken som helst side\nmed en URL som inneholder \"\/jobs\"<\/pre>\n<p><\/p>\n<p>Denne nettsiden hadde enorm mengde trafikk, og vi lagret over en million unike URL-adresser kun for den. Og de \u00f8nsket \u00e5 finne et ganske enkelt URL-m\u00f8nster relatert til forretningsmodellen deres.<\/p>\n<p>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Foresp\u00f8rsel om etterforskning<\/h2>\n<p><\/p>\n<p>La oss se hva som skjer i databasen. Nedenfor er den originale langsomme SQL-foresp\u00f8rselen:<\/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>Og her er tidene:<\/p>\n<p><\/p>\n<pre>Forventet tid: 1.480 ms\nKj\u00f8ringstid: 1431924.650 ms<\/pre>\n<p><\/p>\n<p>Foresp\u00f8rselen krysset 150 000 rader. Sp\u00f8rringsplanleggeren viste noen interessante detaljer, men ingen \u00e5penbare flaskehalser.<\/p>\n<p><\/p>\n<p>La oss g\u00e5 videre med foresp\u00f8rselen. Som vi ser, gj\u00f8r den <code>JOIN<\/code> tre tabeller:<\/p>\n<p><\/p>\n<ol>\n<li><strong>\u00f8kter<\/strong>: for \u00e5 vise sesjonsinformasjon: nettleser, brukeragent, land og s\u00e5 videre.<\/li>\n<li><strong>recording_data<\/strong>: registrerte URL-er, sider, varighet av bes\u00f8kene<\/li>\n<li><strong>urls<\/strong>: for \u00e5 unng\u00e5 duplisering av ekstremt store URL-er, lagrer vi dem i en egen tabell.<\/li>\n<\/ol>\n<p><\/p>\n<p>V\u00e6r ogs\u00e5 oppmerksom p\u00e5 at alle tabellene v\u00e5re allerede er delt opp etter <code>account_id<\/code>. Dermed er situasjonen utelukket hvor problemer med andre oppst\u00e5r p\u00e5 grunn av \u00e9n spesielt stor konto.<\/p>\n<p><\/p>\n<h2>I s\u00f8ken etter spor<\/h2>\n<p><\/p>\n<p>Ved n\u00e6rmere ettersyn ser vi at noe er galt med den spesifikke foresp\u00f8rselen. Det er verdt \u00e5 se n\u00e6rmere p\u00e5 denne raden:<\/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>Den f\u00f8rste tanken var at p\u00e5 grunn av <code>ILIKE<\/code> p\u00e5 alle disse lange URL-ene (vi har mer enn 1,4 millioner <strong>unike\u00a0<\/strong>URL-er samlet for denne kontoen) kan ytelsen v\u00e6re d\u00e5rlig.<\/p>\n<p><\/p>\n<p>Men nei \u2014 det er ikke saken!<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">SELECT id FROM urls WHERE url ILIKE '%enterprise_customer.com\/jobs%';\n  id\n--------\n ...\n(198661 rader)\n\nTid: 5231.765 ms<\/code><\/pre>\n<p><\/p>\n<p>Selve s\u00f8keforesp\u00f8rselen etter m\u00f8nstre tar bare 5 sekunder. S\u00f8king etter m\u00f8nstre p\u00e5 en million unike URL-er er tydeligvis ikke et problem.<\/p>\n<p><\/p>\n<p>Den neste mistenkte p\u00e5 listen \u2014 flere <code>JOIN<\/code>. Kan det ha v\u00e6rt deres overdrevne bruk som f\u00f8rte til forsinkelse? Vanligvis <code>JOIN<\/code>\u2018er er de mest \u00e5penbare kandidatene for ytelsesproblemer, men jeg trodde ikke at v\u00e5rt tilfelle var typisk.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">analytics_db=# SELECT\n    count(*)\nFROM\n    acc_{account_id}.urls as recordings_urls,\n    acc_{account_id}.recording_data_0 as recording_data,\n    acc_{account_id}.sessions_0 as sessions\nWHERE\n    recording_data.usp_id = sessions.usp_id\n    AND sessions.referrer_id = recordings_urls.id\n    AND r_time &gt; to_timestamp(1542585600)\n    AND r_time =5\n    AND recording_data.num_of_pages &gt; 0 ;\n count\n-------\n  8086\n(1 rad)\n\nTid: 147.851 ms<\/code><\/pre>\n<p><\/p>\n<p>Og det var heller ikke v\u00e5rt tilfelle. <code>JOIN<\/code>\u2018er viste seg \u00e5 v\u00e6re ganske raske.<\/p>\n<p><\/p>\n<h2>Vi snevrer inn listen over mistenkte<\/h2>\n<p><\/p>\n<p>Jeg var klar til \u00e5 begynne \u00e5 endre foresp\u00f8rselen for \u00e5 oppn\u00e5 enhver mulig forbedring i ytelse. Vi i teamet utviklet to hovedideer:<\/p>\n<p><\/p>\n<ul>\n<li><strong>Bruke EXISTS for URL-unders\u00f8kelsen<\/strong>: Vi \u00f8nsket \u00e5 sjekke igjen om det var problemer med underforesp\u00f8rselen for URL-ene. En m\u00e5te \u00e5 oppn\u00e5 dette p\u00e5 er ganske enkelt \u00e5 bruke <code>EXISTS<\/code>. <code>EXISTS<\/code> <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/functions-subquery.html#FUNCTIONS-SUBQUERY-EXISTS\">finne ut at brukeren har deaktivert animasjons-effektene og dermed ogs\u00e5 deaktivere ulike animasjonsmuligheter p\u00e5 nettsiden, for eksempel fjerne skjelvingseffekten p\u00e5 knapper som brukes for \u00e5 tiltrekke seg oppmerksomhet;<\/a><\/noindex> betyr mykje for prestasjonen, ettersom det stopper med en gang det finner den eine raden som m\u00f8ter betingelsen.<\/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 rad)\nTid: 1636.637 ms<\/code><\/pre>\n<p><\/p>\n<p>Ja. Ein underforesp\u00f8rsel, n\u00e5r den er pakket inn i\u00a0<code>EXISTS<\/code>, gj\u00f8r alt super raskt. Det neste logiske sp\u00f8rsm\u00e5let er, hvorfor er foresp\u00f8rselen med <code>JOIN<\/code>-ene og den underforesp\u00f8rselen raske hver for seg, men fryktelig trege sammen?<\/p>\n<p><\/p>\n<ul>\n<li><strong>Vi flytter underforesp\u00f8rselen til CTE <\/strong>: hvis foresp\u00f8rselen er rask i seg selv, kan vi bare beregne det raske resultatet f\u00f8rst, og deretter gi det til hovedforesp\u00f8rselen.<\/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>Men dette var fortsatt veldig sakte.<\/p>\n<p><\/p>\n<h2>Vi finner synderen<\/h2>\n<p><\/p>\n<p>Hele tiden hadde en liten ting flommet forbi \u00f8ynene mine, som jeg stadig avviste. Men siden det ikke var noe annet \u00e5 se p\u00e5, bestemte jeg meg for \u00e5 ta en titt p\u00e5 det ogs\u00e5. Jeg snakker om <code>&amp;&amp;<\/code> operat\u00f8ren. Mens <code>EXISTS<\/code> bare forbedret ytelsen, <code>&amp;&amp;<\/code> var den eneste gjenv\u00e6rende felles faktoren i alle versjoner av den langsomme foresp\u00f8rselen.<\/p>\n<p><\/p>\n<p>Ser p\u00e5 <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/9.1\/functions-array.html\">dokumentasjonen<\/a><\/noindex>, ser vi at <code>&amp;&amp;<\/code> brukes n\u00e5r du trenger \u00e5 finne felles elementer mellom to arrayer.<\/p>\n<p><\/p>\n<p>I den originale foresp\u00f8rselen er dette:<\/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>Det betyr at vi gj\u00f8r et m\u00f8nstergitt s\u00f8k p\u00e5 URL-ene v\u00e5re, og deretter finner kryssingen med alle URL-ene som har felles poster. Dette er litt forvirrende, siden \u00aburls\u00bb her ikke refererer til tabellen som inneholder alle URL-adressene, men til kolonnen \u00aburls\u00bb i tabellen <code>recording_data<\/code>.<\/p>\n<p><\/p>\n<p>Med \u00f8kende mistanker om <code>&amp;&amp;<\/code>, pr\u00f8vde jeg \u00e5 bekrefte dem i foresp\u00f8rselen som ble generert av <code>EXPLAIN ANALYZE<\/code> (jeg hadde allerede en lagret plan, men jeg pleier vanligvis \u00e5 eksperimentere i SQL heller enn \u00e5 pr\u00f8ve \u00e5 forst\u00e5 den uklare planleggingen av sp\u00f8rringer).<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">Filter: ((urls &amp;&amp; ($0)::text[]) AND (r_time &gt; '2018-12-17 12:17:23+00'::timestamp with time zone) AND (r_time = '5'::double precision) AND (num_of_pages &gt; 0))\n                           Rader fjernet av filteret: 52710<\/code><\/pre>\n<p><\/p>\n<p>Det var flere filterlinjer kun fra <code>&amp;&amp;<\/code>. Dette betydde at denne operasjonen ikke bare var kostbar, men ogs\u00e5 ble utf\u00f8rt flere ganger.<\/p>\n<p><\/p>\n<p>Jeg sjekket dette ved \u00e5 isolere betingelsen<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">SELECT 1\nFROM \n    acc_{account_id}.urls as recordings_urls, \n    acc_{account_id}.recording_data_30 as recording_data_30, \n    acc_{account_id}.sessions_30 as 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>Denne foresp\u00f8rselen ble utf\u00f8rt sakte. Siden <code>JOIN<\/code>-ene er raske og underforesp\u00f8rslene er raske, var det bare igjen <code>&amp;&amp;<\/code> operatoren.<\/p>\n<p><\/p>\n<p>Bare dette er den n\u00f8kkeloperasjonen. Vi m\u00e5 alltid s\u00f8ke gjennom hovedtabellen URL-adresser for \u00e5 lete etter m\u00f8nstre, og vi m\u00e5 alltid finne overlappinger. Vi kan ikke s\u00f8ke direkte p\u00e5 URL-oppf\u00f8ringene fordi de bare er ID-er som refererer til <code>urls<\/code>.<\/p>\n<p><\/p>\n<h2>Veien til l\u00f8sningen<\/h2>\n<p><\/p>\n<p><code>&amp;&amp;<\/code> er sakte, fordi begge settene er enorme. Operasjonen vil v\u00e6re relativt rask hvis jeg erstatter <code>urls<\/code> En funksjon har blitt <code>{ \"http:\/\/google.com\/\", \"http:\/\/wingify.com\/\" }<\/code>.<\/p>\n<p><\/p>\n<p>Jeg begynte \u00e5 se etter en m\u00e5te \u00e5 gj\u00f8re i Postgres mengdeinterseksjoner uten \u00e5 bruke <code>&amp;&amp;<\/code>, men uten s\u00e6rlig hell.<\/p>\n<p><\/p>\n<p>Til slutt bestemte vi oss for \u00e5 bare l\u00f8se problemet isolert: gi meg alle <code>urls<\/code> linjer der url-en samsvarer med malen. Uten ytterligere betingelser vil dette v\u00e6re \u2014\u00a0<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">SELECT urls.url\nFROM \n\tacc_{account_id}.urls as urls,\n\t(SELECT unnest(recording_data.urls) AS id) AS unrolled_urls\nWHERE\n\turls.id = unrolled_urls.id AND\n\turls.url ILIKE '%jobs%'<\/code><\/pre>\n<p><\/p>\n<p>I stedet\u00a0<code>JOIN<\/code> Syntaksen, jeg brukte bare en underforesp\u00f8rsel og rullet ut <code>recording_data.urls<\/code> array slik at betingelsen kunne anvendes direkte i <code>Den maksimale\/minimale hurtig oppfriskningen etter sletting eller blandede DML har ikke samme oppf\u00f8rsel som innsetting-tilfelle. Den sletter og beregner p\u00e5 nytt maks\/min verdiene for de ber\u00f8rte gruppene. Du m\u00e5 v\u00e6re oppmerksom p\u00e5 dens ytelseseffekt.<\/code>.<\/p>\n<p><\/p>\n<p>Det viktigste her er at <code>&amp;&amp;<\/code> brukes til \u00e5 sjekke om den aktuelle oppf\u00f8ringen inneholder den tilsvarende URL-en. Litt med \u00f8ynene lukket kan man se i denne operasjonen at den beveger seg gjennom elementene i arrayet (eller radene i tabellen) og stopper n\u00e5r betingelsen (overensstemmelsen) er oppfylt. Ligner det p\u00e5 noe? Ja, <code>EXISTS<\/code>.<\/p>\n<p><\/p>\n<p>Siden <code>recording_data.urls<\/code> kan refereres utenfor konteksten til underforesp\u00f8rselen, n\u00e5r dette skjer, kan vi g\u00e5 tilbake til v\u00e5r gamle venn <code>EXISTS<\/code> og innkapsle dem med underforesp\u00f8rselen.<\/p>\n<p><\/p>\n<p>N\u00e5r vi setter alt sammen, f\u00e5r vi den endelige optimaliserte foresp\u00f8rselen:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">VELG \n    telj (*) \nFROM \n    acc_{account_id}.urls som opptak_urls, \n    acc_{account_id}.recording_data som opptaksdata, \n    acc_{account_id}.sessions som sesjoner \nHVOR \n    opptaksdata.usp_id = sesjoner.usp_id \n    OG  (  1 = 1  )  \n    OG sesjoner.referrer_id = opptak_urls.id \n    OG r_time &gt; to_timestamp(1542585600) \n    OG r_time = 5 \n    OG opptaksdata.antall_sider &gt; 0\n    OG EXIST(\n        VELG urls.url\n        FRA \n            acc_{account_id}.urls som urls,\n            (VELG unnest(urls) SOM rec_url_id FRA acc_{account_id}.recording_data) \n            SOM rullet_ut_urls\n        HVOR\n            urls.id = rullet_ut_urls.rec_url_id OG\n            urls.url ILIKE '%enterprise_customer.com\/jobs%'\n    );\n<\/code><\/pre>\n<p><\/p>\n<p>Og den endelige kj\u00f8retiden <code>Tid: 1898.717 ms<\/code> Er det p\u00e5 tide \u00e5 feire?!?<\/p>\n<p><\/p>\n<p>Ikke s\u00e5 raskt! F\u00f8rst m\u00e5 vi sjekke korrektheten. Jeg var veldig skeptisk til <code>EXISTS<\/code> optimaliseringen, fordi den endrer logikken til en tidligere avslutning. Vi m\u00e5 v\u00e6re sikre p\u00e5 at vi ikke har lagt til en skjult feil i foresp\u00f8rselen.<\/p>\n<p><\/p>\n<p>En enkel sjekk var \u00e5 utf\u00f8re <code>telj(*)<\/code> og p\u00e5 b\u00e5de sakte og raske foresp\u00f8rsel for mange forskjellige datasett. Deretter, for et lite undersett av data, sjekket jeg riktigheten av alle resultater manuelt.<\/p>\n<p><\/p>\n<p>Alle sjekker ga konsekvent positive resultater. Vi har fikset alt!<\/p>\n<p><\/p>\n<h2>L\u00e6rte Leksjoner<\/h2>\n<p><\/p>\n<p>Fra denne historien kan det trekkes mange leksjoner:<\/p>\n<p><\/p>\n<ol>\n<li>Sp\u00f8rsm\u00e5lsskjemaer forteller ikke hele historien, men kan gi ledetr\u00e5der<\/li>\n<li>Hovedmistenkte er ikke alltid de virkelige gjerningspersonene<\/li>\n<li>Sakte foresp\u00f8rsel kan brytes ned for \u00e5 isolere flaskehalser<\/li>\n<li>Ikke alle optimaliseringer er der for \u00e5 redusere<\/li>\n<li>Bruken <code>EXIST<\/code>, n\u00e5r det er mulig, kan f\u00f8re til dramatisk ytelsesvekst<\/li>\n<\/ol>\n<p><\/p>\n<h2>Utdata<\/h2>\n<p><\/p>\n<p>Vi gikk fra en foresp\u00f8rselstid p\u00e5 ~24 minutter til 2 sekunder \u2014 en betydelig ytelses\u00f8kning! Selv om denne artikkelen ble lang, skjedde alle eksperimentene vi utf\u00f8rte p\u00e5 en dag, og etter estimater, tok de omtrent 1,5 til 2 timer \u00e5 optimalisere og teste.<\/p>\n<p><\/p>\n<p>SQL er et fantastisk spr\u00e5k, s\u00e5 lenge man ikke er redd for det, men pr\u00f8ver \u00e5 forst\u00e5 og bruke det. Med en god forst\u00e5else av hvordan SQL-foresp\u00f8rsler kj\u00f8res, hvordan databasen genererer sp\u00f8rsm\u00e5lsskjemaer, hvordan indekser fungerer og bare st\u00f8rrelsen p\u00e5 dataene du har med \u00e5 gj\u00f8re, kan du virkelig utmerke deg i foresp\u00f8rseloptimering. Det er like viktig, imidlertid, \u00e5 fortsatt pr\u00f8ve forskjellige tiln\u00e6rminger og sakte bryte ned problemet for \u00e5 finne flaskehalser.<\/p>\n<p><\/p>\n<p>Den beste delen med \u00e5 oppn\u00e5 slike resultater er det merkbare forbedringen i hastigheten \u2013 n\u00e5r en rapport som tidligere ikke engang lastet inn, n\u00e5 lastes inn nesten umiddelbart.<\/p>\n<p><\/p>\n<p><strong>Spesiell takk\u00a0<\/strong>til mine kolleger\u00a0<em>i teamet Aditya Mishra<\/em>,\u00a0<em>Aditya Gaur\u00a0<\/em>og\u00a0<em><noindex><a rel=\"nofollow\" href=\"https:\/\/twitter.com\/s0ftvar\">Varun Malhotra\u00a0<\/a><\/noindex><\/em>for brainstorming og\u00a0<em>Dinkar Pandir\u00a0<\/em>for \u00e5 ha oppdaget en viktig feil i v\u00e5r endelige foresp\u00f8rsel f\u00f8r vi forlot den helt!<\/p>\n<p>Kilde: <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.5 - 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\/no\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.5\" \/>\n\t\t<meta property=\"og:locale\" content=\"nn_NO\" \/>\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\/no\/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\udd47Historien om en SQL etterforskning | ProHoster","description":"I desember i fjor mottok jeg en interessant feilrapport fra VWO supportteamet. Lastetiden for en av de analytiske rapportene for en stor bedriftskunde virket uforholdsmessig lang.","canonical_url":"https:\/\/prohoster.info\/no\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"nn_NO","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\/no\/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\/no\/wp-json\/wp\/v2\/posts\/35259","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/comments?post=35259"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/posts\/35259\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/media?parent=35259"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/categories?post=35259"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/tags?post=35259"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}