JPA, eclipselink, 10 millió rekord, update nagyon lassú

Fórumok

Van egy adatbázis amiben van egy több mint 10 millió rekordot tartalmazó tábla.

Java oldalon egy ugyan ennyi entitásból álló ArrayList reprezentálja ezt az adathalmazt 

Egy üzleti logika minden egyes entitást módosít majd ezek visszaírásra kerülnek a JPA által az adatbázisba. Na ez tart irdatlan sokáig. Több mint egy óra amíg ez a bő 10 millió update megfut. 

Elég sok mindent kipróbáltam már. Egy tranzakcióval, x rekordonként külön tranzakcióval, entitymanager.flush() meghívásával de nem javult érdemben a teljesítmény. 

EclipseLinkben bekonfiguráltam a batch-write opciót is de így sem. És ez a több mint egy órás időtartam agyrém főleg úgy, hogy egy teljes adatbázismentést ami a mysqldumppal készült és benne van ez a 10 millió rekord kb. 3 perc alatt visszatölt.

Hozzászólások

Bekapcsoltam az EclipseLink logolását.

Ebből egyértelműen látszik, hogy ha be van kapcsolva a batch update 

<property name="eclipselink.jdbc.batch-writing" value="jdbc" />
<property name="eclipselink.jdbc.batch-writing.size" value="1000000" />

akkor kötegelten küldi, ha ki van kapcsolva akkor egyesével hajtja végre az updateket. 

Ami nekem furcsa, hogy bármekkora értéket állítok be a batch-writing.size-nak teljesen randomszerű, hogy mekkora kötegekben küldi el az utasítást. Van, hogy egy utasításként küldi el van, hogy öt darabot, van, hogy ~100-at de annál többet nem küld soha.

Indexet egyesével frissíti az külön finom.

Gábriel Ákos

kod es db schema nelkul csak tapogatozni lehet a sotetben.
A dump visszatoltes persze, hogy gyors, ott egy tablat egyetlen sql parancs tolt vissza.

> Egy üzleti logika minden egyes entitást módosít majd ezek visszaírásra kerülnek a JPA által az adatbázisba.

Most nem mondom, hogy ez egy hibás üzleti logika, csak azt, hogy a relációs adatbázis nem a 'beolvasom az egészet, memóriában módosítom, visszaírom' stílusú  feldolgozásra való. Erre mondjuk egy CVS vagy XML file lenne jó.

Nyilvánvalóan minden is "üzleti logika", és az adatokat mindig sql adatbázisban tároljuk (a relációs az túl gyenge kifejezés), mert az az egyetlen eszköz a tranzakció megvalósítására. És minimum xmlben, mert az a trendi. Ezzel párhuzamban egy puszta számot json segítségével közlekedtetünk. (stb.) :-D

Ja, amit leírtam, az mind felesleges, mert csak egy full table scan + update áldozatai lettünk. Ha ilyenre van szükség, akkor valami rossz helyen és rossz módon van tárolva. Ha nem így lenne, akkor az export-import nem lenne egy nagyságrenddel gyorsabb.

A fentiekbe beletörődve, már csak az a kérdés, hogy akkor miért kerül ki az "üzleti logika" az adatbáziskezelő hatóköréből?

+1

Olyan mintha az ország minden betegét úgy gyógyítanánk, hogy mind a 10m ember sorban áll, majd aki beteg az kap valamit, majd mindenki hazamegy. Lehet hogy nem egy tebeg van, de biztos nem 10m.

Biztos minden sort fel kell olvasni? Mennyi változik? (Az SQL pont arra jó, hogy "távol" tárolt adatokat "távoli" műveletekkel módosítsunk).

Szerintem is sokkal inkább az a kérdés, hogy milyen gyakran kell ezt eljátszani mind a 10M rekordon.

Ha ezekből csak az utolsó ~1000 változik, akkor előbb azokat kell módosítani, majd a többi egyszer csak kész lesz, várd ki. Jövőre megint csak lesz egy óra, amíg végigfuthat.

Ha mindenképpen szinte "azonnal" kell ennyi rekordot módosítani, akkor dobjátok ki az egészet tokkal-vonóval, és vegyetek egy normális adatbázis kezelőt (pl. Progress OpenEdge ) .

Nem csak az M$ számol furán... A Zinternet lenne ilyen gyors?
65% [62 Sources 1528 kB/6239 kB 24%] 3062 PB/s 0s

Valószínűleg a batch update-el lesz a gond.

A teszt kedvéért legeneráltuk az update utasításokat egy sql scriptbe és meg néztük úgy mennyi ideig fut (mysql <teszt.sql).

Ha START TRANSACTION és COMMIT közé vannak betéve akkor kevesebb mint 3 perc, ha nincs akkor több mint egy óra.

Ha bekapcsolom az EclipseLink-ben a logolást akkor egyértelműen látszik, hogy ha nincs bekapcsolva a batch update akkor egyesével küld el minden updatet, viszont ha be van kapcsolva a

<property name="eclipselink.jdbc.batch-writing" value="jdbc" />

akkor látszik hogy kötegelve küldi el. 

Ami viszont fölöttébb furcsa, hogy bármilyen érték van megadva a

<property name="eclipselink.jdbc.batch-writing.size" value="1000000" />

propertyben teljesen random szerűen kötegel. Hol csak 2 updatet, hol 10, 20 darabot, de ~száz darabnál sosem többet.  

https://www.eclipse.org/eclipselink/documentation/2.7/concepts/data_access009.htm

Egy batchben alapesetben egyféle utasítást pakol bele. Így annyi lesz a batch mérete, ahány ugyanolyan típusú SQL query van egymás után.

A tranzakció auto-commit be van állítva a JDBC kapcsolaton? Az alapértelmezés ugye, hogy igen.

Ha igen, akkor megmagyarázza, hogy miért tart ilyen sokáig: minden egyes SQL query külön tranzakcióban fut.

Ha ezt kikapcsolod, akkor viszont figyelj rá, hogy magadnak kell kezelned ezután a tranzakciókat.

>akkor viszont figyelj rá, hogy magadnak kell kezelned ezután a tranzakciókat.

Lehet, hogy mar most is neki kell. JPA-rol beszelunk, de nem tudjuk, hogy Spring-rol vagy Java EE-rol van-e szo, Container vagy pedig Bean managed tranzakciok, mi tortenik egy tranzakcion belul, hiv-e valaki flush-t commit elott, stb stb. Csilliard oka lehet annak, hogy ugy viselkedik ahogy.

Mi a DB? Ha mysql, akkor elvileg a connection stringen külön lehet engedélyezni batchrewrite-ot a rewriteBatchedStatements=true -val, ami itt neked pont kéne. Ha mysql.