Autor Zpráva
Ffreeman
Profil *
Ahoj,

mám postgre databázi internetového obchodu, jehož schema můžete stáhnout NA TÉTO ADRESE

Soustředím se především na produkt a to konkrétně na výpis katalogu. Pro tento výpis potřebuji informace z tabulek (v závorce je přibližný počet záznamů, kde znak + vyjadřuje potenciální růst):

produkt (3000)
produkt_translation (3000+)
cena (10000+)
vyrobce (80)
rw_string (4670)
prirazeni_poplatku (40++)
poplatek (20)
dan_produktu (3000)
dan (3)
kategorie2produkt (8000)
produkt2privlastek (0+++)
privlastek_produktu (10) // přívlastkem je míněno označení např. pro novinku, akci apod.

Již v současnosti mám obrovské problémy s výkonem celé databáze. Aplikace je provozována na virtual hostingu a již přišly stížnosti z nadměrného vytěžování celého serveru procesem postgre (až 90 %). Brzdí to samozřejmě i celou aplikaci, která je neskutečně pomalá.

Aplikace je psaná v PHP a používám ORM nástroj Doctrine.

Kód v PHP vypadá zhruba takto (je tam několik IFů, ty jsem odstranil):
$q->select('p.kod_produktu, t.nazev_produktu, t.charakteristika, c.kod_cs, c.kod_meny, c.hodnota_ceny, dan.vyse')
								->from('Produkt p')
								->leftJoin('p.Dan dan')
								->innerJoin('p.Cena cena')
								->leftJoin('p.Poplatek pop')
								->innerJoin('p.Vyrobce vyr')
								->innerJoin('p.Kategorie2produkt kat')
								->leftJoin('p.PrivlastekProduktu pp')
								->where('cena.kon_datum IS NULL')
								->andWhere('cena.kod_cs = ?', $params['kod_cs'])
								->andWhere('cena.kod_meny = ?', $params['kod_meny'])
$q->andWhereIn('kat.kod_kategorie', $params['kod_kategorie']);
$q->orderBy($params['orderBy'].' '.$smer);
$q->limit($params['limit']);



Ten posílá databázi dotazy, jako je tady tento:
SELECT p.kod_produktu AS p__kod_produktu, d.kod_dane AS d__kod_dane, d.vyse AS d__vyse, p6.kod_produktu AS p6__kod_produktu, p6.lang AS p6__lang, p6.nazev_produktu AS p6__nazev_produktu, p6.charakteristika AS p6__charakteristika 
FROM produkt p 
LEFT JOIN dan_produktu d2 ON (p.kod_produktu = d2.kod_produktu)
LEFT JOIN dan d ON d.kod_dane = d2.kod_dane 
INNER JOIN cena c ON p.kod_produktu = c.kod_produktu
LEFT JOIN prirazeni_poplatku p3 ON (p.kod_produktu = p3.kod_produktu)
LEFT JOIN poplatek p2 ON p2.kod_poplatku = p3.kod_poplatku 
INNER JOIN vyrobce v ON p.kod_vyrobce = v.kod_vyrobce 
INNER JOIN kategorie2produkt k ON p.kod_produktu = k.kod_produktu 
LEFT JOIN produkt2privlastek p5 ON (p.kod_produktu = p5.kod_produktu) 
LEFT JOIN privlastek_produktu p4 ON p4.id_privlastek = p5.id_privlastek 
LEFT JOIN produkt_translation p6 ON p.kod_produktu = p6.kod_produktu 
WHERE p.kod_produktu IN 
(SELECT doctrine_subquery_alias.kod_produktu 
FROM (SELECT DISTINCT p7.kod_produktu, p12.nazev_produktu 
FROM produkt p7 
LEFT JOIN dan_produktu d4 ON (p7.kod_produktu = d4.kod_produktu) 
LEFT JOIN dan d3 ON d3.kod_dane = d4.kod_dane 
INNER JOIN cena c2 ON p7.kod_produktu = c2.kod_produktu 
LEFT JOIN prirazeni_poplatku p9 ON (p7.kod_produktu = p9.kod_produktu) 
LEFT JOIN poplatek p8 ON p8.kod_poplatku = p9.kod_poplatku 
INNER JOIN vyrobce v2 ON p7.kod_vyrobce = v2.kod_vyrobce 
INNER JOIN kategorie2produkt k2 ON p7.kod_produktu = k2.kod_produktu 
LEFT JOIN produkt2privlastek p11 ON (p7.kod_produktu = p11.kod_produktu) 
LEFT JOIN privlastek_produktu p10 ON p10.id_privlastek = p11.id_privlastek 
LEFT JOIN produkt_translation p12 ON p7.kod_produktu = p12.kod_produktu 
WHERE c2.kon_datum IS NULL 
AND c2.kod_cs = 'CS600001' 
AND c2.kod_meny = 'CZK' 
AND k2.kod_kategorie IN ('9675') 
AND (p12.lang = 'cz') 
AND (p7.deleted_at IS NULL) 
ORDER BY p12.nazev_produktu ASC LIMIT 12) AS doctrine_subquery_alias) 
AND c.kon_datum IS NULL 
AND c.kod_cs = 'CS600001' 
AND c.kod_meny = 'CZK' 
AND k.kod_kategorie IN ('9675') 
AND (p6.lang = 'cz') 
AND (p.deleted_at IS NULL) 
ORDER BY p6.nazev_produktu ASC


Možná již někteří, kteří tento příspěvek čtou, kroutí hlavou a říkají si, proč jsem nepoužil příkaz explain. Háček je v tom, že i přes načtení mnoha materiálů, nejsem pravděpodobně schopen správě pochopit, co mi vlastně čísla znamenají.

Příkaz explain analyze + předchozí dotaz naleznete ZDE (link na textový soubor)


Celý dotaz lze přepsat do následujícího dotazu, který je, alespoň dle mého chápání, daleko méně náročný:

SELECT p.kod_produktu,p.id_rw_string, p.kod_vyrobce, t.nazev_produktu, t.charakteristika, c.hodnota_ceny, v.nazev_vyrobce, rw.string, d.vyse, pop.poplatek
FROM produkt as p
INNER JOIN produkt_translation as t ON (p.kod_produktu = t.kod_produktu)
INNER JOIN cena as c ON (p.kod_produktu = c.kod_produktu)
INNER JOIN vyrobce as v ON (p.kod_vyrobce = v.kod_vyrobce)
INNER JOIN rw_string as rw ON (p.id_rw_string = rw.id_rw_string)
LEFT JOIN prirazeni_poplatku as pri ON (p.kod_produktu = pri.kod_produktu)
LEFT JOIN poplatek as pop ON (pop.kod_poplatku = pri.kod_poplatku)
LEFT JOIN dan_produktu as dp ON (p.kod_produktu = dp.kod_produktu)
INNER JOIN dan as d ON (d.kod_dane = dp.kod_dane)
INNER JOIN kategorie2produkt as k2p ON (p.kod_produktu = k2p.kod_produktu)
LEFT JOIN produkt2privlastek as p2p ON (p.kod_produktu = p2p.kod_produktu)
LEFT JOIN privlastek_produktu as pp ON (pp.id_privlastek = p2p.id_privlastek)
WHERE  c.kod_meny = 'CZK'
AND p.deleted_at IS NULL
AND c.kod_cs = 'CS600001'
AND k2p.kod_kategorie IN ('9675')
AND t.lang = 'cz'
ORDER BY t.nazev_produktu ASC
LIMIT 12


explain anylze + Select ... pak vypadá TAKTO (link na textový soubor)

Otázkou je, jak vnutit Doctrine, aby svoje dotazy "neprznil", jak to dělá doteď.

Jak tuto záležitost radikálně zrychlit? Uvažoval jsem nad nějakými dočasnými tabulkami, ale vzhledem k tomu, že je celá aplikace napsána v Doctrine, bylo by dosti těžké převést data z temporární tabulky do objektů.

Další možnost, která mě napadla, je kompletní cache a to buď výstupu nebo objektů. Ta ovšem funguje pouze v případě, že člověk nevyhledává podle názvu, nefiltruje podle výrobce apod.

Má někdo nějaký návrh, co s tím?

Předem děkuji za jakoukoli pomoc či názor.

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: