Hogyan lehet nagymennyiségű adatot lekérdezni?

Fórumok

Sziasztok!
Elég kezdő javás vagyok, úgyhogy ne tessék nagyon flame-elni... Szóval: van otthon egy MySQL adatbázisom, és írtam NetBeans-el egy java proggit, ami kapcsolódik hozzá, és frankón le is kéri az adatokat, ha kisebb tábláról van szó. Ha nagyobbat akarok lekérdezni, akkor viszont meghal out of memory (javaheap) hibával. Teljesen a "tankönyvi" jdbc tutorialos kóddal próbálkoztam, és nyilvánvaló is lett, hogy az a baja, hogy a ResultSet túl nagy. Nézegettem a neten fórumokat, ahol hasonló problémákkal küzdöttek, de jellemzően az volt a megoldás, hogy a vm-et úgy paraméterezték, hogy több memóriát kapjon. Nekem ez nem megoldás, mert az adatbázisom folyamatosan nő (most kb. 1.7 millió sor van benne). Másik javaslat az volt, hogy korlátozzuk a lekérdezett adatok mennyiségét valami okos where klauzulával, és több darabban olvassuk fel az adatokat. Ez végülis járható út, csak elég favágó megoldás. Kérdésem az lenne, hogy van -e erre valami normális, javás megoldás, olyasmi, hogy mondjuk párezer soros page-enként hozzáférni az adatokhoz, vagy ilyesmi? Egyáltalán van -e valami javás konvenció arra, hogy hogyan nyúljunk hozzá nagyobb adatbázisokhoz?
Ja, mellesleg az lenne a feladatom, hogy egy paraméterezhető méretű window-val végig kellene szánkázni az adatbázison, és az épp aktuális window utolsó eleme mellé egy számított mutatót beírni.
Üdv.:
G

Hozzászólások

Aha, köszi, ez ígéretesnek tűnik. Kicsit még tanulmányoznom kell a technológiát, mert úgy láttam, van még pár használható metódus, pl. getMoreResults, meg ilyenek, ezek szerint egyszerre több resultsetet is lehet valahogy kezelni, kérdés, hogy ez ugyanazon lekérdezés eredménye, vagy mind külön query.
A LIMIT is jó ötlet, csak az az adatbázis oldaláról fogja meg a dolgot. Most, hogy így nézem, olyan, mint a ROWNUM az Oracle SQL-ben, csak kicsit szofisztikáltabb...
G

Szia!
Hat lehet, hogy a baratom, de nem tul jo, mert nem segitett rajtam. :)
Kicsit jobban utananezve a setFetchSize nem a visszaadott sorok szamat korlatozza, hanem a JDBC drivernek ad egy hintet, hogy az adatok lekerese soran milyen gyakran (hany lekert soronkent) kommunikaljon a szerverrel. Mindenesetre jo tippet adtal, mert mint kiderult, a statement ojjektumnak van egy setMaxRows metodusa is, ami viszont tenyleg a visszaadott sorok szamat korlatozza. Bar nem pont az, amit szeretnek, jobban orultem volna, ha letezik erre egy mar megirt "pager" megoldas, de kezdetnek nem rossz, az outofmemory hibat mar kikuszoboltem vele.
Azert lehet megkerdezek valakit az adatbazisos topikban is.
Udv:
G

Nézd meg a MySQL SELECT LIMIT opcióját. Az kell igazából neked.

Miért is van baj azzal, hogy rset.next()-el végigsétálsz a visszaadott RecordSet-en ?
Ha nem a memóriában tárolod a belőle nyert adatokat, akkor nem kellene elfogynia...

Szia!
Gondolom az alternatíva az lenne, hogy memória helyett lemezen tárolom az adatokat? Hogyan tudom megmondani a statement.executeQuery("blablabla")-nak, hogy a visszaadott resultsetet ne a memóriában tárolja? Nem lesz túl lassú a feldolgozás, ha előbb átszívom az adatokat a másik gépről, feldolgozom, majd visszaírom? Az én "modellemben" úgy néz ki a dolog, hogy 2-300.000-es mennyiségeket dolgozok fel memóriában, az eredményt diszkre írom, majd visszaküldöm a mysql szerverre, és egy joinnal összerakom az eredeti táblával.
Üdv.:
G

Kicsit pontosabban is leírhatnád, hogy mit jelent a feldolgozás. Mert ha valami egyszerű szűrést, akkor azt meg lehet úgy csinálni (ahogy már írták), hogy mindig csak "pár sort" tartasz a memóriában. Megnyitod a result setet, meg egy kimeneti fájlt. A result setből annyi lesz egyszerre a memóriában, amennyit beállítasz. A fájlból semmi. A resultseten lépkedve amit kiszűrsz azt a fájlba írod, a többit eldobod (Javában nincs rá referenciád, akkor már fel is szabadult a helye). finally blokkban lezárod a fájlt, meg a resultsetet is.
Szerintem eddig juss el, utána gondolkodj a visszatöltésen.

Szia!
Fentebb irtam, de akkor kifejtem egy kicsit pontosabban. Nem tudom, mennyire vagod az Oracle SQL-t, abban vannak u.n. analitikus fuggvenyek, vagy "window" fuggvenyek, amik roviden szolva ugy mukodnek, hogy a kurzornak megfeleloen elore, hatra, vagy mindket iranyban kialakitanak egy rekordokbol allo "ablakot" es az aktualis sor melle ezen ablak rekordjaibol szamolnak valamilyen erteket. Ezek jellemzoen egyszeru ertekek, mondjuk minden alkalmazott melle beirjak azon osztaly atlagfizeteset, amelyiken az illeto dolgozik, tehat "standard" mutatok, mondjuk atlag, szoras, osszeg, ntilis, rangsor, ilyesmik. Nekem hasonlora lenne szuksegem, csak javaban leprogramozva, es sokkal bonyolultabb szamitasokra. Vagyis nem szurni akarok, hanem minden sor melle betenni egy szamitott erteket, ami a parameterezheto meretu ablaknak megfelelo rekordok 4-5 mezojenek ertekei alapjan szamolodnak (pl. minden sor melle beirna a megelozo 10 sor valos szamnegyeseinek vektorszorzatat).
Igazsag szerint pontosan azt akarom csinalni, amit mondtal, csak nem par sorral, mert akkor nagyon sok ido megy el az adatok lekeresevel, hanem egy amolyan "lapozos" technologiaval, hogy felolvasok mondjuk 2*100000 sort egymas utan, es amikor az elso 100000 vegere erek, akkor azt eldobom, "hatratolom" a masik 100000-et, es beolvasom utana a kovetkezo 100.000-et, igy folyamatosan mondjuk 200.000 sort tartva memoriaban, amelyen folyamatosan folyna a feldolgozas, vagyis menne vegig rajta a window, es az egyedi azonositok melle irna ki a szamitott ertekeket vinyora. Amikor ez megvan, akkor az eredmenyt visszatolom az adatbazisba, egy joinnal hozzateszem az eredetihez, es elkeszultem.
Namost, ha letezne olyan JDBCs modszer, ami konkretan az ilyen helyzeteket kezeli, hogy ne akarja mondjuk a teljes adatbazist egy resultsetben felolvasni, hanem ilyen result page-enkent tudnam folyamatosan felolvasni az adatokat, es mondjuk "nextpage" metodussal felolvasna nekem a kovetkezot, akkor happy lennek, de sajnos ugy tunik, hogy ilyen nincs, ugyhogy kenytelen leszek magam megirni, ha nincs jobb otletetek.
Udv.:
G

Nem tudom, pontosan hogy kapod jelenleg az OutOfMemoryException-t, de ha felolvasod a 200e rekordot a memóriába, akkor kaphatsz is :)

Az miért nem elég, hogy egyszerre csak 1 window-nyi adat van a kezedben, és rset.next()-el olvasod fel az igénynek megfelelően a következőt? A setFetchSize-al be lehet állítani, hogy ne minden next menjen az adatbázishoz (azaz már ő is cache-el).

GT

Miután leírtad, mit akarsz csinálni már tényleg nem értem mi az akadály.
Szerintem: Mivel számolni fogsz az adatokkal úgyis fogsz csinálni egy burkoló osztályt az adatbázis sorainak. Tehát a lépések:
1. Legyen meg, hogy végigolvasod a táblát, de nem tárolod sehova. Ez a setfetchsize vagy mittudoménmi hangolásán múlik, hogy ne foglaljon túl sokat. Ilyet még konkértan nem csináltam adatbázisból eddig mindig kevés már leszűrt adatot kérdeztem le.
2. A rekordokat (másolatot) beolvasáskor tedd bele a burkoló osztályba, így a resultsettől függetlenül a kezedben van az adathalmaz
3. A burkoló ojjektumokat gyűjtsd mondjuk egy ArrayList-be. Ha rendezni is kell, akkor jobb lehet egyből TreeMap-be tenni.
4. mikor megvan az N ojjektum, dolgozd fel, aztán usgyi üríted az ArrayListedet és kezdede elölről.
5. Az ojjektumaid memória használatát tutod monitorozni többféleképpen:
1. Csak 1 féle funkciót aktiválsz a programodban (csak resultset felolvasás, vagy csak ojjektumlista építés), System.gc() és java.lang.maxmemory, freememory, totalmemory hármassal meg lehet tudni, hogy mennyit eszik épp a program. Ezekből következtetni lehet 1 ojjektum méretére, amiből már kiderül, hogy mik a rendszer korlátai
2. vannak Javás memória elemző módszerek is (mindenféle JVM kapcsolókkal szedhetők elő), de ezeket nem javaslom, az első módszer legtöbb esetben elegendő, és kevesebb otánajárást igényel
6. Ha nem fér így sem bele a memóriába, akkor nem lehet megcsinálni. Illetve meg lehet próbálni, hogy nem ojjektummal burkolod, hanem valami tömör módon bájt, int, vagy float tömbbe írod az adatokat, azzal lehet memóriát spórolni (viszonylag sokat). Ha erre az "adatszerkezetre" írsz burkoló osztályt statikus metódusokkal, ami ojjektumszerűen éri el, akkor még szépen is lehet. 1X csináltam ilyet, de inkább csak kísérletképpen.
7. Ha 6 szerint sem fér a memóriába, akkor assemblerben sem fér bele és a feladat lehetetlen kategóriájú.

Sziasztok!
Azert irtam Oracle-t, mert benn a cegnel azon dolgozom, de itthon MySql-em van, amit le akarok programozni, az pedig kvazi olyan, mint az Oracle analitikus fuggvenyei, amik MySQL-ben nincsenek (meg), csak mondhatni tetszoleges szamitasi muveletet lehet vele vegezni.

gthomas: 200000 sort csontnelkul beolvas (Sun Ulta10-es 1 Giga memoriaval), leteszteltem (a jvm-nek nem tudom mi a default memoria parametere, de nem volt gond a 200.000 sor). Az 1.7 millaval mar persze kiakad. A razta altal irt linken (alul) megvan, hogy hogyan tudom a Drivert row-by-row streamingre kenyszeriteni, es az mar nagyjabol meg is felel nekem, csak kicsit kenyelmetlen, hogy amig le nem fut az executeQuery(), addig nem lehet mast csinalni, hacsak nem korlatozom a visszaadott sorok szamat getMaxRows()-al, vagy LIMIT-tel adatbazis oldalon. Na mindegy, szoval innen mar szerintem megoldom.

asch: koszi a tippeket ezeken at fogom ragni magam. Fontos lesz meg a performancia. Szerinted ha ilyen streamingben dolgozom fel az adatokat, az a leggyorsabb megoldas? Hogy mukodik ez a streaming dolog? minden egyes sorert kuld egy kerest a Driver a szervernek?

Koszi!
Udv:
G

Általában informatikában midnen művelet pufferelve történik, amit lehet pufferelni. A fetchsize pont a lekérdezés ütemét szabályozza. Row by row ha azt jelenti, amire gondolok, akkor az jelentősen lassítja a feldolgozást a sok TCP pingpong miatt.
Tehát sebesség szempotjából:
- a lekérdezés tempóját ki kell (inkább úgy mondanám, hogy lehet, de szerintem felesleges) kísérletezni, hogy a leggyorsabb. Ez az odavissza menő kérések, meg adminisztratív cuccok overheadjén múlik, mivel a hasznos adat mennyiségét nem lehet csökkenteni.
- a feldolgozás nem lehet sokkal gyorsabb annál a megoldásnál, hogy minden adat a memóriában van.

Amúgy Oracle-ből is az XE verziót ingyen felteheted magadnak. Van Linuxos verzió is, nem kell hozzá windows sem.

Értem. Végülis van rá mód, hogy kipróbáljam, megnézem, hogy mennyi idő a teljes táblát FORWARD_ONLY típusú ResultSettel felolvasni, meg mennyi mondjuk 10-15 darabban kurzoros típusúval, aztán ha szignifikáns a különbség, akkor az utóbbit választom.

Oracle XE-m is van otthon, csak a javás kapcsolatot nem tudtam gyorsan felállítani, nem tudtam, hogyan állítsam be neki a TNSNAMES-T, meg a Listenert (érdekes módon a PL/SQL Developeremhez hozzá sem kellett érni, az csontnélkül kapcsolódik hozzá), a MySQL-es konfiggal meg 20 perc alatt megvoltam.
Meg aztán a MySQL-nek van azért pár hasznos megoldása, ami az Oracle-nek nincs (ez mondjuk kölcsönös), és nekem most jól jött (pl. ez a LIMIT dolog).

(Nem tudok olyan hozzászólást írni, amiben leírom a SELECT statementet, amire szükséged lenne.
Már ötödször írtam meg a kommentet. Most feladtam.)

Ahogy már más is javasolta: a select végére tedd be a
"limit 100000 offset 0"
"limit 100000 offset 100000"
"limit 100000 offset 200000"
"limit 100000 offset 300000"
stb.. záradékokat. Ezt elég könnyű leprogramozni. NEm a legszebb JAva-s megoldás, de elég gyengék a JDBC driverek ilyen tekintetben.

Amire figyelj, hogy a lekérdezésbe tegyél sorbarendezést olyan mező szerint, amit nem változtatsz a visszaírás közben és egyedileg azonosítja a sort. Ellenkező esetben összekuszálódhatnak a sorok a két ablak között.

Remélem segítettem.

Miert nem csinalod meg az egeszet egy tarolt eljarasban?
Akkor nem a te geped lenne a gyenge lancszem, adatbazisszerver meg azert van, hogy hasznaljuk.
szoval ahelyett, hogy kliens oldalon valositanad meg ezt a csuszoablakozast, ird meg szerver oldalon, a kliensbol meg csak hivd meg ezt a fuggvenyt/procedurat.
Mysql-t nem ismerem ebbol a szempontbol (tarolt eljarasok), viszont tehetsz fel ingyen oracle-t xe-t, 10g-t, vagy postgresql-t.
mindkettoben van(lehet) pl/pgsql, meg postgresben tuti, de szerintem oracle-ben is: pl/java.

Tyrael