A magazintémától a projektgyakorlatig
A bejegyzéshez tartozó szolgáltatási és technikai oldalak
Egy BDE-leváltás natív csatlakozással Bulk-Insert Array DML-lel gyakran a leggyorsabb módja annak, hogy sok rekordot juttassunk adatbázisba: ezer külön INSERT helyett egy paramétertömböt kötünk, és egyszerre küldjük a szerverre. A gyakorlatban azonban gyorsan előjön a buktató: egy rekord megsérti az egyedi indexet, egy NOT NULL mező üres, egy idegen kulcs nem illeszkedik – és hirtelen nem világos, melyik sor törte meg a batch-et, hogy egy rész már beírásra került-e, és hogyan lehet tisztán folytatni anélkül, hogy adatinkonzisztenciát okoznál.
Pont erről szól ez: hogyan alkalmazd az Array DML-t úgy, hogy soronként megbízható hibainformációt kapj, a tranzakciót kézben tartsd, és az üzem során visszakövethető legyen, mi történt. A fókusz nem az API elméleti bemutatásán van, hanem azon a szélső eseten, amely valódi importoknál rendszeresen előfordul: nagy batch, néhány hibás sor, de mégis sebességet akarsz.
FireDAC Bulk-Insert mit Array DML: Warum Array DML beim Bulk-Insert überhaupt lohnt
Az Array DML (Data Manipulation Language) a FireDAC-ben azt jelenti, hogy a paramétereket nem egyenként, hanem tömbként kötöd meg. A FireDAC ezután (a drivertől/DB-től függően) kevesebb körutat igényel, szerveroldalon hatékonyabban dolgozhat, és drasztikusan csökkenti a kliensoldali overheadet. Ez három helyzetben különösen releváns:
- ETL- és importfolyamatok: CSV/XML/JSON be, normalizálás/leképezés, majd staging- vagy cél táblába.
- Interfész-puffer: REST- vagy MQ-payloadok gyűjtése és periodikus perszisztálása.
- Napló-/eseménytáblák: sok kis INSERT, ahol a késleltetés dominál.
A haszon azonban nem ingyen van. Array DML használatával a komplexitást a „sok egyedi utasításról” „egy soksort tartalmazó utasításra” helyezed át. Ez jó a teljesítmény szempontjából, de nagyobb kihívást jelent a hibadiagnosztika, a tranzakciólogika és az újrafuttatás terén.
Der typische Randfall: Ein Batch, eine kaputte Zeile
A működés klasszikus esete: 50 000 sort importálsz. Egy 1 000-es ArraySize-ot választasz, mert nem akarsz minden sorhoz külön körutat. A 17. batch meghiúsul. Az adatbázis csak annyit jelez: „duplicate key” vagy „violates foreign key constraint”. A felületen vagy a szolgáltatás naplójában gyakran csak annyi látszik: „ExecSQL failed”.
Tiszta hibakezelés hiányában általában két rossz dolog történik:
- Az egész batch-et elveted, pedig 1 000-ből 999 sor rendben lenne.
- Visszatérsz az egyesével futó INSERT-ekhez, és tartósan elveszíted a teljesítményelőnyt.
A cél egy harmadik út: batch-teljesítményt megtartani, de hibára pontosan naplózni (sorindex, kulcsértékek, DB-hibaszöveg) és opcionálisan a „good rows”-okat commitálni – attól függően, mennyire kritikus a konzisztencia és az idempotencia (többszöri futtatás ismétlődés nélkül) a folyamataidban.
FireDAC Array DML: A releváns állítóelemek (mítoszok nélkül)
Bulk-Insert esetén Array DML használatakor a gyakorlatban mindig ugyanazok a beállítások a döntők:
1) ArraySize és batch-méret
ArraySize (TFDQuery/TFDCommand esetén) határozza meg, hány „sor” FireDAC dolgoz fel egy hívásban. Nagyobb nem feltétlenül jobb. Túl nagy érték több memóriát igényel a kliensen, nagyobb terhelést jelent a hálózaton, nagyobb lock-/log-terhelést a szerveren, és hiba esetén nagyobb a „blast radius”. Robosztus importoknál gyakran a 200 és 2 000 közötti batch-méret jó kiindulópont, a mezők száma, BLOB-ok és késleltetés függvényében.
2) Tranzakciós határ
Döntened kell: batchenkénti commit vagy commit a teljes import végén. Ez nem ízlés kérdése, hanem üzemeltetési döntés:
- Batchenkénti commit: korlátozza a zárolásokat és a tranzakciós log méretét, egyszerűbb a visszaállás/újraindítás, de köztes állapotok láthatók (az izolációs szinttől függően). Hiba a 17. batch-ben azt eredményezheti, hogy az 1–16. batch már benne van a rendszerben.
- Commit a végén: „mindent vagy semmit”, szakmai értelemben konzisztensebb, de nagy adatmennyiségnél hosszú zárakat, nagy rollbacket kockáztatsz, és hiba esetén minden elveszhet.
Számos interfész- és importfolyamatnál a „batchenkénti commit” reálisabb üzemeltetési stratégia – de csak akkor, ha az idempotencia és a duplikátumkezelés rendezett (pl. természetes kulcsokkal, upsert-tel vagy import-ID-val).
3) UpdateOptions és Prepared Statements
Ismétlődő batch-eknél érdemes az állítást előkészítve hagyni. A „Prepare” azt jelenti, hogy FireDAC engedi a DB-nek a statement parse-olását/kompilálását és újrafelhasználását. Adattárolótól függően ennek érezhető hatása lehet, különösen nagy frekvencia mellett. Itt nem trükkök kellenek, hanem: azonos Query-objekt (vagy azonos TFDCommand) következetes újrafelhasználása és stabil paramétertípusok.
Tiszta soronkénti hibakezelés: amire tényleg szükséged van
Ha soronként szeretnél hibákat kezelni, három dologra van szükséged:
- Hozzárendelés: Melyik Array-index (0..N-1) hibázott?
- Kontextus: Milyen szakmai kulcsértékek tartoznak ehhez a sorhoz (pl. külső ID, ügyfélazonosító, időbélyeg)?
- Vezérlés: Mit csinálsz utána? Megállítod, csak a hibás sorokat kihagyod, vagy feldarabolod a batch-et?
FireDAC-tól és drivertől függően előfordulhat, hogy hibát soronként ad vissza. Gyakorlatilag ez azonban nem mindig „automatikusan elérhető”. Számolnod kell azzal, hogy néhány adatbázis/provider csak az első hibát jelzi, vagy egy hiba miatt a batch további részét egyáltalán nem hajtják végre. Pont ezért egy robosztus minta általában kétlépcsős:
- Stufe A: Próbáld meg a batch-et Array DML-ként feldolgozni.
- Stufe B: Ha a batch megbukik, oszd ketté (félbevágás) vagy térj kontrolláltan egysoros feldolgozásra – de csak erre a batch-re – és naplózz részletesen.
Ez több munkának tűnik, de az importfolyamatokban ez a különbség aközött, hogy „hajnali 02:00-kor minden megáll” és aközött, hogy „az import végigfut, 7 sor kerül a hibalistára”.
Gyakorlati minta: először batch, majd célzott izolálás
A következő minta bevált az olyan, folyamathoz közeli szoftvermegoldásokban, ahol az adatok minősége vegyes:
1. lépés: Adatok egy Batch-struktúrába csomagolása (hibakontekstussal együtt)
A beimportálandó adatokat ne csak nyers értékként tárold, hanem minimális kontextussal: külső ID, a forrásból származó sorszám, esetleg hash/checksum. Ez nem „Nice to have”: hiba esetén nem akarod újra beparse-olni a CSV-t, hogy kiderüljön, mi ment tönkre.
2. lépés: Array DML végrehajtása
Beállítod az ArraySize-ot a batch hosszára, paramétereket tömbökként kötsz, és lefuttatod az ExecSQL-t. Fontos: a paramétertípusokat stabilan tartani (pl. numerikus mezőknél ne köss néha Stringként, néha Integerként), különben az adatbázis implicit castokat hoz létre, vagy FireDAC-nek kell elemenként konvertálnia.
3. lépés: Hibajelenség – a Batch leszűkítése a vak ismétlés helyett
Ha az ExecSQL megbukik, két robusztus opciód van:
- Binary Split (felosztás ketté): oszd a batchet két felére, és próbáld meg mindkét felet külön Array DML-ként. Ezt ismétled, amíg olyan kis mérethez nem érsz, amit egyesével meg tudsz vizsgálni. Előny: sok teljesítményt megtarthatsz, ha csak néhány sor hibás. Hátrány: több logika, és rendszerszintű hibák (pl. hibás adattípus) esetén kevésbé hatékony.
- Fallback egyes sorokra az adott batchhez: beállítod ArraySize=1 (vagy egyedi értékeket kötsz), és soronként futtatod, logolod a hibákat és folytatod. Előny: egyszerű, soronként biztos. Hátrány: ebben a batchben elveszíted a sebességet.
Gyakorlatban mindkettőt kombinálom: először 1–2 alkalommal splittelek (hogy a „jó blokkokat” gyorsan átengedjem), majd kis maradék esetén egyes sorokra váltok, hogy egyértelmű hibainformációkat tudjak naplózni.
Hibaobjektumok és üzenetek: Mit érdemes kinyerni FireDAC-ből
FireDAC adatbázis-hibákat kapszuláz kivételekbe (tipikusan EFDDBEngineException) részletekkel. Az üzemeltetés szempontjából három szint fontos:
- DB-hibakód (adatbázis-specifikus): pl. SQLSTATE PostgreSQL-nél, Error Number SQL Servernél.
- Constraint-/objektumnév: gyakran benne van a hibaüzenetben (Unique index, FK-constraint).
- Statement-kontekstus: tábla, művelet, esetleg paraméterértékek (személyes adatoknál óvatosan).
Ha soronként szeretnél naplózni, hibánál emellett a sor azonosítása is szükséges. FireDAC adott esetben adhatja az array-indexet. Ne támaszkodj azonban kizárólag erre. Mindig építs be egy saját indexet (pozíció a batchben), és ehhez a pozícióhoz naplózz legalább egy üzleti kulcsot.
Bukkanók, amelyek valódi importoknál időt emésztenek fel
1) „Csak egy sor volt” – de a tranzakció már „dirty”
DB-tól és drivertől függően egy hiba eredményezheti, hogy az egész statement-végrehajtás sikertelennek minősül, és a tranzakció olyan állapotba kerül, ahol vagy kifejezetten végre kell hajtanod egy rollbacket, vagy ahol további statementek fognak hibázni. Különösen egyes drivereknél az a feltevés, hogy „hiba után egyszerűen folytatjuk”, nem megbízható.
Következmény: ha tranzakción belül dolgozol és egy batch megbukik, az alapértelmezett út: a jelenlegi batch-kontekstus rollbackje (vagy a teljes tranzakció), majd újrakezdés. Ez illeszkedik a „Commit batchenként” megközelítéshez.
2) Autocommit vs. explicite tranzakció
Ha nem indítasz explicite tranzakciót, gyakran a driver/provider dönti el, hogyan committálja a statementeket. Tömeges importoknál ez ritkán az, amit szeretnél. Az explicite tranzakciók kontrollt adnak a következők felett:
- Zárolás időtartama
- Rollback-viselkedés
- Újraindítási pontok
És: explicit nem azt jelenti, hogy „egy óriási tranzakció“. Azt jelenti, hogy „tudatosan“.
3) Trigger, Constraints és mellékhatások
Az Array DML felgyorsítja az átadást, de nem feltétlenül a szerveroldali munkát. Ha a cél táblán vannak triggerek (pl. audit-logging, automatikus státuszszámítás), akkor a szűk keresztmetszet esetleg nem az INSERT, hanem maga a triggerkód. Ilyenkor egy batch kevesebb roundtrip-et eredményezhet, de az adatbázis-szerver CPU-ja marad a limitáló tényező.
Rendszergazdáknak és technikai vezetőknek: teljesítményprobléma esetén érdemes megnézni a Wait Events/Locks-okat és a tranzakciós naplót. A Bulk-Insert ilyenkor csak a kiváltó esemény, nem maga az ok.
4) Adattípusok és implicit konverziók
Az egyik leggyakoribb „Miért lassú?“ ok: a paramétereket stringként kötjük, az adatbázis pedig soronként castolja őket Integer/Date/Decimal típussá. Ez láthatatlan, de költséges. A stabil teljesítmény érdekében:
- Állítsd be a paraméterek adattípusát megfelelően (dátum dátumként, szám számként).
- Decimálisoknál ügyelj a locale-buktatókra (vessző vs. pont). FireDAC itt általában helyes, de kevert források nem.
- Időzónák/UTC-stratégiát előre tisztázni (a timestampok importoknál klasszikus problémák).
5) Hibaüzenetek embereknek, nem az automatizálásnak
Csábító a hibaüzenet szövegét parse-olni („duplicate key value violates unique constraint …“). Ezt csak végső eszközként tedd. Jobbak a strukturált kódok (SQLSTATE, hibaszám). Sajnos nem minden driver adja mindezt egyformán. Tervezd be ezért mindkettőt: kódot és szöveget, plusz opcionálisan „constraint-név a szövegből“, de kemény függőség nélkül.
Hibakeresési tippek: Így találod meg gyorsan a hibás sort
A batch reprodukálhatóvá tétele
Ha egy import szórványosan elbukik, reprodukálhatóságra van szükség. Ments per batch egy kis diagnosztikai fájlt vagy egy naplóbejegyzést, amely tartalmazza:
- Batch-szám és időpont
- ArraySize és tranzakciós mód
- a szakmai kulcsok listája (pl. külső azonosítók) a batchben
Ez gyakran elegendő ahhoz, hogy utólag célzottan elindíts egy mini-importot csak ezekre az azonosítókra.
A végső SQL láthatóvá tétele (de adatvédelmi szivárgás nélkül)
Hibakereséskor tudni akarod: helyes-e az SQL? Rendben vannak-e a paraméterek? FireDAC monitoringot/tracinget kínál FDMoni-komponenseken és driver-naplózáson keresztül. Produkcióközeli környezetekben fontos:
- A tracinget célzottan, csak ideiglenesen engedélyezni (teljesítmény és adatvédelem miatt).
- A paraméterértékeket csak biztonságos környezetben vagy maszkolva naplózni.
- Személyes adatok esetén: a naplóban csak technikai kulcsok (azonosítók), semmi világos szöveg.
Ha split-tesztelsz: megszakítási feltételek definiálása
A bináris felosztásnál nem akarsz végtelenül osztani. Állíts be egy alsó határt, pl. „20 sor alatt váltás egyes módra“. És határozz meg egy limitet, hogy összességében hány hibát tolerálsz, mielőtt megszakítod az importot (pl. rendszerszintű mapping-problémák esetén). Ellenkező esetben végtelen hibalistákba futsz és blokkolod a további feldolgozást.
Mikor éri meg a ráfordítás (és mikor nem)
Az Array DML soronkénti hibakezeléssel különösen megéri, amikor:
- Nagy mennyiségű sor feldolgozása (ezrekről milliókra).
- Kevés sor hibás, de mégis végig akarod futtatni.
- Az importnak üzem közben stabilan kell futnia (pl. éjszakai feldolgozás, UI nélküli szolgáltatás).
- Vissza kell játszanod egy hibalistát a szakmai területnek/forrásnak (sor-hivatkozással).
Kevésbé érdemes, ha:
- ha csak néhány tucat sort írsz (egyedi INSERT-ek rendben vannak),
- az adatok minősége olyan rossz, hogy a sorok 30–50%‑a meghiúsul (ilyen esetben egy staging‑stratégia célszerűbb),
- amúgy is DB‑natív kötegelt betöltési eljárást használsz (pl. COPY PostgreSQL‑ben, BCP/BULK INSERT SQL Serverben) – ilyen esetben az Array DML nem a megfelelő eszköz.
Alternatív architektúra: staging‑tábla a „közvetlen célba” helyett
Ha rendszeresen kevered a különböző minőségű adatokkal, az egyszerű „Insert direkt in die Zieltabelle” gyakran rossz döntés. Egy staging‑tábla (előfok) olyan tábla, amelyben az adatokat először műszakilag helyesen tárolod (esetleg lazább típusokkal), és csak utána validálod és átvitted a céltáblába.
Előnyök az üzemeltetésben:
- A hibás rekordok nyomon követhetően megmaradnak tárolva (ideértve a nyers adatokat).
- A validálást külön és ismételhető módon végezheted el.
- Elválasztod az interfészbeérkezés kezelését a szakmai feldolgozástól.
Az Array DML gyakran a gyors út a staging‑táblába, míg az átvitel a céltáblába set‑alapú SQL‑lel (vagy tárolt eljárással) történik. Ez a hibakezelést erőteljesebben az adatbázis oldalára helyezi, ami szervezettől (DBA‑szerepek, deployment) függően ésszerű vagy nem kívánatos lehet.
Üzemeltetés és adminisztráció: Amit IT‑vezetőknek és rendszergazdáknak tudniuk kell
Monitoring: A hibaarány és az átbocsátás a kulcsmutatók
Egy bulk‑import stabil üzemeltetéséhez két metrika informatívabb, mint pusztán a „futásidő”:
- Átbocsátás: sorok percenként (vagy per batch), ideértve a csúcsértéket és a mediánt.
- Hibaarány: hibás sorok futásonként, ideálisan hibakategóriák szerint csoportosítva (Unique, FK, NOT NULL, típuskonfliktus).
Ha ezeket a két értéket rendszeresen figyeled, korán észreveheted, hogy a forrásban valami megváltozott (pl. új formátum), vagy hogy a célrendszer (pl. új megszorítások) szigorúbbá vált.
Zárolások és terhelési ablakok
Bulk‑INSERTek zárolást és IO‑terhelést okozhatnak. Ha párhuzamosan felhasználók dolgoznak ugyanazokon a táblákon, számításba kell venned az izolációs szintet, indexeket és esetleg partícionálást. Gyakorlatilag ez annyit jelent: vagy az importokat terhelési ablakba időzíted, vagy úgy építed az adatfolyamot, hogy az együttéljen a folyamatos üzemmel (pl. staging + aszinkron átvétel).
Konkrét ellenőrzőlista egy robusztus Bulk‑Inserthez Array DML‑lel
- Batch‑méret meghatározása (kezdeti érték 500–1 000) és mérhetően hangolni.
- Explicit tranzakció: alapértelmezettként batchenkénti commit; „Commit am Ende” csak tudatos választás legyen.
- Paramétertípusok stabil beállítása, ne kényszeríts implicit konverziókat.
- Hibakontextus rekordonként rögzítése (külső azonosító, forrássor).
- Hibastratégia: először batch, majd Split/Fallback, soronkénti logolás.
- Logging: kódok + szöveg, de adatvédelmi szempontból megfelelve; Batch‑ID és futás‑ID rögzítése.
- Újrafutás: idempotencia biztosítása (kulcs/Upsert/Import‑ID).
Összegzés: Az Array DML gyors – robusztussá a folyamat és a hibastratégia teszi
Egy FireDAC Bulk-Insert Array DML-lel erős eszköz, amíg nem úgy teszel, mintha hibák nem léteznének. A valós adatszálakban mindig vannak kilengések: duplikátumok, hiányzó hivatkozások, érvénytelen dátumértékek. Ezért a tiszta megközelítés: Array DML a teljesítményhez, kombinálva egy kontrollált izolációs stratégiával (Split vagy Fallback) és egy soronként nyomon követhető hibalistával. Így egyszerre kapod meg a sebességet és az üzembiztonságot – és ez az, ami számít, ha az importok nemcsak a laborban futnak, hanem minden éjjel megbízhatóan át kell menniük.
Ha egy meglévő import- vagy interfészfolyamatot a Delphi/FireDAC környezetben stabilizálni akarjátok (teljesítmény, tranzakciók, újrafuttatás, naplózás), szívesen tisztázzuk ezt strukturáltan egy technikai megbeszélésen:
Ehhez a témához fontosak a Delphi Bulk Insert és a Bulk Insert Delphi FireDAC. A bejegyzés ezeket az aspektusokat érthetően rendszerezi és bemutatja, mire kell figyelni a gyakorlatban.
Projekt vagy modernizációs terv megbeszélése Net-Base segítségével.
Következő lépés
Ha a téma valós projektté válik, az architektúrát, a meglévő rendszert és az üzemeltetést már korán együtt kell értékelni.
Nemcsak egyedi kérdésekben támogatunk, hanem akkor is, amikor forráskódrészletekből, örökölt rendszerekkel kapcsolatos témákból vagy portálötletekből robusztus vállalati projektet kell kialakítani.
- A jelenlegi állapotot, a célállapotot és a műszaki kockázatokat együttesen értékeljük.
- REST, az adathozzáférés, a portálok és a Rollout nem kerülnek utólagos teendőkként elhalasztásra.
- Már korán láthatja, melyik út gazdaságilag és üzemeltetési szempontból életképes.