Návrh na zlepšení výkonu (Nahrazení NetteDatabase a pole – array)
- GEpic
- Člen | 566
Ahoj, co se týče PHP, nikdy jsem neřešil výkonnostní problém, ale vím, že určité rezervy tu jsou a vím že i možnosti, každopádně chtěl bych radu od Vás, co využíváte vy.
Využívám následující kod pro výpis kalendáře akcí. Nejprve se iteruje skrze první kurt, poté druhý, poté třetí… atd. V každém kurtu se navíc získávají události pro určitý den, a poté navíc pro určitý termín.
Funkce getEventTemplate
poté vrací instanci objektu Event,
který existuje vždy (budto hráčský event, uzamknutý, opakovatelný,
atd..). Každopádně problém je, že při každém volání této funkce
dochází k prohledání databáze, zda-li určitá událost existuje. To ve
výsledku znamená, že je při týdenním výpisu pro všechny kurty a všechny
časy voláno přes 460 dotazů na databázi a to trvá od 351ms až po
3500ms.
Dotazy jsou malé, a rychlé, ale jejich velký počet přesto zvyšuje
odezvu. Navíc ukládání do array
mi už taky nepřijde
úplně košér.
Takže mé hlavní dotazy – Jak zredukovat dotazy na databázi? A další dotaz. Jaké využít uložiště? Mapu? Či něco jiného?
Všem děkuji za jakoukoliv odpověd. Čísla jsou z localhostu, na serveu vše běží rychle a řeší to navýšení výkonu serveru, ale… to není řešení do budoucna.
/**
* BUT!
* Structure for week is different, so we need a little bit different iterating system there
* Because we need to render firstly 1. court, than time and lastly every date in each column
* @param $JNY
* @param $userId
* @return array
*/
private function getTemplatesForWeek($JNY, $userId)
{
$events = array();
$dates = array();
// If we want to get all events from database for 7 days, we need these dates
for ($i = 0; $i < 7; $i++)
{
$dates[$i] = $this->date->get_JNY_Offset($JNY, $i);
}
foreach ($this->getCourts() as $c => $name)
{
foreach ($this->getEventTimesList() as $time => $id)
{
foreach ($dates as $date => $d)
{
$events[$c][$time][$d] = $this->getEventTemplate($d, $time, $c, $userId);
}
}
}
Debugger::barDump($events);
return $events;
}
/**
* Get 1 concrete Event Template according to params
*
* @param $JNY
* @param $time
* @param $c
* @param $userId
* @return Event|bool
*/
private function getEventTemplate($JNY, $time, $c, $userId)
{
$stamp = $this->date->get_Timestamp($JNY, $time);
// Get Event
$event = $this->database->table('events')->where('timestamp', $stamp)->where('court', $c)->fetch();
// rest of the logic... and returning Event instance
}
barDump
poté vypadá následovně:
array (2)
1 => array (16)
7 => array (7)
"1.2.2016" => App\Model\Event\Event #f556 { ... }
"2.2.2016" => App\Model\Event\Event #ad2a { ... }
"3.2.2016" => App\Model\Event\Event #7ba2 { ... }
"4.2.2016" => App\Model\Event\Event #2a8d { ... }
"5.2.2016" => App\Model\Event\Event #7c05 { ... }
"6.2.2016" => App\Model\Event\Event #893f { ... }
"7.2.2016" => App\Model\Event\Event #2117 { ... }
8 => array (7)
9 => array (7)
10 => array (7)
...
21 => array (7)
22 => array (7)
2 => array (16)
7 => array (7)
8 => array (7)
...
21 => array (7)
22 => array (7)
Editoval GEpic (1. 2. 2016 2:15)
- CZechBoY
- Člen | 3608
Pokud budeš určitě chtít vypsat všechny události všech kurtů tak bych si
- vybral všechny události, který budu vypisovat
- seskupil (group by) podle id akce
- dal do asociativního pole podle čísla kurtu
- uložil bych si to pole někam do cache, abych to nemusel pořád generovat znova
- potom už vybíral z cache
- GEpic
- Člen | 566
@F.Vesely To nevím, jestli je možné, stejným způsobem, jako naplnuji pole, je stejným způsobem poté i vypisuji do tabulek. Samozřejmě tabulky jsou propojené, pracuji poté se selectem typu $event->user->username a pod.
@Šaman Na serveru je databáze lokálně, na lokálu je taky lokálně
@CZechBoY Četl jsem už dost o Cache a její kontraproduktivitě, ale zdá se, že v tomto případě doopravdy bude užitečná.
Editoval GEpic (1. 2. 2016 16:15)
- Michal Hlávka
- Člen | 190
@GEpic při nejhorším si aspoň optimalizuj funkci
getEventTemplate
ať se až při sedmém volání té funkce,
zavolá dotaz na databázi s podmínkou date < x && x > date a
né jindy. Ona si během iterací nasbírá timestampy, které pak všechny
použije v jednom dotazu.
To můžeš udělat tak, že si budeš zjišťovat jestli ti funkce vrátila instanci App\Model\Event\Event, tehdy budeš vědět, že máš vynulovat timestampy a v druhém případě, ti ta funkce bude vracet timestampy, které do ní znova vhodíš a ty se tam budou hromadit až do fáze, kdy je funkce použije a vráti instanci Event.
Dle mého je zrovna tohle ta nejhorší čast, která by stála za to optimalizovat a hodně by databázi ušetřila.
Editoval emptywall (2. 2. 2016 12:55)
- F.Vesely
- Člen | 369
Co tak na to koukam, tak to pole jsi schopnej vytvorit jednim selectem.
Neco jako:
foreach($this->getAllEventsInWeek($week) as $event) {
$events[$event->court_id][$event->timestamp->format('H')][$event->timestamp->format('j.n.Y')][] = $event;
}
Editoval F.Vesely (3. 2. 2016 8:28)
- GEpic
- Člen | 566
Jen pro info. Instanci Event to vrací vždy. I prázdný nezarezervovaný Event má své nastavení, a proto úplně každé pole nehledě na jeho typ vždy vrací instanci Event, pokud ho to nenajde v databázi, vrátí to prázdný Event, který má také specifické nastavení. Proto se funkce nejmenuje getEvent (která existuje, ale vrací ActiveRow), ale getEventTemplate, která obsahuje nastavení události, typů událostí je v tuto chvíli cca 7.
Tzn. že do šablony nepředávám Database Selection / ActiveRow(s), ale zpracované instance třídy Event, které obsahují to nejnutnější pro vykreslení jednotlivých typů událostí (které samozřejmě v databázi neukládám).
Jen pro představu, jak to funguje v praxi, se můžete podívat například zde.
EDIT: Pořád nad tím přemýšlím, ale jak to vybrat jedním selektem a pak roztřídit do pole mě v tuto chvíli nenapadá.
Editoval GEpic (3. 2. 2016 8:42)