Fórum Root.cz
Hlavní témata => Server => Téma založeno: registrovany123 27. 05. 2024, 22:24:50
-
Ahoj,
vím že tady občas zavítají experti na Postgres, tak třeba budu mít štěstí a pomůžou.
Mám v databázi ve 2 tabulkách asi 6 - 10 mil. záznamů, a service zpracovává přibližně 1 request za vteřinu, načež v rámci toho jednoho requestu se udělá asi tak 8 jednoduchých selectů a 2 inserty.
Žádné další requesty do service ani do databáze nechodí. Tedy databáze zpracovává jen výše uvedené requesty.
99% requestů je hotových do 50ms, ale 1% requestů má spike, v délce trvání 150ms - 400ms.
Měřením jsem zjistil, že k těmto spikům dochází jak v AWS RDS se 4GB RAM a 2 vcpu, tak i na mém localhostu, kde mám Postgres rozjeto v Dockeru, opět se 4GB RAM a 2 vcpu.
Dále jsem měřením zjistil, že spike se může objevit na libovolném query, a to jak na selectu, tak na insertu, tedy z toho usuzuji, že v queries problém není, ostatně, průměrná délka requestu je jen nějakých 30ms, což optimální queries potvrzuje.
Všechny query mají řádně udělané potřebné indexy a query plany jsou zkontrolovány a jsou optimální.
Měl jsem podezření, že za spiky můžou checkpointy, avšak nakolik se spike vyskytne pravidelné každých 2-10 minut, checkpoint mám za to že je přecejen pravidelnější, každých 5 minut, a takovoutu železnou pravidelnost se nezdá, že by spiky měly.
V databázovém logu vidím vypsané queries a checkpointy, nikdy zde však nic podezřelého nepozoruji, všechno se zdá být standardní a opakující se s každým requestem, nikde žádný hint.
Nesetkal se s tím někdo už?
-
Začal bych přidáním rozšíření pg_stat_statements, budete mít přesná data a uvidíte co přesně se Vám tam děje.
A jen hinty:
máte shared_buffers velký adekvátně velikosti dat?
máte dostatečně velkou work_mem?
jak velký máte bloat v indexech?
neběží Vám tam v tu dobu něco jiného, co ovlivní výkon nebo obsah bufferu, .... třeba autovacuum je kandidát, může být nastaveno příliš agresivně.
-
Zkusil bych jednoduchý select pouštěný z psql. Ta latence může vzniknout kdekoliv na jakékoliv vrstvě. To může dělat Java, podobné peeky jsem viděl a dělala to virtualizace. Zkuste z bashe v cyklu pustit SELECT 1 a sleep 1 .. pokud dochází k latencím v Postgresu, tak SELECT 1 by se měl zpomalit také. Můžete zkoušet odkud tento skript pustit. 100ms latenci vám může udělat síť, firewall, ...
-
Ak mate dost kapacity skusat, skuste analyzovat logy a hlavne zapnut viac logovania. Osobne som (hadam pred 6 rokmi) pouzival na produkcii https://github.com/darold/pgbadger . Teda na produkcii sa logovalo, ale analyza logov sa robila na inom stroji pravidelne asi raz tyzdenne a pozretie sa do reportu pomohlo odhalit zavedene drobne chyby.
Mozno Vas to niekam nasmeruje.
PS: Monitorovat ping , alebo ine vrstvy podobnym sposobom, moze byt tiez prospesne. Klnudne to moze skoncit na veci ako "esxi si raz za den uspi stroj na 7 sekund, lebo backup je tak nakonfigurovany a potom ntp riesi problem s posunom casu"... (teda ori localhostovom dockeri asi nie a ani pri aws. Skor myslim typovo na nejakej takejto haluzi)
-
Zkusil bych jednoduchý select pouštěný z psql. Ta latence může vzniknout kdekoliv na jakékoliv vrstvě. To může dělat Java, podobné peeky jsem viděl a dělala to virtualizace. Zkuste z bashe v cyklu pustit SELECT 1 a sleep 1 .. pokud dochází k latencím v Postgresu, tak SELECT 1 by se měl zpomalit také. Můžete zkoušet odkud tento skript pustit. 100ms latenci vám může udělat síť, firewall, ...
Pán píše, že se mu to takto chová i na localhostu, kde mu to běží v dockeru. V tomhle scénáři je síťová vrstva asi nepravděpodobná.
-
Zkusil bych jednoduchý select pouštěný z psql. Ta latence může vzniknout kdekoliv na jakékoliv vrstvě. To může dělat Java, podobné peeky jsem viděl a dělala to virtualizace. Zkuste z bashe v cyklu pustit SELECT 1 a sleep 1 .. pokud dochází k latencím v Postgresu, tak SELECT 1 by se měl zpomalit také. Můžete zkoušet odkud tento skript pustit. 100ms latenci vám může udělat síť, firewall, ...
Pán píše, že se mu to takto chová i na localhostu, kde mu to běží v dockeru. V tomhle scénáři je síťová vrstva asi nepravděpodobná.
Pokud si je jistý, že všude komunikuje přes socket, tak tam asi ne. Někde se může použít TCP vůči localhostu - a už jsem slyšel, že problémy dělalo třeba problematické DNSko. Je otázkou, co je pomalé - jestli samotný dotaz, nebo připojení do pg. S virtualizací už jsem viděl tolik problémů
-
Zkusil bych jednoduchý select pouštěný z psql. Ta latence může vzniknout kdekoliv na jakékoliv vrstvě. To může dělat Java, podobné peeky jsem viděl a dělala to virtualizace. Zkuste z bashe v cyklu pustit SELECT 1 a sleep 1 .. pokud dochází k latencím v Postgresu, tak SELECT 1 by se měl zpomalit také. Můžete zkoušet odkud tento skript pustit. 100ms latenci vám může udělat síť, firewall, ...
Pán píše, že se mu to takto chová i na localhostu, kde mu to běží v dockeru. V tomhle scénáři je síťová vrstva asi nepravděpodobná.
Pokud si je jistý, že všude komunikuje přes socket, tak tam asi ne. Někde se může použít TCP vůči localhostu - a už jsem slyšel, že problémy dělalo třeba problematické DNSko. Je otázkou, co je pomalé - jestli samotný dotaz, nebo připojení do pg. S virtualizací už jsem viděl tolik problémů
Nechci zakládat další vlákno, ale jaký je Váš názor na PostgreSQL v kontejneru? Dříve myslím nedoporučované pro databáze obecně, ale viděl jsem to už na několika produkcích.
-
Nechci zakládat další vlákno, ale jaký je Váš názor na PostgreSQL v kontejneru? Dříve myslím nedoporučované pro databáze obecně, ale viděl jsem to už na několika produkcích.
Já to zrovna nemusím - a třeba provozovat kontejner třeba čistě pro Postgres mi přijde úlet (zvlášť na produkci - na testovacích prostředích nebo v lokále na vývoj to může být něco jiného). Praxe je, že když se to rozjede a nejsou s tím problémy, tak to běží. Ale pokud tam nějaké problémy jsou, tak je to další vrstva s kterou musíte počítat, a kde ty problémy jsou hezky skryté. Někdy jsou přímo zdrojem problémů, jindy multiplikují problémy na db úrovni. Virtualizace se běžně používá, pokud máte server s 0.5TB RAM a nemáte db, kterou byste to využili. Ale správně to nakonfigurovat nemusí být legrace. Mám zákazníka, kde db provozovali několik let ve virtualizaci s naprosto tragickým výkonem - fixlo se to spíš náhodně po 3 měsících analýz, kdy já už jsem byl v koncích a stejně se tam objevovala magická chyba, která se fixla až další rok. Teď už to dva roky běží suprově bez problémů.
-
Já to zrovna nemusím - a třeba provozovat kontejner třeba čistě pro Postgres mi přijde úlet (zvlášť na produkci - na testovacích prostředích nebo v lokále na vývoj to může být něco jiného). Praxe je, že když se to rozjede a nejsou s tím problémy, tak to běží. Ale pokud tam nějaké problémy jsou, tak je to další vrstva s kterou musíte počítat, a kde ty problémy jsou hezky skryté. Někdy jsou přímo zdrojem problémů, jindy multiplikují problémy na db úrovni. Virtualizace se běžně používá, pokud máte server s 0.5TB RAM a nemáte db, kterou byste to využili. Ale správně to nakonfigurovat nemusí být legrace. Mám zákazníka, kde db provozovali několik let ve virtualizaci s naprosto tragickým výkonem - fixlo se to spíš náhodně po 3 měsících analýz, kdy já už jsem byl v koncích a stejně se tam objevovala magická chyba, která se fixla až další rok. Teď už to dva roky běží suprově bez problémů.
My jsme provozovali PG v vmware 10 let (kdy jsem u toho byl) a žádný problém. Jak jsem psal níže, vždy je potřeba dávat pozor na úložiště. S výkonem disků ve vmware jsme nikdy neměli problém, pokud nebyl problém vyloženě na vrstvě pod tím. Soukromě jsem ještě navíc provozoval KVM, kdy disky virtuálek byly jednotlivé LV v LVM a opět, rychlost nebyla žádný problém.
U virtualizace je obecně vhodné si dát pozor, co dalšího běží v ostatních virtuálkách. Dávat na jedno diskové pole 50 databázových serverů je problém vždy.
Nechci zakládat další vlákno, ale jaký je Váš názor na PostgreSQL v kontejneru? Dříve myslím nedoporučované pro databáze obecně, ale viděl jsem to už na několika produkcích.
Tak záleží na tom, co se přesně myslí tím "kontejner". Já provozuju už skoro 10 let DB servery v kontejnerech, jak na Linuxu (nspawn), tak na FreeBSD (jail). Kontejner je jen oddělený namespace pro procesy. Data se ukládají stejně tak jako tak na disky a u kontejneru odpadá virtualizace úložiště. Takže PG v Jailech ukládá na nativní ZFS. Úplně stejně jako bez kontejnerizace. Pokud nějaká jiná kontejnerizační služba ještě navíc nějak virtualizuje úložiště, tak tam dopad jistě bude.
-
Díky za tipy, musím to ještě přelouskat.
Zatím ale jen řeknu, že lokalizovat kde je problém, jestli síť nebo postgres, to už bych měl, kdyby postgres do logů zapisovalo i milisekundy a nejen sekundy toho, kdy jaký query proběhl.
-
Díky za tipy, musím to ještě přelouskat.
Zatím ale jen řeknu, že lokalizovat kde je problém, jestli síť nebo postgres, to už bych měl, kdyby postgres do logů zapisovalo i milisekundy a nejen sekundy toho, kdy jaký query proběhl.
Nastavte si log_line_prefix https://www.postgresql.org/docs/current/runtime-config-logging.html %m
-
Podobny problem sme riesili aj mi. Zapnutie viac logovania to cele este k tomu spomalilo. jedine co sa podarilo vylucit bola siet a disky, tak zostal len Potgre.
-
Podobny problem sme riesili aj mi. Zapnutie viac logovania to cele este k tomu spomalilo. jedine co sa podarilo vylucit bola siet a disky, tak zostal len Potgre.
Jestli je několik málo dotazů za sec, tak i kdyby se logovalo vše, tak by to nic nemělo udělat. RDS má obecně negarantovaný výkon IO, a 4GB RAM stroje jsou na dema - takže garance výkonu IO tam budou minimální. Tam je otázkou jestli tam aplikačně nedojde k nějakému problému s cache nebo k vynucenému checkpointu. Další věcí může být lagování file systému - občas je nutné na o.s. snížit dirty buffers ratio. Postgres nemá důvod, pokud nečeká na IO protahovat operace na stovky ms.
Určitě bych si napřed ověřil, jestli db není bloatnutá - https://wiki.postgresql.org/wiki/Show_database_bloat případně bych nad db pustil VACUUM FULL. Pár mých zákazníků mělo nedobrý nápad vypnout autovacuum, a zvlášť na slabých strojích to má dost neblahý dopad.
-
.
-
Díky všem za rady, byl jsem schopný izolovat ten problém. Hodně pomohl ten pgbadger.
Tedy po analýze jsem zjistil, že databáze v AWS RDS se 4GB RAM a 2vCPU všeobecně má problém, když se nad běžným procesováním requestů, které mi tam chodí v průměru 1 za vteřinu, bokem spustí nějaký jiný proces.
Konkrétně se mi spouštěl každých 30 vteřin proces, který dělal SELECT COUNT nad tabulkou, kde je oněch 10 mil. záznamů. Tento proces, pokud se dostal do blbé fáze s procesováním requestů, tak vytvořil spike, který mohl z průměrného času 30ms na request udělat 100-400ms na request.
Tedy velice pozitivní zpráva je, že není postgres asi viník.
No a teď přichází na řadu další věc a to je, co udělat s tím performance databáze, protože není možné, aby spuštění nějakého bočního sql query mi způsobovalo, že se request dostane přes 300ms trvání.
Tedy já se nejspíše vydám cestou partitioningu tabulek dle "id modulo 10", protože jsem s tímto měl dobrou zkušenost na minulém projektu. Dle query planneru se jeví, že Postgres nad partitioned tables spouští query paralelně. Tedy očekávám, že toto by mi mohlo zlepšit výkonnost databáze. Případně potom se vydat cestou silnějšího stroje.
Silnější stroj jsem zkusil dnes, 16GB RAM a 4vCPU, a nakolik to má značný vliv na rychlost zpracování requestů, a běží to "krásněji", neochránilo to před vznikem spiků, načež spike nad 300ms už by znamenal velký problém.
Tedy nezdá se, že by samotné zvýšení RDS vyřešilo ten problém, a proto to chci zkusit zkombinovat s partitioningem té velké tabulky, kde jednou bude 20 mil. záznamů.
To, že výrazně silnější stroj nepomohl - tak by to mohlo znamenat, že buďto tam máte problémy se zámky, nebo utavíte IO. Obecně RDS nabízí IO s málo IOPS, a jsou to stroje spíš na aplikace, které jsou náročné na CPU než na IO. A zkontrolujte si jestli tu tabulku nemáte bloatnutou. Schválně jsem si udělal čerstvou tabulku o 3 sloupcích s 10M řádky. Má 422 mega - SELECT count(*) mi běží 300 ms. Pokud se vám ta tabulka včetně indexů vejde do shared buffers, tak by to ostatní operace nemělo zpomalit. Pokud ne, a máte pomalé IO, tak to vliv mít může. Jinak jestli count chcete dělat často, tak Postgres možná nebude ta správná db - zkuste se podívat na DuckDB. Pár INSERTů a masivní SELECT může byt dobrý use case pro tuhle db. Vzhledem ke komprimaci dat může být tabulka na disku výrazně menší - což znamená, že to vyžaduje i méně IO.
-
Já jsem ten předchozí příspěvek smazal, protože ačkoliv vypnutí oněch "bočních" jobů vyredukovalo spiky v DB, nevyredukovalo to všechny spiky.
Přesto myslím si stále to, že by to mělo mít lepší performance, když se dělají "boční" queries do databáze.
"Bloatnutá" tabulka předpokládám je řešené přes Vacuum analyse. Tento spouštím každou noc nad všemi tabulkami.
Tabulka má 10mil. řádků a 40 sloupců. Shared buffer je momentálně na 4GB při 16GB RAM. Velikost tabulky je 8.5GB.
Jak je tedy vidět, tabulka má 2x větší velikost než shared buffer. Nejsem si však jist jaký toto má vliv. Pokud to však není dobře, očekávám že partitioning tabulky do 10 podtabulek by tento problém snad mohl vyřešit, pokud se nemýlím.
-
Já jsem ten předchozí příspěvek smazal, protože ačkoliv vypnutí oněch "bočních" jobů vyredukovalo spiky v DB, nevyredukovalo to všechny spiky.
Přesto myslím si stále to, že by to mělo mít lepší performance, když se dělají "boční" queries do databáze.
"Bloatnutá" tabulka předpokládám je řešené přes Vacuum analyse. Tento spouštím každou noc nad všemi tabulkami.
Tabulka má 10mil. řádků a 40 sloupců. Shared buffer je momentálně na 4GB při 16GB RAM. Velikost tabulky je 8.5GB.
Jak je tedy vidět, tabulka má 2x větší velikost než shared buffer. Nejsem si však jist jaký toto má vliv. Pokud to však není dobře, očekávám že partitioning tabulky do 10 podtabulek by tento problém snad mohl vyřešit, pokud se nemýlím.
VACUUM ANALYZE bloating neresi - ten resi VACUUM FULL. Jestli mate tabulku 8.5GB, a ctete ji kazdych 30 sec, tak jenom tim si utavite IO. I docela slusne IO vam dava 1GB/s - a jste vyrazne nad shared buffers, takze si proplachujete shard buffers - sice asi jenom jednu tretinu, coz je 1GB, ale to se muze nacitat z disku, a v momentu, kdy intenzivne ctete, tak muzete mit problem. Tam je otazka, jake tam mate indexy a co se vsechno musi pouzit temi ostatnimi dotazy. Pokud tam mate lagy nad 100ms, tak uz by se mozna dalo videt neco i v pg_stat_activity, jsou tam videt zamky. Partitioning vam nijak nepomuze, pokud budete cist vsechny partisny. Pokud chcete, aby se vam to chovalo +/- jako realtime system, tak chca nechca musite mit dost velkou rezervu v hw. Partitioning by vam mohl pomoct s mazanim, nebo snizenim rezie autovacuua, pokud do starsich partitions nezapisujete.
-
Nj, jenže je otázka, co to je to čtení každých 30s.
Pakliže já mám dobře udělané indexy, tak queries mi vrací max řekněme 100 záznamů. Co na tom záleží, že je tam 8GB dat, když podle index scanu se jich načte z tabulky jen třeba 100.
Problém můžou být queries typu ORDER BY date OFFSET 250000 LIMIT 500, ty dokážou spolehlivě vytvořit spike, trvají přibližně 2 vteřiny.
Zajímavé je, že už třeba query ORDER BY date OFFSET 500000 LIMIT 1000 dokáže v 50% případů trvat přes 30s, tipuju že se to občas přepne na sequential scan.
Nicméně tyto queries tam samozřejmě nespouštím běžně, říkám jen, že dokáží způsobit spike a request co trvá 30ms rázem trvá přes 300ms.
Neodpustím si však poznámku, že nakolik ORDER BY date OFFSET 500000 LIMIT 1000 zní šíleně, pořád by to mělo být přes index, tzn. těch 1000 řádků si to nejprve najde přes index, a potom z tabulky si to natáhne oněch 1000 záznamů přes random access. Nelíbí se mi, že to způsobí spike, protože to přece není zase taková hrůza, jak se zdá. To Postgres tam něco "vyvádí".
Zároveň s tím, ORDER BY date OFFSET 250000 LIMIT 500 si nemyslím že by mělo trvat 2 sekundy, když to jde přes index. Přijde mi to jako moc.
Líbilo by se mi, kdyby to dokázalo udělat OFFSET 500000 LIMIT 1000 a zároveň s tím by mi délka hlavních requestu nepřekročila 300ms. Kdyby to zvládlo tohle, měl bych klid a jistotu, že DB je dostatečně dimenzovaná.
-
Nicméně tyto queries tam samozřejmě nespouštím běžně, říkám jen, že dokáží způsobit spike a r
Neodpustím si však poznámku, že nakolik ORDER BY date OFFSET 500000 LIMIT 1000 zní šíleně, pořád by to mělo být přes index, tzn. těch 1000 řádků si to nejprve najde přes index, a potom z tabulky si to natáhne oněch 1000 záznamů přes random access. Nelíbí se mi, že to způsobí spike, protože to přece není zase taková hrůza, jak se zdá. To Postgres tam něco "vyvádí".
OFFSET neznamená jump na pozici - znamená zahoď, co jsi spočítal - takže když tam máte OFFSET 500000 LIMIT 1000, tak se samozřejmě index nepoužije - jelikož čtete víc než čtvrtinu tabulky. Tohle nemůže fungovat - jestli stránkuje tímhle mechanismem, tak je hodně nešťastné - buďto to musíte převést na WHERE id > x LIMIT 1000 nebo používat kurzor a postupně si fetchovat.
-
Nicméně tyto queries tam samozřejmě nespouštím běžně, říkám jen, že dokáží způsobit spike a r
Neodpustím si však poznámku, že nakolik ORDER BY date OFFSET 500000 LIMIT 1000 zní šíleně, pořád by to mělo být přes index, tzn. těch 1000 řádků si to nejprve najde přes index, a potom z tabulky si to natáhne oněch 1000 záznamů přes random access. Nelíbí se mi, že to způsobí spike, protože to přece není zase taková hrůza, jak se zdá. To Postgres tam něco "vyvádí".
OFFSET neznamená jump na pozici - znamená zahoď, co jsi spočítal - takže když tam máte OFFSET 500000 LIMIT 1000, tak se samozřejmě index nepoužije - jelikož čtete víc než čtvrtinu tabulky. Tohle nemůže fungovat - jestli stránkuje tímhle mechanismem, tak je hodně nešťastné - buďto to musíte převést na WHERE id > x LIMIT 1000 nebo používat kurzor a postupně si fetchovat.
Dobře řekněme že mám tabulku:
Objednavky
A mám tam 10 mil záznamů. A udělám:
SELECT *
FROM Objednavky
ORDER BY date ASC
OFFSET 250000
LIMIT 500
A mám index:
CREATE INDEX idx0 ON Objednavky (date ASC)
Tak potom query plan by měl postupovat takto:
1. Vlezu do indexu idx0
2. Ten je sortován by default ASC.
3. Přeskočím prvních 250000 záznamů z indexu
4. Vezmu následujících 500 záznamů v indexu
5. S těmi záznamy z indexu vlezu do tabulky Objednavky a načtu z disku oněch 500 záznamů přes random access.
Takto by to přece mělo fungovat, ne? Tzn. tabulka Objednávky co má 40 sloupců může mít klidně 10GB, ale index idx0 má třeba jen 500MB, a ani ty se nemusí přečíst všechny, stačí nad tím indexem, tzn. souborem na disku, udělat iteraci 250000 záznamů a dalších 500 vrať.
Někdy bych byl rači kdybych si ty query plany mohl psát sám.
-
Takto by to přece mělo fungovat, ne? Tzn. tabulka Objednávky co má 40 sloupců může mít klidně 10GB, ale index idx0 má třeba jen 500MB, a ani ty se nemusí přečíst všechny, stačí nad tím indexem, tzn. souborem na disku, udělat iteraci 250000 záznamů a dalších 500 vrať.
Někdy bych byl rači kdybych si ty query plany mohl psát sám.
Ne tak to nefunguje - tabulka není pole - je to halda, kde data mohou být kdekoliv - řádky nejsou očíslované. Možná si index můžete vynutit použitím ORDER BY - nicméně pořád se bude skrz index číst 250000 záznamů, což je zoufale pomalé. I kdybyste si ty query plány psal sám, tak by vám to bylo k ničemu, jelikož v relační databázi není žádná rychlá možnost skoku na ntý záznam. V historických databázích s fixní délkou věty to šlo, pokud jste z tabulky nikdy nemazali, ale v moderních relačních databázích s proměnlivou délkou věty žádná taková magie není.
-
Takto by to přece mělo fungovat, ne? Tzn. tabulka Objednávky co má 40 sloupců může mít klidně 10GB, ale index idx0 má třeba jen 500MB, a ani ty se nemusí přečíst všechny, stačí nad tím indexem, tzn. souborem na disku, udělat iteraci 250000 záznamů a dalších 500 vrať.
Někdy bych byl rači kdybych si ty query plany mohl psát sám.
Ne tak to nefunguje - tabulka není pole - je to halda, kde data mohou být kdekoliv - řádky nejsou očíslované. Možná si index můžete vynutit použitím ORDER BY - nicméně pořád se bude skrz index číst 250000 záznamů, což je zoufale pomalé. I kdybyste si ty query plány psal sám, tak by vám to bylo k ničemu, jelikož v relační databázi není žádná rychlá možnost skoku na ntý záznam. V historických databázích s fixní délkou věty to šlo, pokud jste z tabulky nikdy nemazali, ale v moderních relačních databázích s proměnlivou délkou věty žádná taková magie není.
No počkat, tohle bych potřeboval líp pochopit. Databáze si přece i na disku dělá svoje speciální soubory, a měl jsem i v představě či v naději, že si to umí pracovat až s jednotlivými bloky na disku.
Pokud vy si v indexu najdete přes query 1 zázname přes equals, tak ten index musí vrátit nějaký identifikátor, ne? A ten identifikátor to musí být něco, jak vytáhne ten záznam z té originální velké tabulky.
Tzn. index "něco" vrátí, a databáze na to něco v tabulce dokáže "skočit", a když ne skočit tam přímo, tak alespoň přibližně skočit někam, proiterovat omezenou množinu dat, najít záznam a vrátit ho. Jako Hashmapa.
Technicky vzato, když se nad tím zamyslím jako programátor. Mám nějakou tabulku, tak ji budu reprezentovat soubory na disku. Těch souborů tam může být třeba 100000. A když mi někdo řekne "Dej mi záznam "93fj2fj209fjh2dgrd3109f92", tak já vím, že tento záznam bude v souboru s číslem 12344.
Takhle v principu tpo přece nějak musí umět fungovat, ne? Nehledě na to, že technicky vzato můžu na SSD disk se speciálním naformátováním vkládat záznamy do jednotlivých bloků, každému bloku dám identifkátor, a potom s tím SSD diskem rovnou skočím na daný blok. I tohle technicky vzato je proveditelné, že můžu na záznam skočit přímo. A když ne přímo, tak alespoň na nějaký agregační uzel, a z něj už potom přímo.
Tak by mě zajímalo jak to dělá postgres.
-
Takhle v principu tpo přece nějak musí umět fungovat, ne? Nehledě na to, že technicky vzato můžu na SSD disk se speciálním naformátováním vkládat záznamy do jednotlivých bloků, každému bloku dám identifkátor, a potom s tím SSD diskem rovnou skočím na daný blok. I tohle technicky vzato je proveditelné, že můžu na záznam skočit přímo. A když ne přímo, tak alespoň na nějaký agregační uzel, a z něj už potom přímo.
Tak by mě zajímalo jak to dělá postgres.
Databáze označuje řádky číslem datové stránky a pozicí na stránce - můžete se jednoduše a rychle přesunout na 100 stránku a 10 řádek na této stránce. Ale nikdy nevíte kolikátý je to řádek od začátku tabulky (nevíte kolik je řádku před a nevíte kolik je řádků za). Taková informace nikde není - a kdyby byla, nedal by se udělat jednoduše DELETE, nebo třeba v Postgresu UPDATE atd. Relační databáze jsou založené na tom, že relace (tabulka) je množina - v množině nemáte očíslované hodnoty. Má to svoje výhody, má to svoje nevýhody. Nevýhodou je třeba pomalý OFFSET - který redukuje jen obsah přenášený po síti. Skrz index můžete mít pekelně rychlou operaci WHERE id > než ale nikdy skoč na 30 řádek, jelikož taková informace tam prostě není.
Můžete si udělat nějaký vlastní hash partitioning nebo list partitioning. Nicméně s větší přesností vám poroste počet souborů počet partitions a pekelně se zpomalí optimalizace, a brutálně vám vzrostou nároky na paměť při optimalizaci dotazu. A uvnitř partitions zase nebudete vědět kolik je tam řádků a kde ten správný řádek je. Vy si můžete napsat jednoduchou aplikaci, která bude pracovat se souborem direktivně s pevnou délkou věty a bude vám to fungovat - a v podstatě budete simulovat databáze 1 generace, které uměly velice rychle seekovat. Ale třeba už se nedala udělat jednoduše defragmentace. Relační databáze jsou db 2 generace, které schválně datový model nezávisí na fyzické pozici - mne jako uživatele nezajímá, kde je konkrétně řádek (a v postgresu se při každém update jeho pozice mění), ale seek na ntý řádek v relační db jednoduše neuděláte. Ve speciálních případech, kdy id bude totožné s pozicí, tak vám index pomůže - ale opět nemůžete použít OFFSET, jelikož pro relační db, je něco podobného nepředstavitelné - a) nesměl byste mazat z db, a každý INSERT by se musel povést.
-
Takhle v principu tpo přece nějak musí umět fungovat, ne? Nehledě na to, že technicky vzato můžu na SSD disk se speciálním naformátováním vkládat záznamy do jednotlivých bloků, každému bloku dám identifkátor, a potom s tím SSD diskem rovnou skočím na daný blok. I tohle technicky vzato je proveditelné, že můžu na záznam skočit přímo. A když ne přímo, tak alespoň na nějaký agregační uzel, a z něj už potom přímo.
Tak by mě zajímalo jak to dělá postgres.
Databáze označuje řádky číslem datové stránky a pozicí na stránce - můžete se jednoduše a rychle přesunout na 100 stránku a 10 řádek na této stránce. Ale nikdy nevíte kolikátý je to řádek od začátku tabulky (nevíte kolik je řádku před a nevíte kolik je řádků za).
Ale to přece víte, protože jste na tu 100 stránku a 10 řádek skočil prostřednictvím toho indexu. A vy podle indexu víte, že to je offset 10000 třeba.
-
Takhle v principu tpo přece nějak musí umět fungovat, ne? Nehledě na to, že technicky vzato můžu na SSD disk se speciálním naformátováním vkládat záznamy do jednotlivých bloků, každému bloku dám identifkátor, a potom s tím SSD diskem rovnou skočím na daný blok. I tohle technicky vzato je proveditelné, že můžu na záznam skočit přímo. A když ne přímo, tak alespoň na nějaký agregační uzel, a z něj už potom přímo.
Tak by mě zajímalo jak to dělá postgres.
Databáze označuje řádky číslem datové stránky a pozicí na stránce - můžete se jednoduše a rychle přesunout na 100 stránku a 10 řádek na této stránce. Ale nikdy nevíte kolikátý je to řádek od začátku tabulky (nevíte kolik je řádku před a nevíte kolik je řádků za).
Ale to přece víte, protože jste na tu 100 stránku a 10 řádek skočil prostřednictvím toho indexu. A vy podle indexu víte, že to je offset 10000 třeba.
Ale ani v indexu není nikde informace, že nějaký řádek má offset 10000. Musíte se proiterovat indexem, což u velkých tabulek, které se vám nevejdou do RAM je výrazně horší než sekvenční IO, jelikož tam máte random IO operace. Pokud nemáte extra širokou tabulku, tak je většinou výrazně rychlejší přečíst tabulku sekvenčně než skrz index náhdoně. Dnes s SSD to už není řádový rozdíl, ale pořád tam nějaký rozdíl je.
-
Jasně tomu rozumím a dává mi smysl, že když řeknu postgres:
ORDER BY date OFFSET 250000 LIMIT 500
Tak použije index a query je hotový ve stovkách ms. A když řeknu Postgres
ORDE BY date OFFSET 500000 LIMIT 1000
Tak už query trvá přes 10 vteřin, protože se postgres přepne.
Ale: Kdyby se to nepřeplo, tak to určitě nebude trvat několik vteřin ale prostě násobek toho horního času. Tzn. já vím, že by theory někde je řečeno, že v určitých situacích je seq scan rychlejší než index scan s následným randomom access, ale v >> praxi <<, protože ladím postgres queries posledních 5 let často, tak jasně vidím, že seq scan zpravidla nikdy nechci, protože trvá >> násobně << déle. V praxi.
Takže někde "in RDB theory" je lepší seq. scan, ale v praxi já jasně vidím, že zpravidla vždycky je lepší index scan.
Taky kolikrát jsem zuřil když jsem hledal, proč mi pg přepne na seq. scan, a X-krát jsem četl, že je seq scan výhodnější v určitých situacích, a já přitom jasně vidím, že je násobně nevýhodnější, a vidím to jasně i teď u výše uvedených queries.
A potom co se týká toho ORDER BY, tak já asi pořád nesouhlasím s vám v tom, že to neumí zjistit OFFSET 10000 hned indexu. Protože ty indexy jsou sortovány. Pakliže já mám v indexu sortovány záznamy podle datumu Ascending, tak můžu skočit na záznam č. 10001 v INDEXU, a vím, že je na stránce 12345 na řádku 123. Z indexu si pak načtu pozice dalších 500 záznamů a vím, že jsou sorted a že jsou OFFSET 10000 v tabulce.
By theory jistojistě to udělat takto lze, technicky. O tom jsem přesvědčen. jestli to však je v silách Postgres to nevím. Já na vlasnní oči viděl, jaké dokáže Postgres query engine dělat do očí bijící nesmysly.
Jako např. že si je schopno počítat json_agg funkce a jine funkce v SELECTU u záznamů, které nejsou vráceny v konečném
výsledku dotazu, a zpomalí tím query násobně.
Nebo to, že Postgres nedokáže optimalizovat Views používané ve FROM klauzuli v jiném View, tzn. nedokáže jakoby "mergnout" a optimalizovat 2 selecty ve 2 Views. A tak namísto Views já musím uděla strašný copy-paste a použít jeden obří SQL query, aby Postgrs pochopilo, že některé věci může vykrátit. Katastrofa, a vede to k velkému nepořádku, protože Views mají velké SQL queries schovávat.
-
Ale ani v indexu není nikde informace,
Myslím, že tazatel naráží na "Index only access", které některé MVCC databáze co vím mají. Je to ale něco za něco, jestlitomu rozumím dobře, tak to znamená přepisovat do indexů informace o tom, zdali je záznam commited nebo ne, což znamená další IO režii navíc při zápisech (a kdovíjaké další problémy). Postgresql to do indexů co vím nezapisuje, takže nemůže tuto techniku použít.
-
Jasně tomu rozumím a dává mi smysl, že když řeknu postgres:
ORDER BY date OFFSET 250000 LIMIT 500
Tak použije index a query je hotový ve stovkách ms. A když řeknu Postgres
ORDE BY date OFFSET 500000 LIMIT 1000
Tak už query trvá přes 10 vteřin, protože se postgres přepne.
Ale: Kdyby se to nepřeplo, tak to určitě nebude trvat několik vteřin ale prostě násobek toho horního času. Tzn. já vím, že by theory někde je řečeno, že v určitých situacích je seq scan rychlejší než index scan s následným randomom access, ale v >> praxi <<, protože ladím postgres queries posledních 5 let často, tak jasně vidím, že seq scan zpravidla nikdy nechci, protože trvá >> násobně << déle. V praxi.
Takže někde "in RDB theory" je lepší seq. scan, ale v praxi já jasně vidím, že zpravidla vždycky je lepší index scan.
Taky kolikrát jsem zuřil když jsem hledal, proč mi pg přepne na seq. scan, a X-krát jsem četl, že je seq scan výhodnější v určitých situacích, a já přitom jasně vidím, že je násobně nevýhodnější, a vidím to jasně i teď u výše uvedených queries.
A potom co se týká toho ORDER BY, tak já asi pořád nesouhlasím s vám v tom, že to neumí zjistit OFFSET 10000 hned indexu. Protože ty indexy jsou sortovány. Pakliže já mám v indexu sortovány záznamy podle datumu Ascending, tak můžu skočit na záznam č. 10001 v INDEXU, a vím, že je na stránce 12345 na řádku 123. Z indexu si pak načtu pozice dalších 500 záznamů a vím, že jsou sorted a že jsou OFFSET 10000 v tabulce.
By theory jistojistě to udělat takto lze, technicky. O tom jsem přesvědčen. jestli to však je v silách Postgres to nevím. Já na vlasnní oči viděl, jaké dokáže Postgres query engine dělat do očí bijící nesmysly.
Jako např. že si je schopno počítat json_agg funkce a jine funkce v SELECTU u záznamů, které nejsou vráceny v konečném
výsledku dotazu, a zpomalí tím query násobně.
Nebo to, že Postgres nedokáže optimalizovat Views používané ve FROM klauzuli v jiném View, tzn. nedokáže jakoby "mergnout" a optimalizovat 2 selecty ve 2 Views. A tak namísto Views já musím uděla strašný copy-paste a použít jeden obří SQL query, aby Postgrs pochopilo, že některé věci může vykrátit. Katastrofa, a vede to k velkému nepořádku, protože Views mají velké SQL queries schovávat.
Index scan budete mít rychlejší, když se vám datové stránky udrží v cache. Jinak záleží na aktuální utilizaci disku. S optimalizací indexu - je možné, že se vám u složitějších dotazů aktivuje GEQO optimalizátor, který je výrazně rychlejší, ale nemusí vrátit skutečně nejlepší plán, případně jste možná hitnul from_collapse_limit. Vaše theory je nesmysl - pokud bych umazal první řádek, musel bych regenerovat celý index, aby se přepočítaly pořadová čísla řádků. Což se samozřejmě neděje. Ne u klasických relačních databází. Existují append only databáze, ale to bychom se bavili o hodně specifických databázích.
Pozn. Postgres má jednoduché pravidlo - pokud čtete více než 1/4 tabulky, použije se seq scan. To je dáno poměrem seq_page_cost/random_page_cost. Pokud děláte s ssd nebo pokud se vám db vejde do RAM, tak můžete stáhnout random_page_cost na hodnotu 2 - někdy se stahuje i na 1.1 nebo 1.5, ale to už může dělat brikule.
-
Ale ani v indexu není nikde informace,
Myslím, že tazatel naráží na "Index only access", které některé MVCC databáze co vím mají. Je to ale něco za něco, jestlitomu rozumím dobře, tak to znamená přepisovat do indexů informace o tom, zdali je záznam commited nebo ne, což znamená další IO režii navíc při zápisech (a kdovíjaké další problémy). Postgresql to do indexů co vím nezapisuje, takže nemůže tuto techniku použít.
Postgres má index only scan (s podporou visibility map). To vám zrychlí čtení dat, ale pořád při OFFSET 10000 musíte přečíst 10000 hodnot z indexu. Pokud chcete dělat efektivně s relačními db (nemluvím jen o Postgresu), tak OFFSET s nějakým vyšším číslem je antipattern
-
Ale ani v indexu není nikde informace,
Myslím, že tazatel naráží na "Index only access", které některé MVCC databáze co vím mají. Je to ale něco za něco, jestlitomu rozumím dobře, tak to znamená přepisovat do indexů informace o tom, zdali je záznam commited nebo ne, což znamená další IO režii navíc při zápisech (a kdovíjaké další problémy). Postgresql to do indexů co vím nezapisuje, takže nemůže tuto techniku použít.
Postgres má index only scan (s podporou visibility map). To vám zrychlí čtení dat, ale pořád při OFFSET 10000 musíte přečíst 10000 hodnot z indexu. Pokud chcete dělat efektivně s relačními db (nemluvím jen o Postgresu), tak OFFSET s nějakým vyšším číslem je antipattern
K tomu antipatternu. Řekněme, že mám tabulku:
Orders(id, date)
A budu tím chtít procházet. Můžu udělat:
SELECT *
FROM Orders
ORDER BY date ASC
LIMIT 500
OFFSET 100000
Nebo můžu ud2lat:
SELECT *
FROM Orders
WHERE date > '...' AND date <= '...'
A teď mi zkuste vysvětlit, v čem bude query dole rychlejší, když to nahoře je antipatern. Jak říkám, cíl je stránkovat přes ty záznamy a vracet je přes API - to je cíl - není cíl se kochat jak se DB nenadře - máte business requirement na vaši službu a potřebujete implementovat stránkování. Buďto přes OFFSET a když je to teda antipattern, dobrá, tak tedy přes date.
Technicky vzato, v čem se ve druhé případě postgres méně nadře?
Protože v prvním případě se musi engine podívat do date_index a proiterovat prvních 100000 entries, aby si to následně vybralo 500 následujíchc entries a vytáhnout si je z tabulky Orders, kde je 10 mil záznamů.
Ve druhém případě však musí udělat totéž: musí si proiterovat date_index, zjistit, které záznamy zplňují podmínku, a potom si je vytáhnout z tabulky Orders.
To vyžaduje trochu vysvětlení, proč je v tom případě OFFSET antipatern.
-
Rozdíl je v tom, že druhý dotaz najde v indexu záznamy v daném rozsahu, a pak do primárního souboru jde jen pro data z daného rozsahu.
První dotaz musí projít celý index, aby zjistil, který záznam je číslo 100000. Mám za to, že musí navíc i do primárního souboru, aby ověřil, které záznamy jsou pro danou transakci viditelné (a platné, tedy které má do těch 100000 počítat). A pak teprv začne číst ty záznamy, které má vrátit.
-
Rozumím, dobře, díky za vysvětlení.
Takže příště stránkování se budu snažit dělat by date nebo něco podobného, a nikoliv by offset. Tím sice nejsem schopen pohlídat velikost stránky, ale může se to přesto v určitých chvílích hodit.
-
Ve druhém případě však musí udělat totéž: musí si proiterovat date_index, zjistit, které záznamy zplňují podmínku, a potom si je vytáhnout z tabulky Orders.
To vyžaduje trochu vysvětlení, proč je v tom případě OFFSET antipatern.
Vyhledávání rozsahu je operace indexem podporovaná. Pokud tam máte index, tak na 3-4 seek to najde začátek v stovkách miliónů záznamů.
Můžete si vyzkoušet, kolik Postgres přečte diskových stránek - použijte EXPLAIN (ANALYZE, BUFFERS) SELECT ...
Postgres vám vypíše, kolik která operace načetla diskových bloků. Případně si můžete vynutit index scan (pro porovnání) nastavením SET enable_seqscan TO OFF;
Zlaté stránky - tel seznam můžete vzít jako jednoduchou databázi. Když vám řeknu, abyste mi našel které číslo je na 10000 řádku, tak mne pošlete do pr... nebo to budete počítat měsíc (pokud nemají stránky fixní počet tel čísel - což neměli někdy tam byla třeba reklama nebo půl stránky prázdné). Když vám řeknu abyste tam našel Stěhuleho z Benešova, tak to bude pro vás otázka minuty s rezervou. Databáze fungují plus mínus podobně.
-
Jak říkám, cíl je stránkovat přes ty záznamy a vracet je přes API - to je cíl - není cíl se kochat jak se DB nenadře - máte business requirement na vaši službu a potřebujete implementovat stránkování. Buďto přes OFFSET a když je to teda antipattern, dobrá, tak tedy přes date.
To by nebylo špatný říct hned na začátku. Že vlastně nepotřebuješ OFFSET 500000, ale stránkování. Bez offsetu se dá stránkovat pomocí tzv. cursor-based pagination - místo abych v příštím dotazu řekl "chci stránku 10 s 50 záznamy na stránku", tak řeknu "Chci dalších 10 záznamů po X" (kde X je hodnota poslední položky na mojí stránce, podle které to mám seřazený)
-
snugar:
To není tak jednoduchý, když např. potřebuješ, aby ta stránka měla persistentní URL.
-
snugar:
To není tak jednoduchý, když např. potřebuješ, aby ta stránka měla persistentní URL.
tak za předpokladu, že se nemaže z db (aby to persistentní URL mělo smysl), tak pak bych si pomohl jednoduchou pomocnou tabulkou, kde bych mapoval číslo stránky na rozsah id, a pak přes to už vyhledával. Perzistentní číslo stránky mi ale přijde dost nepraktický - to už bych raději do URL zakódoval rozsah id. Tam ještě záleží o jakých velikostech se bavíme - OFFSET do nižších desítek tisíc řádek bude mít akceptovatelnou režii - což je nějakých 100 stránek po sto záznamech. Pokud chcete použít stránkování na tabulku o 1Mřádek, tak tam je OFFSET nepoužitelný, plus z hlediska UI je to už 1000 stránek, což mi také nepřijde použitelné - a pokud by data žrala nějaká aplikace, tak ta ať si říká o rozsah - a maximálně zkontroluji, jestli na jeden get si neřekla o příliš řádek.
Před lety jsem viděl jednu monitorovací aplikaci, s šíleným UI, kde se zobrazovalo nějakých 720 stránek - a uživatelé věděli, že na každý den připadá 24 stránek a samy si překódovali, kterou chtějí stránku podle toho z které hodiny, kterého dne měsíce chtějí vidět data. Nicméně UI naprosto na palici. Jinak požadavek na stránkování s pevnou velikostí stránky je dost často důsledkem přenesení UI ze souborových databází do web appek. Ale u těch souborových databází to bylo relativně laciná operace (fixní délka věty) a v těch starých databázích bylo málokdy víc než pár set tisíc záznamů, takže tam nebyla žádná extra režie.
-
snugar:
To není tak jednoduchý, když např. potřebuješ, aby ta stránka měla persistentní URL.
Ak je možnosť, tak upraviť API tak, aby to bolo REST. Teda, napriklad po nejakom vyhladavani (napriklad ked otvorime google a napiseme tam daco a klikneme enter), tak nam pride odpoved. V odpovedi mame linky na dalsie stranky a dane linky si neupravujeme v url rucne podla nejakeho magickeho pravidla typu "&page=%number+1", alebo "&stránočka=%number+1", ale proste klikneme na linku, ktorá má názov "next", "previous", "first", alebo "last". A v tej linke moze byt co potrebujete. Kludne aj odkaz na ini server.
Tuto vlastnost rest api zacinaju preberat rozne api, ako je napriklad apicko gitlab-u. am napriklad ale neposielaju url linky v obsahu samotnej odpovede, ale v headroch http. Aj to je mozne riesenie.
Kusok ustabilizovanejsie api sa da dosiahnut pouzitim standardu ako je HAL napr. Vid ukazku (je to kusok ukecanejsie): https://stateless.group/hal_specification.html
-
Jak říkám, cíl je stránkovat přes ty záznamy a vracet je přes API - to je cíl - není cíl se kochat jak se DB nenadře - máte business requirement na vaši službu a potřebujete implementovat stránkování. Buďto přes OFFSET a když je to teda antipattern, dobrá, tak tedy přes date.
To by nebylo špatný říct hned na začátku. Že vlastně nepotřebuješ OFFSET 500000, ale stránkování. Bez offsetu se dá stránkovat pomocí tzv. cursor-based pagination - místo abych v příštím dotazu řekl "chci stránku 10 s 50 záznamy na stránku", tak řeknu "Chci dalších 10 záznamů po X" (kde X je hodnota poslední položky na mojí stránce, podle které to mám seřazený)
Jo rozumím, na to potřebuju mít číslo záznamu a to musí být rostoucí, to je doable. Akorát teda když user přijde k UI a chce udělat jump v tabulce záznamů na stránku 100, tak se musí stejně proiterovat všechny předchozí stránky, aby si ono číslo záznamu na stránce 100 získal. Ale na některé věci se ten tvůj pagination určitě hodí víc.
-
Nenasel jsem v diskuzi. Meni se ty zaznamy z hlediska radicich sloupcu (treba lastModified), pripadne vklada se doprostred seznamu, nebo je to insert-only (sloupecek created)?
Mozna by sel pouzit ohejbak na rovnak a proste si pridat numericke id jako sloupecky pro jednotlive typy razeni, kde se pri vkladani udela MAX(sorting_by_date) +1 a nad tim se pak uz da rucne skocit na X-ty zaznam pres where.
-
Akorát teda když user přijde k UI a chce udělat jump v tabulce záznamů na stránku 100, tak se musí stejně proiterovat všechny předchozí stránky, aby si ono číslo záznamu na stránce 100 získal.
Pre uzivatela moze byt skok na konkretnu stranku uzitocny iba vtedy ak tam najde hodnoty ktore potrebuje. To je mozne iba vtedy ak sa zaznamy v tabulke nemenia. Kazdy insert, update alebo delete zmeni poziciu udajov tak, ze offset udajov ktore ho zaujimaju sa zmeni. Ak mas uzivatela, ktory vie ake data potrebuje, tak daleko vhodnejsi je normalny filter (where). Ak nevie ake udaje potrebuje, len vie ze potrebuje udaje zo stranky 100, tak mu je zrejme jedno co mu naservirujes.
A preco DB nerobi seek na OFFSET podla INDEXU? Proste index je strom. Vo vacsine pripadov. Vetvy obsahuju rozsahy s odkazmi na dalsie vetvy a az na koniec na listy kde su konkretne hodnoty s odkazmi na zaznamy. Je to idealne ked potrebujes dostat odkaz na zaklade hodnoty. Seek na poziciu je vhodny ak mas data ulozene ako vektor (tak je ulozena tabulka).
Ta magia by sa dala realizovat cez pomocnu tabulku ktora by obsahovala pravidelne aktualizovany histogram hodnot, na zaklade ktorych vies povedat aky rozsah hodnot pripada na konkretnu "stranku".
Ale podla mojej skusenosti je daleko jednoduchsie vysvetlit uzivatelom, ze by mali vediet ake parametre maju zadat do filtra aby sa dostali k udajom ktore potrebuju. Zvysi to jednak ich efektivitu prace, tak aj tvoju ked nebudes musiet vymyslat algoritmy ktore by suplovaly nedostatky uzivatelov. Pretoze v pripade zivej DB kde sa hodnoty pravidelne menia je poziadavka typu "Chcem stranku 100, pretoze pred tyzdnom tam boli hodnoty ktore chcem vidiet!' je neskutocne dementna.
A este preco ten dotaz ktory si pouzil nie je efektivny.
Pozri si plan toho dotazu. Pouzi napr. pgadmin, ten ti tu textovu informaciu o plane prevedie aj na graf. Co sa tam teda deje:
- Planovac vytvori plan ktory je optimalny podla statistik. Na jeho vystupe je kompletna sada udajov, z dovodu v 2.
- Udaje z 1. spracovava iterator pre sort. Samozrejme ze z 1. potrebuje uplne vsetky hodnoty. Tak sort proste funguje. Je irelevantne ci ma k dispozicii index alebo nie.
- Teraz pride na radu iteratot pre offset, konzumuje data a zahadzuje ich kym nenapocita pozadovany ofset. Ked ma pozadovany ofset tak data posiela na vystup, kym pocitadlo nedosiahne hodnoty offset+limit. Potom konci, a oznami predhadzajucemu iteratoru ze uz dalsie data nepotrebuje cim ukonci jeho cinnost, rovnako ako aj pracu dalsich iteratorov v retazi pred nim.
Ak by mal iterator v 3. ovplyvnovat iteratory v 1. tak na to nedokazes napisat planovac, ktory by nemal natvrdo nakodovane varianty toho ako je mozne napisat dotaz a ku kazdej variante zvlast exekutor.