Managing large databases
Explanation
Magyarázat
Hajdu Csaba, 2026
Managing large databases
Hogyan fekszik az adat, mibe kerül a lekérdezés, és miért a kimondatlan séma a legdrágább hiba.
Hajdu Csaba, 2026
The semester
Sor vagy oszlop — és miért ebből következik minden további
Üdv. Az eddigi heteken az adat készen állt: megkaptad a táblázatot, és elemezted. Ezen a héten megnézzük, honnan jön a táblázat, és mibe kerül. A szó, hogy „nagy adat”, félrevezető — nem a sorok számáról fogunk beszélni, hanem arról, hogy az adat alakja miért dönti el előre, mi lesz gyors és mi lesz elviselhetetlen.
Az a nagy, ami nem fér a memóriába — vagy amitől a lekérdezés megszűnik interaktív lenni.
Mielőtt bármit mondanánk a technológiáról, tisztázzuk a szót. Nincs olyan sorszám, ami fölött az adat nagy lesz. Két küszöb van, és mindkettő a géptől és a feladattól függ. Az első: nem fér a memóriába. A második: a lekérdezés megszűnik interaktív lenni — tehát elindítod, és közben elmész kávézni. A második küszöb a fontosabb, mert ez öli meg az elemzést. Nem attól áll le a munka, hogy lassú, hanem attól, hogy megszakad a gondolatmenet.
A második küszöb személyes. Egy tíz másodperces lekérdezés még elviselhető; egy percesnél már mással foglalkozol, mire lefut, és a felderítő elemzés abbamarad. Az oldalsó rajz a `w07-storage` demóé. Ezen a méreten még minden interaktív; ötvenszeres adaton a felső oszlopból tizenkét másodperc lesz, az alsóból fél alatti. A küszöb nem sorszám, hanem ez a két viselkedés.
Az ábra betöltése…
Rögtön az elején egy mért kép, hogy a két küszöb ne maradjon elvont. Ez ugyanaz az összegzés négy úton, ugyanazon a másfél millió soron. A leglassabb kétszázötven ezredmásodperc, a leggyorsabb hét egész öt. Egyik sem tart percekig — ezen a méreten még minden interaktív. De szorozd fel az adatot ötvenszeresére: a felső oszlop tizenkét másodperc lesz, az alsó marad fél alatt. Ott már nem ugyanaz a munka: az egyiknél gondolkodsz, a másiknál vársz.
A felszorzás nem mérés, hanem lineáris extrapoláció, és ezt ki kell mondani: a valóságban a CSV-olvasás rosszabbul skálázódik, mert egy ponton kifogy a memória. A nagyságrend viszont stabil, és a döntéshez ennyi elég.
Ugyanaz a táblázat kétféleképpen fekhet a lemezen. Sororientáltan: egy rekord mezői egymás mellett vannak, tehát egyetlen mérés kiolvasása egy helyről megy. Oszloporientáltan: egy oszlop értékei vannak egymás mellett, tehát ha csak a nedvességre van szükséged, csak azt olvasod be. Ez a kettő nem verseng: más feladatra való. A kérdés soha nem az, melyik jobb, hanem az, hogy a te munkaterhelésed melyikre hasonlít.
Ugyanaz az adat élhet mindkét alakban egyszerre: a mérésrögzítő sororientált adatbázisba ír, és onnan készül éjszakánként az oszlopos elemzési másolat.
Egy oszlopban egyféle érték van. Az egyformaság az, amit a tömörítő ki tud használni.
Az oszlopos tárolás előnye nem csak az, hogy kevesebbet olvasol. Egy oszlopban egyféle dolog van: csupa hőmérséklet, csupa időbélyeg, csupa táblaazonosító. Az egyforma dolgok között sok az ismétlődés és kicsi a szórás, ezt pedig a tömörítő ki tudja használni. Egy soron belül viszont keverednek a típusok — szöveg, szám, dátum —, és ott nincs mit kihasználni. Ezért lesz a Parquet-fájl nemcsak gyorsabban olvasható, hanem kisebb is.
A mért példánkban a táblaazonosító negyven különböző értéket vesz fel másfél millió soron. Oszloposan ez szinte ingyen van; CSV-ben minden sorban újra ki van írva. Az oldalsó rajz a `w07-storage` demóé: 3,9-szeres különbség, ugyanabból az adatból. Egy oszlopban egyféle érték van, és az egyformaság az, amit a tömörítő ki tud használni.
1 500 000 sor, 5 oszlop. Mérve a `w07-storage` demóval.
Ez mért szám, nem becslés. Másfél millió sor, öt oszlop. CSV-ként hatvankét egész hat megabájt, Parquet-ként tizenöt egész kilenc. Majdnem négyszeres különbség — ugyanazon az adaton, ugyanazzal a tartalommal. Amit a CSV cserébe ad: bármelyik szövegszerkesztő megnyitja. Ez valódi előny, csak nem elemzési előny.
A tömörítés nem tartalmat vesz el. Ugyanaz a másfél millió sor jön vissza belőle, bitre pontosan ugyanazokkal az értékekkel.
„Táblánként mennyi az átlagos nedvesség?” — pandas, ugyanaz a másfél millió sor, öt futás legjobb ideje.
Most a lényeg. Egyetlen kérdést tettünk fel — táblánként mennyi az átlagos nedvesség —, és ugyanazzal a könyvtárral válaszoltuk meg, kétféle fájlból. Kétszázötven ezredmásodperc, illetve huszonegy. Tizenegy egész hétszeres különbség, és egyetlen dolog változott: a fájl alakja. Ez a formátum ára, tisztán.
Öt futás legjobbja, nem az átlag. A lassabb futásokat idegen terhelés is okozhatja, a gyorsabbakat semmi — az alsó burkoló a stabilabb becslés arra, mennyi munka van a műveletben.
Ugyanaz a Parquet-fájl, ugyanaz a kérdés, három olvasó.
Most ugyanaz a fájl, három olvasóval. Figyeld meg, hogy ez külön ábra, és nem az előző mellé raktam a négy oszlopot. Ha egy rajzon áll a kétszázötven és a hét egész öt, akkor a három gyors oszlop egy képpontnyi csíkká lapul, és pont az nem látszik, amiről ez a dia szól. A tengely nulláról indul, tehát a hosszt nem torzíthatom — az egyetlen becsületes megoldás a szétválasztás. És még valamit nézz meg: a polars és a duckdb között nincs érdemi különbség. Ne is keress.
Ez a dia maga is az 5. hét anyagát alkalmazza: oszlopdiagramon a hossz az érték, tehát a tengely nullától indul — és ha ettől a kicsik eltűnnek, nem a tengelyt csonkoljuk, hanem szétválasztjuk az ábrát.
| hatás | mérőszám | mi változott |
|---|---|---|
| A formátum nyeresége | 11,7× | pandas |
| CSV ÷ pandas | ||
| Parquet | ||
| A motor nyeresége ezen felül | 2,9× | pandas |
| Parquet ÷ a leggyorsabb | ||
| Együtt | 33,4× | két változtatás egyszerre |
A leglassabb és a leggyorsabb közötti harminchármas különbséget könnyű a formátum javára írni. Nem az. Két dolog változott egyszerre: a fájl alakja és a motor. Szétszedve: ugyanaz a könyvtár, ugyanaz a kérdés, csak a formátum más — tizenegy egész hétszeres. A leggyorsabb Parquet-olvasó ezen felül hoz még két egész kilencszerest, de az már nem a formátum érdeme, hanem azé, hogy a duckdb és a polars natív és többszálú. Ha valaki két változtatást végez, és egyetlen arányt hirdet, akkor nem tudja, melyik mennyit ért.
Számold utána az előző két dia számaiból: 250,3 ÷ 21,4 és 21,4 ÷ 7,5. Az arány, amit a hallgató nem tud ellenőrizni, nem tanítható állítás — ezért van a mérésben elég tizedesjegy. A két arány nem egyformán megbízható: háromféle lemezen lemérve a formátum nyeresége 10,3 és 11,7 között maradt, a motoré viszont 1,6 és 3,4 között ingadozott. A formátum haszna robusztus, a motoré a géptől függ — és ez a különbség fontosabb, mint bármelyik szám külön.
A cella betöltése…
Ez a demó teljes kimenete. Futtasd le a saját gépeden: a számok nem lesznek pontosan ugyanezek, mert más a lemezed és a processzorod, de a viszonyok ugyanezek maradnak. Ha nálad nem így jön ki, az érdekes — akkor azt kell megnézni, miben más a géped. Ez a mondat komolyan van értve: a nem egyező mérés kérdés, nem hiba.
A demó a fájlokat a kimeneti könyvtárba írja, és ott is hagyja. Megnézheted őket — és megnézheted, mit lát belőlük egy szövegszerkesztő.
Három név, három szerep. A CSV átadási formátum: mindenhol megnyílik, és ez az egyetlen, de valódi erénye. A Parquet tárolási formátum: oszlopos, típusos, tömörített, sémával. Az Arrow nem fájl, hanem memóriabeli elrendezés — ettől lehet egy táblát átadni két könyvtár között anélkül, hogy lemásolnád. A hallgatók fele az Arrow-t fájlformátumnak hiszi; nem az.
Az oldalsó rajz a `w07-storage` demóé, és két lépcsőt mutat egyszerre. Az első kettő között a FORMÁTUM különbsége van, ugyanazzal a könyvtárral: 10,9-szeres. A második és a negyedik között a MOTORÉ, ugyanazon a fájlon: további 3,6-szeres. A kettő összeszorzódik — és mindkettő egyetlen gépen, telepítés nélkül elérhető.
| eszközfajta | mire való | mire NEM |
|---|---|---|
| relációs motor (PostgreSQL, MySQL) | sok párhuzamos írás, pontos sorlekérés, tranzakció | milliárd soros összegzés: soronként dolgozik |
| oszlopos elemző motor (DuckDB, ClickHouse) | nagy összegzés, szűrés, illesztés — olvasásra hangolva | sok apró írás: nem tranzakciós tároló |
| adatkeret-könyvtár (pandas, polars) | ami elfér a memóriában: felfedezés, átalakítás | a memórián túl: ott motor kell, nem nagyobb gép |
| elosztott motor (Spark és rokonai) | ami egy gépre nem fér el, több gépen | ami elfér: a hálózati költség többet visz, mint amit hoz |
| térbeli kiterjesztés (PostGIS) | vetület, térbeli index, poligon-metszés | általános elemzés: attól még relációs motor marad |
Most nevesítsük is az eszközöket, mert a félév során találkozni fogtok velük. Öt fajta van, és a különbség nem a sebesség, hanem a FELADAT alakja. A relációs motor sok párhuzamos írásra való: ez az üzemviteli rendszer háttere. Az oszlopos motor nagy összegzésekre. Az adatkeret-könyvtár arra, ami elfér a memóriában. Az elosztott motor arra, ami egy gépre már nem fér el — és figyeld meg a jobb oldali oszlopot: ez nem a lassúság gyógyszere. A térbeli kiterjesztés pedig attól lesz külön sor, hogy a vetületkezelés és a térbeli index nem utólag pótolható.
A sorrend a választás sorrendje is. Először nézd meg, elfér-e memóriában — ha igen, ott a legolcsóbb dolgozni. Ha nem, oszlopos motor egy gépen. És csak akkor elosztott, ha egy gép tárolókapacitása is kevés. A `w07-storage` demó ezt meg is méri: a formátum 10,9-szeres, a motor ezen felül 3,6-szeres nyereséget hozott — egyetlen gépen.
Típus
A CSV-nek nincs sémája. Ez nem apróság, hanem a hét legdrágább mondata. Ha nincs séma, akkor minden olvasó találgat: ez a szám egész vagy tört, ez a szöveg dátum vagy azonosító, ez az üres cella hiány vagy nulla. Két különböző program két különböző választ ad, és egyik sem szól. Ezért mondjuk, hogy az adatminőségi hibák fele nem az adatban keletkezik, hanem a kimondatlan sémában.
A klasszikus eset: a táblaazonosító `007`, amit a beolvasó egésszé alakít, és lesz belőle `7`. Az illesztés utána csendben nem talál semmit.
Érdemes egyszer végignézni, mi mire épül, mert a nevek keverednek. Legalul az objektumtároló: fájlok kulcs szerint, olcsón és korlátlanul. Fölötte a fájlformátum — nálunk a Parquet, ami oszlopos és tömörít. E kettő már elég az olvasáshoz. A harmadik réteg a táblaformátum: az adja hozzá a tranzakciót, a sémafejlődést és az időutazást. És a legfelső a motor, ami mindezt lekérdezi. A gyakori félreértés az, hogy a táblaformátum új fájlformátum lenne — nem az: a fájlok Parquet maradnak, a formátum a naplót és a pillanatképeket kezeli.
A rétegek cserélhetők, és ez a lényegük: ugyanazt a Parquet-fájlt olvassa a pandas, a DuckDB és a Spark is. Ez a „nyílt” a nyílt táblaformátumban — nem az, hogy ingyenes, hanem hogy nem köt egyetlen motorhoz.
Egy kollégád exportálja a méréseket CSV-be, mert „azt mindenki meg tudja nyitni”. Az elemzés hetente egyszer fut le, és minden alkalommal a teljes fájlt beolvassa, hogy két oszlopot összegezzen. Mi a legerősebb érv a formátum megváltoztatása mellett?
Egy kollégád exportálja a méréseket CSV-be, mert „azt mindenki meg tudja nyitni”. Az elemzés hetente egyszer fut le, és minden alkalommal a teljes fájlt beolvassa, hogy két oszlopot összegezzen. Mi a legerősebb érv a formátum megváltoztatása mellett?
2023
Az oszlopos formátum azt spórolja meg, hogy fölösleges oszlopot olvass. A particionálás azt, hogy fölösleges sort. Az adatot fizikailag szétbontod egy kulcs mentén — év, hónap, tábla —, és a könyvtárszerkezet maga hordozza a kulcsot. Ha a lekérdezésed a 2025-ös évre szól, a motor a többi év könyvtárát ki sem nyitja. Ez nem gyorsítás, hanem elkerülés: a legolcsóbb olvasás az, amelyik meg sem történik.
A rossz particionálási kulcs többet árt, mint használ: tízezer apró fájl olvasása lassabb, mint egy nagyé. A hüvelykujjszabály néhány száz megabájtos partíció, és olyan kulcs, amire a lekérdezések ténylegesen szűrnek.
A `w07-partition` demó mérése: 1 200 000 sor, 6 év, 40 tábla. A világos oszlop a fájlonkénti költséggel együtt.
Itt a particionálás ára és haszna egy ábrán. A kérdés végig ugyanaz: mennyi a kétezerhuszonötös év átlagos nedvessége. Particionálás nélkül hat megabájtot kell végigolvasni. Év szerint particionálva egy egész négyet — négy egész háromszor kevesebbet, és ez nem gyorsítás, hanem elkerülés: a többi öt év fájlját meg sem nyitjuk. Most nézd a harmadikat. Tovább bontva tábla szerint UGYANANNYI adat olvasódik, mert a kérdés nem szűr táblára — csak most negyven fájlból.
A fájlonkénti költség nem a lemezről jön, hanem a megnyitásból és a formátum fejlécéből — tehát akkor is fizeted, ha a fájl kicsi. Negyven fájlnál ez már 1,2-szeres összköltség az év szerinti elrendezéshez képest. A particionáló kulcs nem attól jó, hogy finom, hanem attól, hogy a lekérdezéseid arra szűrnek.
Két tag: a beolvasott adat és a fájlköltség. A finomabb partíció az elsőt csökkenti, a másodikat növeli.
A mérésben: particionálás nélkül 6,0 MB egy fájlban. Év szerint 1,4 MB egy fájlban — 4,3-szeres nyereség. Év ÉS tábla szerint ugyanaz az 1,4 MB, de 40 fájlban: fájlonként 8 kB-tal ez 1,7 MB, vagyis 1,2-szer DRÁGÁBB.
Az oldalsó rajz a `w07-partition` demóé. A kulcs a középső és a jobb oszlop összevetése: ugyanannyi ADATOT olvasnak be, a finomabb mégis drágább. Az ok az, hogy a kérdés nem szűr táblára, tehát mind a 40 partíciót meg kell nyitni. A partíciós kulcsot a KÉRDÉSHEZ kell választani, nem az adat szerkezetéhez.
A `w07-partition` demó mérése: 1,2 millió sor, hat év, negyven tábla.
C=0,23 * 6 + 1 * 0,01=1,39MB
Húzd a csúszkát, és nézd, mi mozdul.
Három csúszka, és ez a dia egy valódi tervezési döntést ad a kezedbe. Az alapbeállítás a demóé: hat megabájt adat, a kérdés a huszonhárom százalékát kéri, egyetlen fájlból. Most told a fájlszámot negyvenre — ennyi lesz, ha tábla szerint is particionálsz. Az adat mennyisége NEM változik, a fájlköltség viszont hozzáadódik, és a teljes ár egy egész hétre nő. Ez a dia magyarázza meg, miért nem mindig jó a finomabb partíció: a második tag nő, miközben az első nem csökken tovább.
A demó három elrendezést mér: particionálás nélkül 6,0 MB, év szerint 1,4 MB egy fájlban, év és tábla szerint ugyanaz az 1,4 MB negyven fájlban — fájlköltséggel együtt 1,7 MB. A partíciós kulcsot tehát a KÉRDÉSHEZ kell választani: ez a kérdés nem szűr táblára, ezért mind a negyven partíció kell.
Ha a séma nincs kimondva, attól még van — csak mindenki mást feltételez róla.
Zárjuk a blokkot azzal, amit érdemes megjegyezni belőle. A séma nem adminisztráció, hanem szerződés: kimondja, mit jelent egy oszlop, milyen típusú, és mit szabad benne. Ha nincs kimondva, attól nem szűnik meg létezni — csak mindenki mást feltételez róla. A kimondatlan séma pontosan addig működik, amíg egyetlen ember dolgozik az adaton.
Egy sémában a mértékegység is ott a helye. A `hozam` oszlop t/ha vagy kg/parcella? A kettő között kétezerszeres a szorzó, és egyik sem néz ki hibásan. Az oldalsó rajz a `w07-quality` demóé. A hangos hiba kibukik a legolcsóbb ellenőrzésből is; a csendes nem, és éppen ezért az jut át a jelentésbe. A séma az egyetlen hely, ahol meg lehet fogni.
A `w07-quality` demó mérése: ugyanaz az oszlop, ha a mértékegység nincs kimondva.
Íme, mit jelent az, hogy a séma akkor is létezik, ha nincs kimondva. Egyetlen oszlop, ötvenezer sor, és a valódi átlag hat egész négy tonna hektáronként. Ha a sorok három százaléka kilogrammban érkezett, és senki nem szólt, az átlag száznyilencvenkilenc egész öt lesz — háromezer százalékos eltérés. Ez kibukik, mert fizikailag lehetetlen. De ha ugyanez a három százalék tonna per HOLDBAN érkezett, akkor hat egész öt-négy-kettő jön ki: két egész két százalék eltérés. Ez nem bukik ki sehol. Az eredmény hihető marad, és ezért jut át a jelentésbe. A séma tehát nem adminisztráció: ez az egyetlen hely, ahol ezt a hibát meg lehet fogni.
Az ábrán a harmadik oszlop agyonnyomja a másik kettőt — és pontosan ez a tanulság képi alakja. Ami látszik, az ártalmatlan, mert azonnal kiderül. Ami nem látszik, az a veszélyes. A t/hold eset 2,2 százalékkal mozdít, és egyetlen tartományellenőrzés sem szól érte.
Index, terv, illesztés — és miért nem a másodperc a döntés alapja
Az első blokk arról szólt, hogyan fekszik az adat. Ez arról, mi történik, amikor kérdezel tőle. Egyetlen eszközt szeretnék a kezedbe adni: a lekérdezéstervet. Aki ezt el tudja olvasni, annak a teljesítményhangolás megszűnik babonának lenni.
A lekérdezésterv az a lépéssorozat, ahogy a motor valóban végrehajtja a kérdést. Ki lehet íratni.
A leggyakoribb hibás reflex: lassú a lekérdezés, tehát átírjuk. Kipróbálunk egy másik illesztést, kiveszünk egy alkérdést, cserélünk valamit — és néha gyorsul, de senki nem tudja, miért. A lekérdezésterv az a lépéssorozat, ahogy a motor ténylegesen végrehajtja a kérdést. Ez nem tippelés: kiíratható, és megmondja, hol megy el az idő.
A parancs motoronként más — `explain query plan` SQLite-ban, `explain analyze` PostgreSQL-ben és duckdb-ben —, de a szokás ugyanaz: előbb megnézni, aztán változtatni. Az oldalsó rajz a `w07-queryplan` demóé. A két oszlop a következmény; az ok a terv első szava — SCAN vagy SEARCH. Az ok akkor is igaz marad, ha a te gépeden mindkettő gyors.
Az ábra betöltése…
Hogy ne maradjon absztrakt: ez az az ábra, amit a demó kiír. Ugyanaz a lekérdezés kétszer, nyolcszázezer soros mérési táblán. Index nélkül a terv első szava az, hogy SCAN — végigolvassa a táblát. Indexszel SEARCH, sőt COVERING INDEX, vagyis még a táblához sem kell hozzányúlnia, mert amit kérdezünk, az benne van magában az indexben. A terv tehát nem sejtés: kiíratható, két szóból megmondja az okot, és ugyanazt mondja akkor is, ha a te gépeden épp minden gyors.
A tervet SQLite-ban az `EXPLAIN QUERY PLAN`, PostgreSQL-ben az `EXPLAIN ANALYZE`, DuckDB-ben az `EXPLAIN` adja. A szavaik különböznek, a kérdés nem: végigolvas vagy odaugrik, és ha összekapcsol, akkor melyik táblát tartja a memóriában.
A terv két szava, amit érdemes megjegyezni. A SCAN azt jelenti: a motor végigolvassa a táblát, az elejétől a végéig, és minden soron eldönti, kell-e. A SEARCH azt jelenti: van egy szerkezet, amiből tudja, hol keresse, és odaugrik. Ez a különbség nem néhány százalék: nyolcszázezer sornál a SCAN nyolcszázezer döntés, a SEARCH néhány ugrás egy fában.
A `COVERING INDEX` külön öröm: ez azt jelenti, hogy minden kért oszlop benne van magában az indexben, tehát a táblához hozzá sem kell nyúlni.
A cella betöltése…
Ez a demó a saját mérése. Nyolcszázezer mérési sor, negyven tábla, egy szűrés és egy csoportosítás. Index nélkül a terv SCAN, indexszel SEARCH `USING COVERING INDEX` — és az idő ötvenhét ezredmásodpercről huszonhétre megy le. Nézd meg a terv sorát is, ne csak az időt: ott a zárójelben az is benne van, melyik oszlopon és milyen feltétellel.
A demó `analyze`-t is futtat az index után. Enélkül a motor a saját statisztikája nélkül dönt, és néha rossz tervet választ egy jó index mellett is.
Egy kétoszlopos fedő index ára ugyanazon a 800 000 soros táblán.
Itt a másik fele annak, amit az előbb mértünk. A gyorsulás két egész egyszeres — szép, de nem drámai, mert a tábla befér a gyorsítótárba. Az ár viszont mérhető: az adatbázis huszonkét egész négy megabájtról harminchét egész hatra nőtt. Tizenöt egész két megabájt, a tábla mérete hatvannyolc százaléka — egyetlen indexért, két oszlopon. És ez csak a hely; a másik ár az, hogy minden beszúrásnál karban kell tartani.
Ezért nincs olyan tanács, hogy „tegyél mindenre indexet”. Az index a lekérdezéshez tartozik, nem az oszlophoz: azt kell indexelni, amire ténylegesen szűrsz vagy rendezel.
A terv az okot mondja meg, a másodperc a következményt. Az ok általánosít, a következmény nem.
Ez a blokk legfontosabb mondata, és szándékosan kellemetlen. A mért gyorsulás két egész egyszeres volt — ha ennyiből kellene dönteni, sokan azt mondanák, nem éri meg. De a tábla itt befér a memóriába. Tízszer nagyobb adaton ugyanez a SCAN már lemezről olvas, és a különbség nem kétszeres lesz, hanem nagyságrendi. A terv ezt előre megmondja; a másodperc csak azt mondja meg, mi volt ma ezen a gépen.
Fordítva is igaz: ha a terv SEARCH, és mégis lassú, akkor nem az index hiányzik. Akkor máshol van a baj — illesztésben, rendezésben, vagy abban, hogy tényleg sok találat van. Az oldalsó rajz a `w07-queryplan` demóé: az index a tábla méretének 68 százalékát teszi hozzá, és minden beszúrásnál karban kell tartani. A másodperc ezt nem mutatja meg — a terv és a méret igen.
A `w07-queryplan` demó mérése: ugyanaz a lekérdezés, 800 000 soron.
Nézd meg a két számot, és aztán azt, mit lehet velük kezdeni. Index nélkül ötvenhat ezredmásodperc, indexszel huszonnyolc: kétszeres gyorsulás. Ez a mérés ezen a gépen, ezzel a nyolcszázezer sorral, meleg gyorsítótárral igaz. Tízszer ekkora táblán a SCAN tízszer annyi ideig tart, a SEARCH viszont alig lassul — a kétszeresből húszszoros lesz. A másodperc tehát következmény, és a nagyságrenddel változik. A terv az ok, és az nem változik: a SCAN akkor is végigolvas, ha épp gyorsan teszi.
Ezért nem szabad teljesítménydöntést egyetlen mért másodpercre alapozni, főleg nem a fejlesztői gépen mértre. Az ok általánosít, a következmény nem — és üzemben más a gép, más a tábla mérete, és hideg a gyorsítótár.
Felveszel egy indexet, és a lekérdezés nem lesz gyorsabb. Kiíratod a tervet, és továbbra is SCAN szerepel benne. Melyik magyarázat nem lehet ok?
Felveszel egy indexet, és a lekérdezés nem lesz gyorsabb. Kiíratod a tervet, és továbbra is SCAN szerepel benne. Melyik magyarázat nem lehet ok?
A motor a becsült sorszámból választ algoritmust. Rossz becslés → rossz terv, változatlan SQL mellett.
Két tábla összekapcsolása közös kulcs mentén — ez a legtöbb elemzés első lépése, és a legtöbb rossz terv is itt születik. A motor megbecsüli, hány sor jön ki, és ebből választ algoritmust. Ha a becslés jó, a terv jó. Ha rosszul becsüli — mert a kulcs eloszlása ferde, mert az egyik tábláról nincs statisztikája —, akkor rossz algoritmust választ, és a lekérdezés egy nagyságrenddel lassabb lesz. Ilyenkor nem a SQL a hibás, hanem a statisztika elavult.
Ezért van az `analyze` parancs. Nagy betöltés után a statisztika elavul, és a motor a régi világ szerint dönt. Az oldalsó rajz a `w07-join` demóé. Egyetlen motoron a ferde kulcs nem büntet — ott éppen gyorsabb. A baj a szétosztott végrehajtásnál jelentkezik: a nyolc magból hét várakozik a leghosszabb partícióra.
A `w07-join` demó mérése: 600 000 sor, azonos SQL, két kulcseloszlás. Egyetlen sqlite-motoron.
Ezt a mérést azért szeretem, mert az ellenkezőjét adta annak, amit vártam. Hatszázezer sor, negyven tábla, ugyanaz az illesztés kétféle kulcseloszlással. A ferde esetben egyetlen tábla viszi a sorok nyolcvan százalékát — és a lekérdezés nem lassabb lett, hanem gyorsabb: nulla egész hatvannyolcszoros idő. Egy motoron a teljes munka ugyanannyi, és a ferde esetben a találatok nagy része ugyanoda esik, ami a gyorsítótárnak kedvez.
A „ferde kulcs lassít” szabályt tehát pontosítani kell. Egyetlen motoron nem igaz. A következő dia megmutatja, hol igaz.
Ugyanaz a két kulcseloszlás, 8 partícióra osztva. A szaggatott vonal az ideális 8-szoros gyorsulás.
És itt van, hol számít a ferdeség. Ha a munkát szétosztjuk — particionált táblán, több szálon, fürtön —, akkor nem az összes munka dönti el a futásidőt, hanem a leghosszabb rész. Egyenletes kulcsnál nyolc partícióra bontva a legnagyobb rész tizenkét egész öt százalék, tehát a nyolcszoros gyorsulás megvan. Ferde kulcsnál a legnagyobb rész nyolcvankét százalék: az elérhető gyorsulás egy egész kettő. Nyolc magból hét várakozik, és a SQL közben betűre ugyanaz.
Ezért nem lehet a lekérdezés költségét a szövegéből megjósolni: az adat eloszlása és a végrehajtás alakja is bemenet. És ezért kellett ezt a diát a mérés után átírni — az eredeti állításom, hogy a ferde kulcs egyetlen motoron is lassít, nem állta ki a mérést.
A legnagyobb rész szabja meg a futásidőt. A gyorsulás felső korlátja 1 / legnagyobb rész.
A korlát FÜGGETLEN a magok számától: 82 százalékos legnagyobb résznél nyolc maggal 1,2-szeres, nyolcszáz maggal is 1,22-szeres a legjobb eset. Ferde kulcsnál tehát nem több gép kell, hanem más kulcs.
Az oldalsó rajz a `w07-join` demóé, és van benne egy fontos csavar: EGYETLEN motoron a ferde kulcs nem lassabb, hanem gyorsabb — 0,72-szeres futásidő, mert a hasítótábla kisebb. A ferdeség tehát nem önmagában baj: akkor lesz azzá, amikor párhuzamosítani akarsz.
A `w07-join` demó két kulcsa nyolc partícióra osztva: 12,5 % és 82,0 %.
G=1 / 0,13=8×
Húzd a csúszkát, és nézd, mi mozdul.
Egy csúszka: a legnagyobb rész aránya. Told nulla egész százhuszonötre — ez az egyenletes eset nyolc magon —, és a gyorsulás nyolcszoros. Most told nulla egész nyolcvankettőre, ami a ferde kulcs mérése: egy egész kettő. És itt a lényeg, amit a képletből azonnal látsz: a magok száma NINCS BENNE. Akárhány magod van, a legnagyobb rész szabja meg a futásidőt. Ferde kulcsnál tehát nem több gép kell, hanem más kulcs.
A demóban van egy csavar, ami ezt a diát élesíti: EGYETLEN motoron a ferde kulcs nem lassabb, hanem gyorsabb — 0,72-szeres futásidő, mert a hasítótábla kisebb. A ferdeség tehát nem önmagában baj, hanem akkor válik azzá, amikor párhuzamosítani akarsz.
A cella betöltése…
A cella itt fut. A csavar a ferdeség mértéke: enyhítsd nulla egész négyre a nyolcvan százalék helyett, és nézd meg, mennyi marad a párhuzamosításból. Az elérhető gyorsulás egy per a legnagyobb rész — ebből előre ki tudod számolni, mit vársz, és a mérés vagy megerősíti, vagy nem.
A demó legmeglepőbb száma az, hogy EGYETLEN motoron a ferde kulcs GYORSABB — 0,72-szeres futásidő —, mert a hasítótábla kisebb. A ferdeség nem önmagában baj: akkor lesz azzá, amikor párhuzamosítani akarsz.
Két munkaterhelés, két világ. A tranzakciós — OLTP — sok apró írásból és pontkeresésből áll: a mérésrögzítő beszúr egy sort, a kezelőfelület kikeres egy rendelést. Az elemző — OLAP — ennek a fordítottja: kevés lekérdezés, de mindegyik sok sort érint. Ugyanaz az adat mindkettőben élhet, de nem ugyanabban az alakban, és ez a hét első blokkjának a következménye.
A keveredés jellegzetes agrárhiba: a mérésrögzítő adatbázisán futtatott havi riport egyszerre lassítja a rögzítést és lassú is. A megoldás nem hangolás, hanem szétválasztás.
Szerver nélkül, a folyamatba ágyazva, közvetlenül a Parquet-fájlon. Ami a tranzakciósnak a SQLite, az az elemzőnek ez.
Van egy eszközosztály, ami az elmúlt években átrendezte ezt a képet. A beágyazott elemző motor — a legismertebb a duckdb — szerver nélkül, a folyamatodba ágyazva fut, és Parquet-fájlon közvetlenül tud SQL-t futtatni. Ami a tranzakciós világban a SQLite, az az elemzőben ez. Nem kell adatbázist telepíteni ahhoz, hogy SQL-lel dolgozz néhány tízmillió soron — és a mérésünkben ez volt a leggyorsabb út.
A határa ott van, ahol a párhuzamos írók és a jogosultságkezelés kezdődik. Egy embernek vagy egy futtatásnak való, nem osztott üzemre. Az oldalsó rajz a `w07-storage` demóé. Ez már nem a formátum érdeme — az a CSV-ről Parquet-re váltásban volt —, hanem a motoré: vektoros feldolgozás, szerver nélkül, közvetlenül a fájlon.
A `w07-storage` demó mérése: ugyanaz az összegzés, 1 500 000 soron, öt futás legjobbja.
Mit nyersz azzal, hogy a motort a folyamatba ágyazod. Ugyanaz a kérdés, ugyanaz a másfél millió sor, négy úton. A pandas a CSV-ről száz-ötvennyolc ezredmásodperc alatt olvas. Ugyanaz a pandas Parquet-ről tizennégy egész hat. A polars négy egész kilenc, a DuckDB négy egész öt. A két végpont között harmincötszörös különbség van — és közben egyetlen szervert sem telepítettünk, nincs adatbázis-felhasználó, nincs port. A DuckDB közvetlenül a fájlra kérdez. Ezért mondjuk, hogy ami a tranzakciósnak a SQLite, az az elemzőnek ez.
A két hatást nem szabad összemosni: a formátumváltás 10,8-szeres, a motorváltás ezen felül 3,2-szeres. Aki a 35-szörös számot írja le anélkül, hogy szétválasztaná, azt sugallja, hogy a könyvtár cseréje hozta — pedig a nagyobbik rész a fájlformátumé.
Akkor indokolt, ha folyamatos a beérkezés ÉS megőrzési szabály is kell. Pusztán attól, hogy az adat idősor, még nem.
Az utolsó tárolási fajta, amivel agrárban találkozni fogsz. Az idősor-adatbázis időbélyeg szerint particionál, erősen tömörít, és megőrzési szabályokat ismer: a nyers percenkénti adat egy hónap után óránkéssé ritkul magától. Akkor indokolt, ha folyamatosan érkező méréssorod van, és megőrzési szabály is kell hozzá. Ha csak idősorod van, de nem folyamatos a beérkezés, akkor a particionált Parquet is elég — és nem kell hozzá szolgáltatást üzemeltetni.
A talajnedvesség-szenzorok tízperces adata tipikus eset; a heti hozamjelentés nem az. Az oldalsó rajz a `w07-partition` demóé. Ez nem gyorsítás, hanem elkerülés: a többi év fájljait meg sem nyitjuk. Egy idő szerint particionált Parquet-készlet sokáig kiváltja az idősor-adatbázist.
Az ábra betöltése…
Mielőtt idősor-adatbázist telepítenél, nézd meg ezt. Egymillió-kétszázezer sor, hat év, és a kérdés egyetlen évre szól. Particionálás nélkül hat megabájtot olvasunk be. Év szerint particionálva egy egész négyet — négy egész háromszor kevesebbet, és ez nem gyorsítás, hanem elkerülés: a többi év fájljait meg sem nyitjuk. De nézd meg a harmadik oszlopot. Év ÉS tábla szerint bontva ugyanannyi adat olvasódik, mert a kérdés nem szűr táblára — csak most negyven fájlból teszi, és a fájlonkénti nyolc kilobájt miatt egy egész kétszer drágább. A particionáló kulcs tehát nem attól jó, hogy finom, hanem attól, hogy a lekérdezéseid arra szűrnek.
Ebből jön a válasz a dia kérdésére: az idősor-adatbázis akkor indokolt, ha a beérkezés folyamatos ÉS megőrzési szabály is kell hozzá. Pusztán attól, hogy az adat idősor, még nem — egy idő szerint particionált Parquet-készlet sokáig elviszi, és nem kell hozzá üzemeltetni semmit.
| adatfajta | jellemző mérete | ahova való |
|---|---|---|
| gépadat (CAN-busz, telemetria) | napi több millió sor, időrendben | idősor-adatbázis, vagy idő szerint particionált Parquet |
| táblahatár, kezelési egység | néhány ezer poligon, ritkán változik | térbeli kiterjesztésű relációs motor (PostGIS) |
| műhold- és drónfelvétel | képenként több száz MB, sok felvétel | objektumtároló, felhőre optimalizált GeoTIFF-ként |
| laboreredmény, mintaadat | néhány ezer sor, sok metaadattal | relációs tábla — itt a séma a fontos, nem a méret |
| üzemi törzsadat (tábla, gép, dolgozó) | néhány száz sor, sokszor hivatkozva | relációs tábla, a többi réteg erre illeszt |
És most tegyük össze, hogy egy valódi agrárüzemben mi hol van. Négy forrásfajta, négy eltérő alak — és mindegyikhez más tároló való. A gépadat idősor, másodperces felbontással: idősor-adatbázisba vagy particionált Parquetbe. A táblahatár vektoros geometria: térbeli kiterjesztéssel ellátott relációs motorba. A műholdkép raszter: felhőre optimalizált GeoTIFF-ként objektumtárolóba. A laboreredmény kevés sor, sok metaadat: relációs táblába. Aki mind a négyet egyetlen technológiába akarja tenni, az háromra rossz választ ad.
A negyedik és ötödik sor a legkönnyebben elrontható: kicsi, tehát bárhova elfér — és éppen ezért kerül gyakran táblázatkezelőbe. Ott viszont nincs séma és nincs hivatkozási épség, és az egész lánc erre illeszt. A legkisebb tábla hibája a legdrágább.
Sorrend, amit érdemes megjegyezni. Először a tervet nézed meg, mert az mondja meg az okot. Aztán megnézed, mennyi adatot olvas be egyáltalán — gyakran itt derül ki, hogy oszlopot vagy partíciót lehet kihagyni. Csak ezután jön az index, és legvégül a SQL átírása. A legtöbben fordított sorrendben csinálják, és ezért nem tudják, mi segített.
A negyedik lépésnél állj meg egy pillanatra: az indexnek ára van, és ezt a 18. dián meg is mértük.
A cella betöltése…
Ez a particionálási demó teljes kimenete, hogy a fájlszámot és a bájtokat együtt lásd. A sorrend, amit az előző dián felsoroltunk, itt kap számot: előbb azt nézed meg, mennyi adat olvasódik be egyáltalán, és csak utána nyúlsz indexhez vagy SQL-hez. Ebben a mérésben a partíció négy egész háromszorosát spórolta — indexet még nem is vettünk fel.
Futtasd le kevesebb táblával is: a `FIELDS` átírásával megnézheted, hol fordul át az, hogy a finomabb kulcs megéri-e. A fájlonkénti költség a demóban 8 kB — valódi tárolón ez a nagyságrend, de mérd meg a sajátodon.
Vektor és raszter, közös koordinátarendszerben
Harmadik blokk. Eddig táblázatokról beszéltünk, ahol egy sor egy mérés. Az agráradat nagy része viszont helyhez kötött: a tábla egy sokszög, az ortofotó egy rács, a mintavételi pont egy koordináta. Ez nem nehezebb, csak más — és két olyan hibalehetőséget hoz magával, ami táblázatos adatban nem létezik.
Két alak, két világ. A vektoros adat geometria attribútumokkal: pont, vonal, sokszög — a mintavételi hely, a sorköz, a táblahatár. A raszteres adat szabályos rács, cellánként egy értékkel: ortofotó, műholdsáv, hozamtérkép. A kettő közötti váltás mindig veszteséges művelet, és ezt tudni kell előre, mert a legtöbb agrárelemzés pont ezen a határon dolgozik.
Az oldalsó rajz a gépi látás modul `cv-resolution` demójáé: 30 méteres cellánál a képpontok 14,1 %-a a táblahatáron ül, félig táblán kívül — és a hiba nem a tábla méretétől függ, hanem a kerület–terület aránytól. A hozamtérkép érdekes köztes eset: a kombájn pontsorozatot rögzít — tehát vektor —, de a felhasználó rácsként akarja látni. Az átalakítás döntéseket rejt, és ezeket nem szokás dokumentálni.
A koordináta a vetülettel és a dátummal együtt értelmes. Két réteg összevetése előbb közös rendszerbe hozás.
Ez az a hiba, amit mindenki elkövet egyszer. Két réteget összeteszel — a táblahatárt és az ortofotót —, és a tábla néhány méterrel odébb van. Nem a mérés rossz: a két réteg más vetületben van. A koordináta önmagában nem jelent semmit; csak a vetülettel és a dátummal együtt értelmes. Magyarországon ez rendszerint EOV és WGS84 keveredése, és az eltérés akkora, hogy a táblaszélen lévő sorok rossz táblához kerülnek.
A legkellemetlenebb változata az, amikor az eltolódás kicsi. Ilyenkor a térkép elfogadhatóan néz ki, és csak a táblaszéli sorok kerülnek rossz csoportba — az átlag pedig csendben elromlik. Az oldalsó rajz a `cv-calibration` demóé — képi analógia, nem vetületi átszámítás. Amit átvihetsz belőle: a referenciarendszer hibája nem zaj, hanem rendszeres eltérés, és a szélén a legnagyobb.
Az ábra betöltése…
Egy analógia a képi oldalról, mert ott ugyanez a hiba számszerűen kimérhető. Ha a kalibrálótáblát nem a tárgy síkjába teszed, hanem öt százalékkal távolabbra, akkor a méretbecslés pontosan öt százalékkal téved — a lépték lineáris a távolsággal, tehát a referencia relatív hibája egy az egyben átmegy az eredménybe. És a kép szélén jön hozzá a második tag: a lencsetorzítás a sarokban további tíz egész öt százalékot visz el. Egy nyolcvanöt milliméteres kalász a sarokban hetvenhat milliméternek látszik. Térbeli adaton ugyanez a szerkezet: a koordináta a vetülettel és a dátummal együtt értelmes, és a hiba a szélén a legnagyobb.
Ez analógia, nem ugyanaz a mérés: a demó képi kalibrálást mér, nem vetületi átszámítást. Amit átvihetsz belőle, az a szerkezet — a referenciarendszer hibája nem zaj, hanem rendszeres eltérés, tehát több felvétel átlagolása nem tünteti el, és a mezők szélén a legnagyobb.
A felbontás nem minőség, hanem döntés: mekkora a legkisebb dolog, amit látni akarsz.
A raszter felbontása nem minőségi jellemző, hanem döntés arról, mit akarsz látni. Tíz méteres cellával a táblán belüli foltosság látszik. Fél méteressel az egyes növénysorok. Száz méteressel csak a táblák közötti különbség. Egyik sem jobb: ha gyomfoltot keresel, a tíz méteres cella a foltot elkeni; ha regionális termésbecslést csinálsz, a fél méteres felbontás csak zajt és számlát hoz.
Fordítva is igaz: finomabb rácsra átmintavételezni nem hoz információt. A kép simább lesz, a tartalma ugyanannyi. Az oldalsó rajz a `cv-resolution` demóé: ugyanakkora, 12 hektáros tábla, két alakban. Nincs olyan mondat, hogy „tíz méter elég” — a kérdés a kerület a területhez képest.
A `cv-resolution` demó mérése: 12 hektáros tábla két alakban, a rács eltolására átlagolva.
Ez mért szám, és a hetedik hét legfontosabb térbeli ábrája. Két tábla, pontosan ugyanakkora területtel — tizenkét hektár —, csak az alakjuk más: az egyik zömök, a másik keskeny és hosszú. Nézd meg, hogyan nő a keverékcellák aránya a cellamérettel. Tíz méternél a zömöknél öt egész nyolc százalék, a keskenynél kilenc egész kettő. Harminc méternél tizennégy és huszonöt. Ugyanaz a terület, ugyanaz a felbontás — kétszeres különbség, pusztán az alakból.
Ezért nincs olyan mondat, hogy „tíz méter elég”. A kérdés nem a tábla mérete, hanem a kerülete a területéhez képest: a zömöknél 117,9 m/ha, a keskenynél 216,7. A parcellás, keskeny táblák az igazi vesztesek, és éppen azok a kísérleti parcellák.
Ha egy cellába két különböző felszín esik, a visszaadott érték a kettő keveréke — és nem tartozik egyikhez sem.
Ez a raszter legfontosabb csapdája, és később a gépi látásnál visszatér. Egy cella nem tudja, hogy a valóságban két dolog van benne. A táblaszéli cella fele növény, fele földút — és amit visszaad, az a kettő keveréke. Az érték látszólag rendben van, csak nem tartozik semmihez. Egy tíz méteres cellánál a táblaszél teljes első sora ilyen; egy kis parcellánál ez a cellák jelentős hányada.
Ezért szokás a táblahatárt befelé húzni néhány métert, mielőtt táblaátlagot számolunk. A negatív puffer nem szépítés, hanem a vegyes képpontok kizárása. Az oldalsó rajz a `cv-resolution` demóé. A keverékképpont lefelé húzza a táblaátlagot, mert a táblán kívüli felszín sötétebb. A javítás olcsó: egy cellányit befelé húzzuk a határt.
Ugyanaz a mérés: a táblaátlag hibája nyersen és egy cellányi negatív puffer után.
És itt a következménye, ugyanabból a mérésből. A függőleges tengelyen a táblaátlag hibája NDVI-egységben. Mindkét görbe lefelé megy, mert a keverékcella mindig lefelé húz — a táblán kívül alacsonyabb az NDVI. Harminc méteres cellánál a keskeny táblánál ez nulla egész nulla harmincegy. Ez nem hangzik soknak, de a szezonon belüli valódi különbségek is ilyen nagyságrendűek. A szaggatott vonal a negatív puffer után: egy cellányit befelé húzva a határt a hiba nullára megy.
A negatív puffer ára a terület: egy cellányi sávot eldobsz a tábla kerülete mentén. Keskeny táblán ez sok — harminc méteres cellánál a tizenkét hektáros keskeny tábla nagy részét. Ilyenkor nem a puffer a megoldás, hanem a finomabb felbontás; és ha az nincs, akkor ki kell mondani, hogy a táblaátlag alsó becslés.
Táblánkénti átlagos NDVI-t számolsz tíz méteres műholdképből, és az egyik keskeny, hosszú tábla átlaga évek óta gyanúsan alacsony. A szomszédos táblák rendben vannak. Mi a legvalószínűbb ok?
Táblánkénti átlagos NDVI-t számolsz tíz méteres műholdképből, és az egyik keskeny, hosszú tábla átlaga évek óta gyanúsan alacsony. A szomszédos táblák rendben vannak. Mi a legvalószínűbb ok?
Zárjuk a blokkot azzal, ahogy az első blokkhoz kapcsolódik. A vektoros adat táblázatos: sorok attribútumokkal, csak van egy geometria oszlopa is — tehát ugyanúgy él Parquet-ben, ugyanúgy particionálható. A raszter nem táblázat: a rá való formátumok cserép alapúak, és a cserép ugyanaz a gondolat, mint a particionálás. Ha a kép egy kis darabja kell, csak azt a cserepet olvasod be.
A közös elv mindkettőben ugyanaz, mint a hét elején: a legolcsóbb olvasás az, amelyik meg sem történik.
Amire az idő ténylegesen elmegy — és miért nem írjuk felül a nyerset
Utolsó blokk, és a legkevésbé látványos. Arról lesz szó, amire az elemzési munkaidő nagyobb része elmegy — nem a modellre, hanem arra, hogy az adat egyáltalán elemezhető legyen. Ezt sokan szégyellik bevallani. Nem kell: ez a munka része, és a hibák, amiket itt elkövetünk, csendesebbek és drágábbak minden modellezési hibánál.
| Hiányzó érték | üres cella, jelölt hiány, hibás nulla | van-e tartományon kívüli vagy gyanúsan kerek érték |
|---|---|---|
| Duplikátum | ugyanaz a mérés kétszer rögzítve | egyedi-e a kulcs, és ha nem, mitől |
| Mértékegység-keveredés | t/ha és kg/parcella egy oszlopban | befér-e minden érték a fizikailag lehetséges tartományba |
| Időzóna és időbélyeg | helyi idő, nyári időszámítás, UTC | van-e éjszakai mérés napsütésre, ugrik-e az adat márciusban |
Négy fajta, és mindegyikhez tartozik egy ellenőrzés, amit meg lehet írni. Ez a lényeg: az adatminőség nem érzés, hanem ellenőrizhető állítások halmaza. Ha egy szabályt le tudsz írni, akkor futtatni is tudod minden betöltéskor, és akkor a hiba a betöltéskor derül ki, nem három lépéssel később.
Az oldalsó rajz szándékosan az ÉRINTETT SOROK arányát mutatja, nem a torzítás nagyságát: az 0,7 % és 3017 % között szór, és egyetlen tengelyen a kicsik eltűnnének — pontosan az a hiba, amit az 5. hét tilt. A `w07-quality` demó mért torzításai: duplikátum 0,7 %, t/hold 2,2 %, hibás nulla −2,0 %, jelölt hiány −314,1 %, kg/ha 3017,0 %. És itt a lényeg: az érintett sorok aránya mind az ötnél 2 és 4 százalék között van — a GYAKORISÁGBÓL tehát semmi nem jósolja meg a kárt. Mindegyik sor harmadik oszlopa egy futtatható ellenőrzés. Ez a küldetés beadandójának is a váza.
A `w07-quality` demó mérése: 50 000 sor, a valódi átlag 6,401 t/ha. Az oszlop a táblaátlag eltérése.
Ez a dia az egész blokk tétele egyetlen ábrában. Öt tipikus adatminőségi hibát vittünk bele ugyanabba a tiszta méréssorba, külön-külön, és megnéztük, mennyivel tolják el a táblaátlagot. Nézd meg a nagyságrendeket. A kilogramm per hektáros egységkeveredés háromezer százalék. A jelölt hiány mínusz háromszáztizennégy. Ezek ordítanak — egy fél soros tartományellenőrzés mindet megfogja. És most nézd az első kettőt: nulla egész hét és két egész két százalék. Ezeket semmi nem fogja meg.
A kg/ha-os keveredés 3017 százalékos eltérése szándékosan hiányzik az ábráról: mellette a többi oszlop egy vonallá lapulna. Ez ugyanaz a döntés, amit az 5. héten hoztunk a k-mer-hisztogramnál és a futásidőknél — ha egy érték a többit olvashatatlanná tenné, nem a tengelyt csonkoljuk, hanem kivesszük és megnevezzük.
| a hiány fajtája | mit jelent | mit szabad vele |
|---|---|---|
| Nem mértük | a szenzor nem adott értéket | becsülhető, ha kimondod |
| Nem létezik | a parcellán nem volt vetés | NEM becsülhető |
| Hibás nulla | a kiesés nullaként rögzült | előbb meg kell találni |
A hiányzó értéket a legtöbben azonnal pótolni akarják. Előbb azt kell megkérdezni, mit jelent. Három különböző dolog néz ki ugyanúgy: nem mértük, nem létezik, vagy hibásan nullának rögzítették. A nem mért nedvességet érdemes lehet becsülni. A nem létezőt — mert a parcellán nem volt vetés — nem szabad. A hibás nullát pedig előbb meg kell találni, mert a becslést is elrontja. A háromból kettőt szokás összemosni, és ez csendes hiba.
A hibás nulla a legveszélyesebb, mert nem is hiányzik: benne van az átlagban, és lefelé húzza. A szenzorkiesés gyakran így jelenik meg.
Az ábra betöltése…
A demó saját ábrája, és a színek mondják meg a lényeget. Zöld, amit egy fél soros tartományellenőrzés megfog; piros, amit nem. Nézd meg, hogy a legnagyobb eltérések mind zöldek — a kilogramm per hektáros keveredés háromezer százaléka, a jelölt hiány mínusz háromszáztizennégye. És a két piros oszlop alig látszik: nulla egész hét és két egész kettő százalék. Ezek jutnak át egy jelentésbe.
A kg/ha-os oszlop az ábrán levágva szerepel, különben a többi egy vonallá lapulna — a demó kódja ezt ki is írja. Ugyanaz a döntés, amit az 5. héten a k-mer-hisztogramnál hoztunk.
A cella betöltése…
Itt a cella. A csavar a rossz egységű sorok aránya: emeld tíz százalékra. A kérdés az, hogy mikor bukik ki magától — vagyis hány százaléknál kezd el az összesítés láthatóan hazudni. Ha egy hiba elég ritka, semmilyen összesítés nem szól érte, és pontosan ezért kell soronkénti ellenőrzés.
Az öt hiba közül az egységhiba a legalattomosabb: a sor szintaktikailag helyes, az érték értelmes tartományban van, csak éppen más mértékegységben. Ezt sem séma, sem tartományellenőrzés nem fogja meg — csak az, ha tudjuk, mit várunk.
Egy rossz időzóna a teljes napi és szezonális elemzést elcsúsztatja — és csendben teszi.
Egyetlen példa, mert ez a fajta hiba önálló dia értékű. A szenzor helyi időt küld, az adatbázis UTC-t tárol, a jelentés megint helyi időt mutat — és valahol a három között valaki nem konvertál. Az eredmény nem hibaüzenet, hanem egy-két órás eltolódás. A napi ciklus elcsúszik, az öntözési döntés rossz órára esik, és semmi nem jelez. Márciusban meg októberben pedig egy óra hirtelen megjelenik vagy eltűnik.
A védekezés egyszerű és unalmas: mindent UTC-ben tárolunk időzóna-jelöléssel, és csak a megjelenítésnél váltunk helyi időre. Az „majd figyelünk rá” nem védekezés. Az oldalsó rajz a `w07-timezone` demóé. Két óra eltolás — és semmi nem jelez, mert minden érték értelmes marad. Ennyi elég ahhoz, hogy egy öntözési ajánlás rossz órára essen.
Az ábra betöltése…
A demó ábrája, mert a csúszás így válik láthatóvá. Két óránkénti átlaggörbe ugyanabból az adatból. A kék a helyes kezelés, a csúcsa tizennégy órakor. A piros ugyanaz, csak a helyi időt UTC-nek vettük — a csúcs tizenkettőre ment. Semmi más nem változott. És vedd észre: mindkét görbe teljesen hihetőnek néz ki. Ha csak az egyiket látnád, nem tűnne fel semmi.
Ez a dia az időzónahiba lényegét mutatja meg: nem ad hibaüzenetet, és nem ad képtelen értéket. Csak elcsúsztatja az egészet — és a döntés a csúcs órájához igazodik.
A cella betöltése…
A cella itt fut. A csavar a csúcs órája: állítsd hatra, vagyis hajnalra. A kérdés az, hogy ugyanannyit csúszik-e. A válasz megmondja, hogy a hiba a csúcs HELYÉTŐL függ-e, vagy mindig ugyanakkora — és ez dönti el, hogy egy meglévő elemzést mennyire lehet utólag megjavítani.
A csapda nem a nyári időszámítás, hanem az, hogy a tárolt időbélyeg zóna nélküli. Egy zóna nélküli időbélyeg nem hibás adat — egyszerűen nem adat, mert nem határoz meg egy pillanatot.
A `w07-timezone` demó mérése: 8 760 órás megfigyelés, óránkénti átlag.
Az időzónahibát azért nehéz elhinni, mert nem ad hibaüzenetet. Itt látod, mit csinál. Egy évnyi órás talajhőmérséklet, a valódi napi csúcs délután kettőkor — ezt mi raktuk bele. A kék görbe a helyes kezelés: a csúcs tizennégy órakor. A piros ugyanaz az adat, csak a helyi időt UTC-nek vettük: a csúcs tizenkettőre csúszott. Két óra. Egy permetezési vagy öntözési ajánlásnál ez a különbség a hajnali és a déli beavatkozás között.
A demó egy harmadik esetet is mér, és az a legkellemetlenebb: aki a sort SORSZÁM szerint vágja napokra, annál a tavaszi óraátállítás egy órás csúszást hoz, ami az őszi váltásig megmarad — 214 napon át, az év 59 százalékán. A napvágás időbélyeg szerint megy, nem sorszám szerint.
Nyers
Ez a hét utolsó szerkezeti gondolata. Három réteg. A nyers az adat úgy, ahogy érkezett — érintetlenül, akkor is, ha hibás. A javító lépés tisztít, típusoz, egységesít, és ez kód, nem kézi szerkesztés. Az elemzésre kész réteg az, amire az elemzés épül: típusos, dokumentált, és bármikor újraépíthető a nyersből. Ha a harmadik elromlik, nem katasztrófa — újra lefuttatod a lépést.
A „sosem írjuk felül” szabály ára tárhely, és az olcsó. A megsértésének ára a visszakövethetőség, és az nem az.
A kézi javítás egyszer működik. A kódba írt javítás minden új adagon lefut — és el lehet olvasni, mit tett.
A kézi javítás a legkényelmesebb és a legrosszabb megoldás. Megnyitod a táblázatot, kijavítod a három rossz értéket, elmented. Két hét múlva jön az új adat ugyanazzal a hibával, és senki nem tudja, mi történt először. A javítás akkor ér valamit, ha kód: le van írva, megismételhető, és a következő adagon magától lefut. Egy mondat különbség, de ez választja el a projektet a fenntartható munkától.
A kód mellékhatása egy dokumentáció, amit senki nem akart megírni: a tisztítószkript sorai pontosan megmondják, milyen hibák fordultak elő az adaton. Az oldalsó rajz a `w07-timezone` demóé. Kétszáztizennégy napot kézzel végigjavítani nem lehet — a kódba írt javítás viszont minden új adagon lefut, és felül lehet vizsgálni.
A `w07-timezone` demó mérése: a sorszám szerinti napvágás hatása egy teljes évre.
Egy konkrét eset arra, miért nem elég a kézi javítás. A sorszám szerinti napvágás a tavaszi óraátállítás után egy órát csúszik, és ez a csúszás az őszi átállásig megmarad. Nem egy nap romlott el, hanem kétszáztizennégy — az év ötvenkilenc százaléka. Egy kézi javítás ezt vagy végigkattintja kétszáztizennégyszer, vagy félbemarad. A kódba írt javítás egyszer íródik meg, minden új adagon lefut, és el lehet olvasni, mit tett. Ez a különbség nem kényelmi kérdés: a kézi javítás nem ellenőrizhető és nem ismételhető, tehát nem is része a módszernek.
A kódba írt javítás mellett szól a harmadik érv is, amiről ritkán esik szó: a kódot felül lehet vizsgálni. Egy kézi javítás után senki nem tudja megmondani, mit változtattál meg és mit nem — a verziókövetett szkript után igen.
Egy hozamtáblában néhány sor értéke háromezerszer nagyobb a többinél. Kollégád javasolja, hogy ezeket a sorokat töröljük, mert nyilván hibásak. Mi a helyesebb első lépés?
Egy hozamtáblában néhány sor értéke háromezerszer nagyobb a többinél. Kollégád javasolja, hogy ezeket a sorokat töröljük, mert nyilván hibásak. Mi a helyesebb első lépés?
Ugyanaz a lekérdezés háromszor. A beadandó egy táblázat és egy bekezdés: hol borul az első megoldás, és miért.
A heti küldetés ugyanaz a mérés, amit a blokkokban láttál, csak a te adatodon és a te gépeden. Egy több millió soros méréstábla, egy összegző lekérdezés, három út: memóriában betöltve, oszlopos fájlból, és beágyazott elemző motorral. A beadandó nem a nyertes megnevezése — az nem válasz. A beadandó az, hogy hol borul az első megoldás, és miért.
Két dolgot érdemes a bekezdésbe beletenni: mennyi a memóriahasználat csúcsa, és hogy a mért arányok visszaszámolhatók-e a kiírt táblázatból. A második a 06. dia tanulsága.
Az ábra betöltése…
A küldetés beadandója egy táblázat és egy bekezdés. Ez az ábra megmutatja, milyen az, amikor a bekezdés valamit állít. Bal oldalt a futásidő két kulcseloszlásra — és a ferde gyorsabb, ami az ellenkezője annak, amit én is vártam. Jobb oldalt ugyanaz szétosztott végrehajtásra: ott a ferde nyolcszoros helyett egy egész kettes gyorsulást ad. A beadandódban ilyen mondat kell: nem az, hogy melyik nyert, hanem hogy milyen feltétel mellett fordul meg az eredmény.
Az „egy motoron nem lassít, szétosztva igen” szerkezet nem a ferde kulcs sajátja. Minden olyan állításnál előjön, amit egy gépen mértek és fürtre általánosítanak — érdemes a beadandóban kimondani, melyik esetről beszélsz.
soronként egy mérés, vagy oszloponként egy jellemző; a kérdés alakja dönti el, melyik olcsóbb
szöveges csere, tömörített oszlopos tárolás, és a memóriabeli közös nyelv
az index a keresés gyorsítója, a terv pedig az, amit a motor TÉNYLEG csinál; a döntés a tervből jön, nem a másodpercből
sok apró írás kontra kevés, de nagy összegzés; két külön feladat, két külön motor
geometria pontokkal és poligonokkal, vagy szabályos rács cellákkal; a táblahatár az egyik, a műholdkép a másik
a beérkezett adat érintetlenül, és a belőle épített, javított változat; a kettő között a lépések kódban állnak
Ez volt a hetedik hét. Egy mondatban: a tárolás alakja a munkaterheléshez igazodik, és a legtöbb elemzési hiba nem a lekérdezésben keletkezik, hanem egy kimondatlan sémában.
| Rövidítés | Feloldás | Mit jelent |
|---|---|---|
| CSV | Comma-Separated Values | vesszővel elválasztott szöveges tábla: mindenhol megnyitható, de se sémája, se típusa nincs |
| SQL | Structured Query Language | a relációs lekérdezések nyelve; nem motor, hanem felület — több motor is érti |
| SCAN | a terv soronkénti végigolvasása | a lekérdezésterv szava: a motor az egész táblát elolvassa |
| SEARCH | a terv indexelt keresése | a lekérdezésterv szava: a motor odaugrik, nem olvas végig |
| OLTP | OnLine Transaction Processing | tranzakciós feldolgozás: sok apró írás és pontos sorlekérés |
| OLAP | OnLine Analytical Processing | elemző feldolgozás: kevés, de nagy összegző lekérdezés |
| UTC | Coordinated Universal Time | zónafüggetlen világidő; a tárolt időbélyeg alapja, a megjelenítés helyi idő |
| CAN | Controller Area Network | a járműves és gépi busz, amiről a telemetria jön |
| EOV | Egységes Országos Vetület | a magyar hivatalos síkvetület; a hazai földhivatali és üzemi adat ebben áll |
| WGS84 | World Geodetic System 1984 | a GPS és a műholdas adat globális rendszere — EOV-val összekeverve méteres hiba |
| GeoTIFF | georeferált TIFF | képfájl, amely a saját vetületét és helyét is tartalmazza |
| COG | Cloud-Optimised GeoTIFF | csempézett, több felbontású GeoTIFF: hálózaton is kiolvasható belőle egyetlen részlet |
| USING, COVERING | a lekérdezésterv szavai | `USING COVERING INDEX`: a motor magából az indexből válaszol, a táblához hozzá sem nyúl |
| NDVI | Normalized Difference Vegetation Index | normalizált vegetációs index; a térbeli fejezet tanpéldája |
| GIS | Geographic Information System | térinformatika; a PostGIS és a QGIS nevében is ez áll |
| PostgreSQL, MySQL, PostGIS | terméknevek, nem rövidítések | relációs motorok, illetve a PostgreSQL térbeli kiterjesztése |
Az adatbázisos hét rövidítései. A két vetületi kód külön figyelmet érdemel: agráradatot Magyarországon rendszeresen kapunk EOV-ban, a műholdas adatot viszont WGS84-ben — és a kettő összekeverése méteres hibát ad.
A PostgreSQL, a MySQL és a PostGIS nem rövidítés, hanem terméknév — de a végük beszédes: az SQL a lekérdezőnyelvre utal, a GIS pedig a Geographic Information System kifejezésre, vagyis térinformatikára.
Az ábra betöltése…
Három típus, és ez nem szegényesség: a 7. hét majdnem minden mérése kategóriák összevetése — két formátum, két motor, két szétvágás —, oda pedig az oszlop való. A vonal ott jelenik meg, ahol az időrend maga az információ, a napi görbénél. A harmadik panel a demóábra: a hét néhány mérése olyan rajzot kíván, log tengelyt vagy hőtérképet, amit a dia saját rajzolója nem tud.
Az ábra a `w05-encoding` demóé (`chartindex 4`). A paneleket a demó `TIPUSKESZLETEK` táblája sorolja fel, és pontosan annyi panel van rajta, ahány típust a következő dia táblázata felsorol — ezt a `check_diagramok.py` ellenőrzi is.
| diagramtípus | mit olvasol le róla | mire NEM jó |
|---|---|---|
| oszlop | kategóriák nagyságrendje és sorrendje — a hossz maga az érték | időbeli alakulás: a sorrend nem látszik rajta |
| vonal | időbeli alakulás: a meredekség és a fordulópontok | kategóriák összevetése: a vonal sorrendet állít oda is, ahol nincs |
| demóábra | amit a keretrendszer rajza nem tud: log tengely, hőtérkép, kis sokszorosok | saját adatod: ez a demó kimenete, nem szerkeszthető a dián |
A 7. hét szinte végig oszlopdiagramot használt, és ennek oka van: itt majdnem minden mérés kategóriák összevetése — két formátum, két motor, két szétvágás. Ahol idő van a vízszintes tengelyen, ott vonal áll, például a napi görbénél.
A felsorolás nem kézi lista: a `check_diagramok.py` ellenőrzi, hogy pontosan ezek a típusok szerepelnek-e a diasorban — ha egy új dia más típust hoz be, vagy egy típus kikopik, az építés szól.