{"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\/cs\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya","title":{"rendered":"P\u0159\u00edb\u011bh jednoho SQL vy\u0161et\u0159ov\u00e1n\u00ed","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>V prosince lo\u0148sk\u00e9ho roku jsem obdr\u017eel zaj\u00edmavou zpr\u00e1vu o chyb\u011b od t\u00fdmu podpory VWO. Doba na\u010d\u00edt\u00e1n\u00ed jednoho z analytick\u00fdch report\u016f pro velk\u00e9ho korpor\u00e1tn\u00edho klienta se zd\u00e1la b\u00fdt nep\u0159im\u011b\u0159en\u011b dlouh\u00e1. A proto\u017ee to spad\u00e1 do m\u00e9 odpov\u011bdnosti, okam\u017eit\u011b jsem se soust\u0159edil na \u0159e\u0161en\u00ed probl\u00e9mu.<\/p>\n<p><\/p>\n<h2>Pozad\u00ed<\/h2>\n<p><\/p>\n<p>Abych objasnil, o co se jedn\u00e1, pov\u00edm jenom trochu o VWO. Je to platforma, pomoc\u00ed kter\u00e9 m\u016f\u017eete spou\u0161t\u011bt r\u016fzn\u00e9 c\u00edlen\u00e9 kampan\u011b na sv\u00fdch webech: prov\u00e1d\u011bt A\/B experimenty, sledovat n\u00e1v\u0161t\u011bvn\u00edky a konverze, analyzovat prodejn\u00ed trycht\u00fd\u0159e, zobrazovat tepeln\u00e9 mapy a p\u0159ehr\u00e1vat z\u00e1znamy n\u00e1v\u0161t\u011bv.<\/p>\n<p><\/p>\n<p>Ale nejd\u016fle\u017eit\u011bj\u0161\u00ed na platform\u011b je tvorba report\u016f. V\u0161echny v\u00fd\u0161e uveden\u00e9 funkce jsou vz\u00e1jemn\u011b propojen\u00e9. A pro korpor\u00e1tn\u00ed klienty by obrovsk\u00e9 mno\u017estv\u00ed informac\u00ed bylo prost\u011b neu\u017eite\u010dn\u00e9 bez siln\u00e9 platformy, kter\u00e1 by je reprezentovala pro analytiku.<\/p>\n<p><\/p>\n<p>Pomoc\u00ed platformy m\u016f\u017eete prov\u00e9st libovoln\u00fd dotaz na velk\u00e9m souboru dat. Zde je jednoduch\u00fd p\u0159\u00edklad:<\/p>\n<p><\/p>\n<pre>Zobrazit v\u0161echny kliknut\u00ed na str\u00e1nce \"abc.com\"\nOD &lt;data d1&gt; DO &lt;data d2&gt;\npro u\u017eivatele, kte\u0159\u00ed\npou\u017e\u00edvali Chrome NEBO\n(b byli v Evrop\u011b A pou\u017e\u00edvali iPhone)<\/pre>\n<p><\/p>\n<p>V\u0161imn\u011bte si logick\u00fdch oper\u00e1tor\u016f. Jsou k dispozici pro klienty v rozhran\u00ed dotazu, aby mohli vytv\u00e1\u0159et libovoln\u011b slo\u017eit\u00e9 dotazy pro z\u00edsk\u00e1n\u00ed vzork\u016f.<\/p>\n<p><\/p>\n<h2>Pomal\u00fd dotaz<\/h2>\n<p><\/p>\n<p>Klient, o kter\u00e9m je \u0159e\u010d, se pokusil ud\u011blat n\u011bco, co by intuitivn\u011b m\u011blo fungovat rychle:<\/p>\n<p><\/p>\n<pre>Zobraz v\u0161echny z\u00e1znamy sezen\u00ed\npro u\u017eivatele, kte\u0159\u00ed nav\u0161t\u00edvili jakoukoli str\u00e1nku\ns URL, kde je \"\\\/jobs\"<\/pre>\n<p><\/p>\n<p>Na tomto webu byl obrovsk\u00fd objem provozu a uchov\u00e1vali jsme v\u00edce ne\u017e milion unik\u00e1tn\u00edch URL adres pouze pro n\u011bj. A cht\u011bli naj\u00edt celkem jednoduchou \u0161ablonu URL t\u00fdkaj\u00edc\u00ed se jejich obchodn\u00edho modelu.<\/p>\n<p>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>P\u0159edb\u011b\u017en\u00e9 vy\u0161et\u0159ov\u00e1n\u00ed<\/h2>\n<p><\/p>\n<p>Pod\u00edvejme se, co se d\u011bje v datab\u00e1zi. N\u00ed\u017ee je p\u016fvodn\u00ed pomal\u00fd SQL dotaz:<\/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>A tady jsou \u010dasy:<\/p>\n<p><\/p>\n<pre>O\u010dek\u00e1van\u00fd \u010das: 1.480 ms\nDoba trv\u00e1n\u00ed: 1431924.650 ms<\/pre>\n<p><\/p>\n<p>Dotaz proch\u00e1zel 150 tis\u00edc \u0159\u00e1dk\u016f. Pl\u00e1nova\u010d dotaz\u016f uk\u00e1zal n\u011bkolik zaj\u00edmav\u00fdch detail\u016f, ale \u017e\u00e1dn\u00e1 o\u010dividn\u00e1 \u00fazk\u00e1 m\u00edsta.<\/p>\n<p><\/p>\n<p>Poj\u010fme se pod\u00edvat na dotaz d\u00e1l. Jak je vid\u011bt, d\u011bl\u00e1 <code>JOIN<\/code> t\u0159\u00ed tabulek:<\/p>\n<p><\/p>\n<ol>\n<li><strong>sessions<\/strong>: pro zobrazen\u00ed relace: prohl\u00ed\u017ee\u010d, u\u017eivatelsk\u00e1 agentura, zem\u011b atd.<\/li>\n<li><strong>recording_data<\/strong>: zaznamenan\u00e9 URL, str\u00e1nky, d\u00e9lka n\u00e1v\u0161t\u011bv<\/li>\n<li><strong>urls<\/strong>: abychom se vyhnuli duplikaci extr\u00e9mn\u011b dlouh\u00fdch URL, ukl\u00e1d\u00e1me je do samostatn\u00e9 tabulky.<\/li>\n<\/ol>\n<p><\/p>\n<p>Tak\u00e9 si v\u0161imn\u011bte, \u017ee v\u0161echny na\u0161e tabulky jsou ji\u017e rozd\u011bleny podle <code>account_id<\/code>. T\u00edm je vylou\u010deno, \u017ee by kv\u016fli jednomu obzvl\u00e1\u0161t\u011b velk\u00e9mu \u00fa\u010dtu nastaly probl\u00e9my ostatn\u00edm.<\/p>\n<p><\/p>\n<h2>Hled\u00e1n\u00ed stop<\/h2>\n<p><\/p>\n<p>P\u0159i bli\u017e\u0161\u00edm pohledu vid\u00edme, \u017ee n\u011bco v konkr\u00e9tn\u00edm dotazu nen\u00ed v po\u0159\u00e1dku. Stoj\u00ed za to pod\u00edvat se na tento \u0159\u00e1dek:<\/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>Prvn\u00ed my\u0161lenka byla, \u017ee mo\u017en\u00e1 kv\u016fli <code>ILIKE<\/code> na v\u0161ech t\u011bch dlouh\u00fdch URL (m\u00e1me v\u00edce ne\u017e 1,4 milionu <strong>unik\u00e1tn\u00edch\u00a0<\/strong>URL adres shrom\u00e1\u017ed\u011bn\u00fdch pro tento \u00fa\u010det) m\u016f\u017ee v\u00fdkonnost klesat.<\/p>\n<p><\/p>\n<p>Ale ne \u2014 to nen\u00ed ono!<\/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 rows)\n\n\u010cas: 5231.765 ms<\/code><\/pre>\n<p><\/p>\n<p>Samotn\u00fd dotaz na vyhled\u00e1v\u00e1n\u00ed podle vzoru trv\u00e1 pouze 5 sekund. Vyhled\u00e1v\u00e1n\u00ed podle vzoru na milionu unik\u00e1tn\u00edch URL rozhodn\u011b nen\u00ed probl\u00e9m.<\/p>\n<p><\/p>\n<p>Dal\u0161\u00ed podez\u0159el\u00fd na seznamu \u2014 n\u011bkolik <code>JOIN<\/code>. Mo\u017en\u00e1 jejich nadm\u011brn\u00e9 pou\u017e\u00edv\u00e1n\u00ed vedlo k zpomalen\u00ed? Obvykle <code>JOIN<\/code>\u2018y jsou nejv\u00edce z\u0159ejm\u00fdmi kandid\u00e1ty na probl\u00e9my s v\u00fdkonem, ale nev\u011b\u0159il jsem, \u017ee n\u00e1\u0161 p\u0159\u00edpad je typick\u00fd.<\/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 row)\n\n\u010cas: 147.851 ms<\/code><\/pre>\n<p><\/p>\n<p>A to tak\u00e9 nebyl n\u00e1\u0161 p\u0159\u00edpad. <code>JOIN<\/code>\u2018y se uk\u00e1zaly jako velmi rychl\u00e9.<\/p>\n<p><\/p>\n<h2>Z\u00fa\u017een\u00ed okruhu podez\u0159el\u00fdch<\/h2>\n<p><\/p>\n<p>Byl jsem p\u0159ipraven za\u010d\u00edt m\u011bnit dotaz, abych dos\u00e1hl jak\u00fdchkoli mo\u017en\u00fdch zlep\u0161en\u00ed v\u00fdkonu. My s t\u00fdmem jsme vyvinuli 2 hlavn\u00ed n\u00e1pady:<\/p>\n<p><\/p>\n<ul>\n<li><strong>Pou\u017e\u00edt EXISTS pro poddotaz URL<\/strong>: Cht\u011bli jsme je\u0161t\u011b jednou zkontrolovat, zda nejsou probl\u00e9my s poddotazem pro URL. Jedn\u00edm ze zp\u016fsob\u016f, jak toho dos\u00e1hnout, je jednodu\u0161e pou\u017e\u00edt <code>EXISTS<\/code>. <code>EXISTS<\/code> <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/functions-subquery.html#FUNCTIONS-SUBQUERY-EXISTS\">m\u016f\u017ee<\/a><\/noindex> v\u00fdrazn\u011b zlep\u0161it v\u00fdkon, jakmile se najde prvn\u00ed \u0159\u00e1dek podle podm\u00ednky.<\/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>Ano. Poddotaz, kdy\u017e je zabalen\u00fd do\u00a0<code>EXISTS<\/code>, d\u011bl\u00e1 v\u0161e extr\u00e9mn\u011b rychl\u00e9. Dal\u0161\u00ed logickou ot\u00e1zkou je, pro\u010d je dotaz s <code>JOIN<\/code>-y a samotn\u00e9 poddotazy rychl\u00e9 samostatn\u011b, ale spole\u010dn\u011b v\u00fdrazn\u011b zpomaluj\u00ed?<\/p>\n<p><\/p>\n<ul>\n<li><strong>P\u0159esuneme poddotaz do CTE <\/strong>: pokud je dotaz rychl\u00fd s\u00e1m o sob\u011b, m\u016f\u017eeme nejprve vypo\u010d\u00edtat rychl\u00fd v\u00fdsledek a pot\u00e9 jej poskytnout hlavn\u00edmu dotazu.<\/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>Ale i to bylo st\u00e1le velmi pomal\u00e9.<\/p>\n<p><\/p>\n<h2>Hled\u00e1me vin\u00edka<\/h2>\n<p><\/p>\n<p>Po celou dobu mi p\u0159ed o\u010dima sko\u010dila jedna drobnost, od kter\u00e9 jsem se neust\u00e1le odkl\u00e1n\u011bl. Ale proto\u017ee u\u017e nebylo nic jin\u00e9ho, rozhodl jsem se na to pod\u00edvat. Mluv\u00edm o <code>&amp;&amp;<\/code> oper\u00e1toru. Zat\u00edmco <code>EXISTS<\/code> to jen zlep\u0161ilo v\u00fdkon, <code>&amp;&amp;<\/code> byl jedin\u00fdm zb\u00fdvaj\u00edc\u00edm spole\u010dn\u00fdm faktorem ve v\u0161ech verz\u00edch pomal\u00e9ho dotazu.<\/p>\n<p><\/p>\n<p>D\u00edv\u00e1me se na <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/9.1\/functions-array.html\">dokumentaci<\/a><\/noindex>, vid\u00edme, \u017ee <code>&amp;&amp;<\/code> se pou\u017e\u00edv\u00e1, kdy\u017e pot\u0159ebujeme naj\u00edt spole\u010dn\u00e9 prvky mezi dv\u011bma poli.<\/p>\n<p><\/p>\n<p>V p\u016fvodn\u00edm dotazu je to:<\/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>Co\u017e znamen\u00e1, \u017ee prov\u00e1d\u00edme hled\u00e1n\u00ed podle vzoru na\u0161ich URL, pot\u00e9 nalezneme pr\u016fnik se v\u0161emi URL, kter\u00e9 maj\u00ed spole\u010dn\u00e9 z\u00e1znamy. To je trochu matouc\u00ed, proto\u017ee \u201eurls\u201c zde neodkazuje na tabulku obsahuj\u00edc\u00ed v\u0161echny URL adresy, ale na sloupec \u201eurls\u201c v tabulce <code>recording_data<\/code>.<\/p>\n<p><\/p>\n<p>S rostouc\u00edm podez\u0159en\u00edm ohledn\u011b <code>&amp;&amp;<\/code>, pokusil jsem se naj\u00edt jejich potvrzen\u00ed v pl\u00e1nu dotazu, kter\u00fd byl vygenerov\u00e1n. <code>EXPLAIN ANALYZE<\/code> (m\u011bl jsem ji\u017e ulo\u017een\u00fd pl\u00e1n, ale obvykle je pro m\u011b pohodln\u011bj\u0161\u00ed experimentovat v SQL, ne\u017e se sna\u017eit pochopit nepr\u016fhlednosti pl\u00e1nova\u010d\u016f dotaz\u016f).<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">Filtr: ((urls &amp;&amp; ($0)::text[]) A (r_time &gt; '2018-12-17 12:17:23+00'::timestamp with time zone) A (r_time = '5'::double precision) A (num_of_pages &gt; 0))\n                           \u0158\u00e1dky odstran\u011bn\u00e9 filtrem: 52710<\/code><\/pre>\n<p><\/p>\n<p>Bylo tam n\u011bkolik \u0159\u00e1dk\u016f filtr\u016f pouze z <code>&amp;&amp;<\/code>. Co\u017e znamenalo, \u017ee tato operace byla nejen n\u00e1kladn\u00e1, ale tak\u00e9 se prov\u00e1d\u011bla n\u011bkolikr\u00e1t.<\/p>\n<p><\/p>\n<p>Ov\u011b\u0159il jsem to t\u00edm, \u017ee jsem izoloval podm\u00ednku<\/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>Tento dotaz byl prov\u00e1d\u011bn pomalu. Proto\u017ee <code>JOIN<\/code>-y jsou rychl\u00e9 a poddotazy jsou rychl\u00e9, z\u016fstal pouze <code>&amp;&amp;<\/code> oper\u00e1tor.<\/p>\n<p><\/p>\n<p>Toto je v\u0161ak kl\u00ed\u010dov\u00e1 operace. V\u017edy mus\u00edme hledat v cel\u00e9 hlavn\u00ed tabulce URL adres, abychom mohli hledat podle vzoru, a v\u017edy mus\u00edme naj\u00edt pr\u016fniky. Nem\u016f\u017eeme hledat podle z\u00e1znam\u016f URL p\u0159\u00edmo, proto\u017ee jsou to pouze ID odkazuj\u00edc\u00ed na <code>urls<\/code>.<\/p>\n<p><\/p>\n<h2>Na cest\u011b k \u0159e\u0161en\u00ed<\/h2>\n<p><\/p>\n<p><code>&amp;&amp;<\/code> je pomal\u00e1, proto\u017ee ob\u011b sady jsou obrovsk\u00e9. Operace bude relativn\u011b rychl\u00e1, pokud nahrad\u00edm <code>urls<\/code> na <code>{ \"http:\/\/google.com\/\", \"http:\/\/wingify.com\/\" }<\/code>.<\/p>\n<p><\/p>\n<p>Za\u010dal jsem hledat zp\u016fsob, jak v Postgres prov\u00e9st pr\u016fnik mno\u017ein bez pou\u017eit\u00ed <code>&amp;&amp;<\/code>, ale bez zvl\u00e1\u0161tn\u00edho \u00fasp\u011bchu.<\/p>\n<p><\/p>\n<p>Nakonec jsme se rozhodli prost\u011b vy\u0159e\u0161it probl\u00e9m izolovan\u011b: dej mi v\u0161echny <code>urls<\/code> \u0159\u00e1dky, pro kter\u00e9 URL odpov\u00edd\u00e1 \u0161ablon\u011b. Bez dal\u0161\u00edch podm\u00ednek to bude \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>Nam\u00edsto\u00a0<code>JOIN<\/code> sintaxi jsem jednodu\u0161e pou\u017eil poddotaz a rozvinul <code>recording_data.urls<\/code> pole, abych mohl p\u0159\u00edmo pou\u017e\u00edt podm\u00ednku v <code>nen\u00ed spolehliv\u00fd:<\/code>.<\/p>\n<p><\/p>\n<p>Nejd\u016fle\u017eit\u011bj\u0161\u00ed je, \u017ee <code>&amp;&amp;<\/code> se pou\u017e\u00edv\u00e1 k ov\u011b\u0159en\u00ed, zda dan\u00fd z\u00e1znam obsahuje odpov\u00eddaj\u00edc\u00ed URL adresu. P\u0159i pohledu zbl\u00edzka m\u016f\u017eeme v t\u00e9to operaci vid\u011bt proch\u00e1zen\u00ed prvky pole (nebo \u0159\u00e1dky tabulky) a zastaven\u00ed, kdy\u017e se podm\u00ednka spln\u00ed (shoda). Nic v\u00e1m to nep\u0159ipom\u00edn\u00e1? Ano, <code>EXISTS<\/code>.<\/p>\n<p><\/p>\n<p>Proto\u017ee na <code>recording_data.urls<\/code> se m\u016f\u017ee odkazovat zven\u010d\u00ed kontextu poddotazu, kdy\u017e k tomu dojde, m\u016f\u017eeme se vr\u00e1tit k na\u0161emu star\u00e9mu p\u0159\u00edteli <code>EXISTS<\/code> a obalit ho poddotazem.<\/p>\n<p><\/p>\n<p>Kdy\u017e to v\u0161echno spoj\u00edme dohromady, dost\u00e1v\u00e1me kone\u010dn\u00fd optimalizovan\u00fd dotaz:<\/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  (  1 = 1  )  \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    AND EXISTS(\n        SELECT urls.url\n        FROM \n            acc_{account_id}.urls as urls,\n            (SELECT unnest(urls) AS rec_url_id FROM acc_{account_id}.recording_data) \n            AS unrolled_urls\n        WHERE\n            urls.id = unrolled_urls.rec_url_id AND\n            urls.url  ILIKE  '%enterprise_customer.com\/jobs%'\n    );\n<\/code><\/pre>\n<p><\/p>\n<p>A kone\u010dn\u00e9 \u010dasov\u00e1n\u00ed <code>\u010cas: 1898.717 ms<\/code> Je \u010das na oslavu?!?<\/p>\n<p><\/p>\n<p>Nen\u00ed to tak rychl\u00e9! Nejprve je pot\u0159eba zkontrolovat spr\u00e1vnost. Byl jsem velmi podez\u0159\u00edvav\u00fd k <code>EXISTS<\/code> optimalizaci, proto\u017ee m\u011bn\u00ed logiku na d\u0159\u00edv\u011bj\u0161\u00ed dokon\u010den\u00ed. Mus\u00edme si b\u00fdt jisti, \u017ee jsme do dotazu nep\u0159idali nepatrnou chybu.<\/p>\n<p><\/p>\n<p>Jednoduch\u00e1 kontrola spo\u010d\u00edvala v proveden\u00ed <code>count(*)<\/code> na pomal\u00fdch i rychl\u00fdch dotazech pro velk\u00e9 mno\u017estv\u00ed r\u016fzn\u00fdch datov\u00fdch sad. Pot\u00e9 jsem ru\u010dn\u011b zkontroloval spr\u00e1vnost v\u0161ech v\u00fdsledk\u016f pro mal\u00e9 podmno\u017einy dat.<\/p>\n<p><\/p>\n<p>V\u0161echny kontroly m\u011bly stabiln\u011b pozitivn\u00ed v\u00fdsledky. V\u0161echno jsme napravili!<\/p>\n<p><\/p>\n<h2>Z\u00edskan\u00e9 Lekce<\/h2>\n<p><\/p>\n<p>Z tohoto p\u0159\u00edb\u011bhu lze vyvodit mnoho lekc\u00ed:<\/p>\n<p><\/p>\n<ol>\n<li>Pl\u00e1ny dotaz\u016f nevypr\u00e1v\u011bj\u00ed celou historii, ale mohou poskytnout vod\u00edtka<\/li>\n<li>Hlavn\u00ed podez\u0159el\u00ed nejsou v\u017edy skute\u010dn\u00fdmi vin\u00edky<\/li>\n<li>Pomal\u00e9 dotazy lze rozd\u011blit, aby se izolovaly \u00fazk\u00e1 m\u00edsta<\/li>\n<li>Ne v\u0161echny optimalizace jsou z povahy reduk\u010dn\u00ed<\/li>\n<li>Pou\u017eit\u00ed <code>EXIST<\/code>, kde je to mo\u017en\u00e9, m\u016f\u017ee v\u00e9st k drastick\u00e9mu n\u00e1r\u016fstu v\u00fdkonu<\/li>\n<\/ol>\n<p><\/p>\n<h2>V\u00fdstup<\/h2>\n<p><\/p>\n<p>P\u0159e\u0161li jsme od \u010dasu dotazu z p\u0159ibli\u017en\u011b 24 minut na 2 sekundy \u2014 to je zna\u010dn\u00fd n\u00e1r\u016fst v\u00fdkonu! A\u010dkoli tento \u010dl\u00e1nek je pom\u011brn\u011b dlouh\u00fd, v\u0161echny experimenty, kter\u00e9 jsme provedli, prob\u011bhly b\u011bhem jednoho dne a odhadujeme, \u017ee optimalizace a testov\u00e1n\u00ed trvalo 1,5 a\u017e 2 hodiny.<\/p>\n<p><\/p>\n<p>SQL je \u00fa\u017easn\u00fd jazyk, pokud se ho neboj\u00edte, a pokus\u00edte se ho poznat a vyu\u017e\u00edt. S dobr\u00fdm pochopen\u00edm toho, jak se SQL dotazy vyhodnocuj\u00ed, jak datab\u00e1ze generuje pl\u00e1ny dotaz\u016f, jak funguj\u00ed indexy a velikosti dat, se kter\u00fdmi m\u00e1te co do \u010din\u011bn\u00ed, m\u016f\u017eete velmi dob\u0159e usp\u011bt v optimalizaci dotaz\u016f. Nen\u00ed m\u00e9n\u011b d\u016fle\u017eit\u00e9 pokra\u010dovat ve zkou\u0161en\u00ed r\u016fzn\u00fdch p\u0159\u00edstup\u016f a postupn\u011b rozeb\u00edrat probl\u00e9m, abyste na\u0161li \u00fazk\u00e1 m\u00edsta.<\/p>\n<p><\/p>\n<p>Nejlep\u0161\u00ed \u010d\u00e1st dosa\u017een\u00ed takov\u00fdch v\u00fdsledk\u016f je v\u00fdrazn\u00e9 zlep\u0161en\u00ed rychlosti \u2013 kdy\u017e se zpr\u00e1va, kter\u00e1 se d\u0159\u00edve ani nena\u010d\u00edtala, nyn\u00ed na\u010d\u00edt\u00e1 t\u00e9m\u011b\u0159 okam\u017eit\u011b.<\/p>\n<p><\/p>\n<p><strong>Zvl\u00e1\u0161tn\u00ed pod\u011bkov\u00e1n\u00ed\u00a0<\/strong>m\u00fdm koleg\u016fm\u00a0<em>v t\u00fdmu Aditya Mishra<\/em>,\u00a0<em>Aditya Gaur\u00a0<\/em>a\u00a0<em><noindex><a rel=\"nofollow\" href=\"https:\/\/twitter.com\/s0ftvar\">Varun Malhotra\u00a0<\/a><\/noindex><\/em>za brainstorming a\u00a0<em>Dinkar Pandir\u00a0<\/em>za to, \u017ee na\u0161el d\u016fle\u017eitou chybu v na\u0161em fin\u00e1ln\u00edm po\u017eadavku, ne\u017e jsme se s n\u00edm definitivn\u011b rozlou\u010dili!<\/p>\n<p>Zdroj: <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\/cs\/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=\"cs_CZ\" \/>\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\/cs\/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\udd47P\u0159\u00edb\u011bh jednoho SQL vy\u0161et\u0159ov\u00e1n\u00ed | ProHoster","description":"V prosinci minul\u00e9ho roku jsem obdr\u017eel zaj\u00edmavou zpr\u00e1vu o chyb\u011b od t\u00fdmu podpory VWO. \u010cas na\u010d\u00edt\u00e1n\u00ed jedn\u00e9 analytick\u00e9 zpr\u00e1vy pro velk\u00e9ho korpor\u00e1tn\u00edho klienta se zd\u00e1l b\u00fdt nep\u0159im\u011b\u0159en\u011b dlouh\u00fd.","canonical_url":"https:\/\/prohoster.info\/cs\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"cs_CZ","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\/cs\/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\/cs\/wp-json\/wp\/v2\/posts\/35259","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/comments?post=35259"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/posts\/35259\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/media?parent=35259"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/categories?post=35259"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/tags?post=35259"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}