Dergi konusundan proje pratiğine
İçeriğe Uygun Hizmet ve Teknik Sayfalar
Bir BDE-yerel entegrasyonla ikame Bulk-Insert ile Array DML, genellikle çok sayıda kaydı bir veritabanına aktarmanın en hızlı yoludur: binlerce tekil Insert yerine bir parametre dizisi bağlanır ve tek seferde sunucuya gönderilir. Pratikte sorun kısa sürede ortaya çıkar: bir kayıt bir Unique-Index ihlal eder, bir NOT NULL alanı boştur, bir Foreign Key uymuyordur — ve bir anda hangi satırın batch’i durdurduğu, bir bölümün zaten yazılıp yazılmadığı ve veri tutarsızlığı yaratmadan nasıl düzgün devam edeceğin belirsizleşir.
Tam da bununla ilgileniyoruz: Array DML’yi nasıl kullanırsın ki satır başına güvenilir hata bilgisi alırsın, işlemi kontrol altında tutarsın ve işletmede ne olduğunu izleyebilirsin. Odak akademik API okuması değil; gerçek importlarda düzenli olarak karşılaşılan uç durum: büyük bir batch, birkaç bozuk satır, ama yine de hız istiyorsun.
FireDAC Bulk-Insert ile Array DML: Array DML’nin Bulk-Insert’te neden avantaj sağladığı
Array DML (Data Manipulation Language) FireDAC’de şunu ifade eder: Parametreleri tek değer olarak değil, bir dizi (array) halinde bağlarsın. FireDAC sonra (sürücü/DB’ye bağlı olarak) daha az roundtrip yapar, sunucu tarafında daha verimli çalışabilir ve istemcideki overhead’i ciddi şekilde azaltır. Bu üç durumda özellikle önemlidir:
- ETL- und Importstrecken: CSV/XML/JSON alınır, normalizasyon/eşleme yapılır, ardından staging veya hedef tabloya yazılır.
- Schnittstellen-Puffer: REST veya MQ payload’ları toplanır ve periyodik olarak kalıcı hale getirilir.
- Protokoll-/Event-Tabellen: gecikmenin baskın olduğu birçok küçük Insert.
Avantajı bedava gelmez. Array DML ile karmaşıklığı „çok sayıda tekil ifade“‚den „çok satırlı tek bir ifade“‚ye kaydırırsın. Bu performans için iyidir, ancak hata tanılama, işlem (transaction) mantığı ve yeniden çalıştırma açısından daha zorludur.
Tipik uç durum: Bir Batch, bir bozuk satır
İşletmedeki klasik senaryo: 50.000 satır import ediyorsun. Her satır için bir roundtrip istemediğin için ArraySize olarak 1.000 seçiyorsun. Batch 17 başarısız oluyor. Veritabanı sadece „duplicate key“ veya „violates foreign key constraint“ bildiriyor. UI’da veya servis log’unda genellikle sadece „ExecSQL failed“ yazar.
Temiz bir hata yönetimi yoksa genelde iki kötü sonuç olur:
- Tüm batch’i atarsın, oysa 1.000 satırdan 999’u uygun olabilirdi.
- Tek tek Insert’lere geri dönersin ve performans avantajını kalıcı olarak kaybedersin.
Amaç üçüncü bir yol: Batch performansını korumak, ancak hata-özel (satır indeksi, anahtar değerler, DB hata metni) olarak protokollemek ve isteğe bağlı olarak „good rows“ları commit etmek — bunun ne kadar kritik olduğu, tutarlılık ve idempotentlik (aynı işlemin tekrar çalıştırılmasının çoğaltıcı etkisi olmaması) sürecinizde ne kadar önemli olduğuna bağlıdır.
FireDAC Array DML: İlgili ayar parametreleri (mitler olmadan)
Array DML ile yapılan Bulk-Insertlerde pratikte her zaman aynı kritik ayarlar belirleyicidir:
1) ArraySize und Batch-Größe
ArraySize (TFDQuery/TFDCommand’de) bir çağrıda kaç „satır“ FireDAC işlendiğini belirler. Daha büyük olması otomatik olarak daha iyi değildir. Çok büyük olması şu anlama gelir: istemcide daha fazla bellek, ağ üzerinde daha fazla yük, sunucuda daha büyük kilitler/log yükü ve hata durumunda daha geniş bir etki alanı. Sağlam importlar için sütun sayısına, BLOB’lara ve gecikmeye bağlı olarak genellikle 200 ile 2.000 arasında bir Batch boyutu iyi bir başlangıçtır.
2) Transaktionsgrenze
Net bir karar vermeniz gerekiyor: Commit pro Batch mi yoksa Commit für den gesamten Import mı? Bu bir zevk meselesi değil, işletme kararıdır:
- Commit pro Batch: kilitleri ve transaction logunu sınırlar, yeniden başlatmayı kolaylaştırır, ancak ara durumlar görünür (izolasyon seviyesine bağlı olarak). Batch 17’deki bir hata Batch 1–16’nın sistemde kalmasına neden olur.
- Commit am Ende: „Her şey ya da hiç“, işlem bazında daha tutarlı, ancak büyük hacimlerde uzun kilitler, büyük rollback riski ve hata durumunda her şeyin kaybolması riski vardır.
Birçok arayüz ve import süreci için „Commit pro Batch“ daha gerçekçi bir işletme stratejisidir — ancak sadece idempotentlik ve çoğaltma (dubletten) stratejisini temiz bir şekilde düzenlediyseniz (ör. doğal anahtarlar, upsert’ler veya bir Import-ID ile).
3) UpdateOptions und Prepared Statements
Tekrarlanan batch’lerde ifadenin hazırlanmış (prepared) bırakılması genellikle faydalıdır. „Prepare“ şu demektir: FireDAC veritabanının ifadeyi parse/derleyip yeniden kullanmasına olanak verir. Veritabanına bağlı olarak bu özellikle yüksek frekansta belirgin bir etki sağlayabilir. Burada önemli olan küçük numaralar değil, aynı Query nesnesinin (veya aynı TFDCommand’un) tutarlı şekilde yeniden kullanılması ve sabit parametre tipleridir.
Satır başına temiz hata işleme: Gerçekten neye ihtiyacınız var
Eğer „satır bazında“ hata ele almak istiyorsanız, üç şeye ihtiyacınız var:
- İlişkilendirme: Hangi dizi indeksi (0..N-1) başarısız oldu?
- Bağlam: Bu satır hangi işe özgü anahtar değerlere sahip (örn. dış ID, müşteri numarası, zaman damgası)?
- Kontrol: Sonrasında ne yapacaksınız? İşlemi durdurmak, sadece hatalı satırları atlamak yoksa batch’i bölmek?
FireDAC sürücüye bağlı olarak her dizi öğesi için hata döndürebilir. Pratikte bu her zaman «her zaman mevcut» değildir. Bazı veritabanları/sağlayıcılar sadece ilk hatayı raporlayabilir veya batch içindeki bir hata geri kalanın hiç çalıştırılmamasına neden olabilir. Tam da bu yüzden sağlam bir desen genellikle iki aşamalıdır:
- Aşama A: Batch’i Array DML olarak deneyin.
- Aşama B: Batch başarısız olursa, onu bölün (yarıya ayırın) veya kontrollü şekilde tek satırlara geri dönün — ancak sadece bu batch için — ve düzgünce loglayın.
Bu daha fazla iş gibi görünebilir, ama import yollarında bu, „saat 02:00’de her şey durur“ ile „Import devam eder, 7 satır hata listesine düşer“ arasındaki farktır.
Praktik bir desen: Önce Batch, sonra hedefli izolasyon
Aşağıdaki model, veri kalitesinin karışık olduğu süreçe yakın yazılım çözümlerinde etkili olduğunu kanıtlamıştır:
Adım 1: Verileri Batch yapısına koyun (hata bağlamı dahil)
İçe aktarılacak verileri yalnızca ham değerler olarak değil, asgari bağlamla saklayın: dış ID, kaynaktaki satır numarası, gerekirse Hash/Checksumme. Bu „Nice to have“ değil: hata durumunda CSV’yi yeniden parse edip neyin bozuk olduğunu bulmak istemezsiniz.
Adım 2: Array DML çalıştırma
ArraySize’ı batch uzunluğuna ayarlarsınız, parametreleri diziler olarak bağlarsınız ve ExecSQL’i çalıştırırsınız. Önemli: Parametre tiplerini sabit tutun (ör. sayısal alanlar için bazen String, bazen Integer bağlamayın), aksi halde DB implizit cast’ler üretir veya FireDAC her bir öğe için dönüştürmek zorunda kalır.
Adım 3: Hata durumu – batch’i daraltın, kör tekrar etmeyin
ExecSQL başarısız olursa, iki sağlam seçeneğiniz var:
- Binary Split (ikiye bölme): Batch’i iki yarıya ayırın, her yarıyı yeniden Array DML olarak deneyin. Bunu, tek tek inceleyebileceğiniz küçük bir miktara ulaşana kadar tekrarlayın. Avantaj: Sadece birkaç satır bozuksa performansın büyük kısmını korursunuz. Dezavantaj: Daha fazla mantık gerektirir ve sistematik hatalarda (ör. yanlış veri türü) çok işe yaramaz.
- Fallback auf Einzelzeilen bu batch için: ArraySize=1 olarak ayarlarsınız (veya tekil değerleri bağlarsınız) ve satır satır çalıştırırsınız, hataları loglarsınız ve devam edersiniz. Avantaj: Basit, satır başına garanti. Dezavantaj: Bu batch’te hız kaybedersiniz.
Uygulamada ikisini birleştiririm: Önce 1–2 kez bölme (iyi blokları hızlıca geçirmek için), ardından küçük kalanlarda tek satırlara geçerim, böylece net hata bilgilerini kaydedebilirim.
Hata nesneleri ve mesajlar: FireDAC’den neleri çıkarmanız gerektiği
FireDAC veritabanı hatalarını Exceptions içinde (genellikle EFDDBEngineException) ayrıntılı bilgilerle kapsar. İşletme açısından üç seviye önemlidir:
- DB hata kodu (DB’ye özgü): örn. SQLSTATE PostgreSQL’de, Error Number SQL Server’da.
- Constraint-/Nesne adı: genellikle hata metninde yer alır (Unique-Index, FK-Constraint).
- Statement bağlamı: tablo, işlem, gerekirse parametre değerleri (kişisel veriler konusunda dikkatli olun).
Eğer satır satır loglamak istiyorsanız, hata durumunda ayrıca satırı tanımlamanız gerekir. FireDAC bazı durumlarda Array-Index sağlayabilir. Ancak buna yalnızca güvenmeyin. Her zaman ek olarak kendi indeksinizi (batch içindeki pozisyon) oluşturun ve bu pozisyona en az bir iş anahtarı kaydedin.
Gerçek importlarda zaman kaybettiren tuzaklar
1) „Sadece bir satırdı“ – ama Transaktion zaten „dirty“
Veritabanına ve sürücüye bağlı olarak bir hata, tüm statement yürütmesinin başarısız sayılmasına ve transaksiyonun ya açıkça rollback edilmesi gereken ya da sonraki statement’ların başarısız olduğu bir duruma girmesine neden olabilir. Özellikle bazı sürücülerde „hata sonrası basitçe devam et“ güvenli bir varsayım değildir.
Sonuç: Eğer bir transaksiyon içinde çalışıyorsanız ve bir batch başarısız olursa, standart yol: mevcut batch bağlamının rollback’i (veya tüm transaksiyonun) ve sonra yeniden başlamak. Bu „Commit pro Batch“ yaklaşımına iyi uyar.
2) Autocommit vs. explizite Transaktion
Açık bir transaksiyon başlatmazsanız, ifadeleri nasıl commit edeceğine genellikle sürücü/provider karar verir. Büyük importlar için bu nadiren istediğiniz şeydir. Açık (explizite) transaksiyonlar size şu konularda kontrol sağlar:
- Kilitleme süresi
- Rollback davranışı
- Yeniden başlama noktaları
Ve: Açık olmak „devasa bir işlem“ demek değildir. „Bilinçli“ demektir.
3) Tetikleyiciler, Kısıtlamalar ve Yan Etkiler
Array DML gönderimi hızlandırır, ancak otomatik olarak sunucu tarafı işi hızlandırmaz. Hedef tabloda tetikleyiciler varsa (örn. denetim kaydı, otomatik durum hesaplaması), darboğaz muhtemelen INSERT değil tetikleyici kodudur. Bu durumda bir batch daha az roundtrip yapsa bile, DB sunucusundaki CPU hâlâ sınırlayıcı faktör olur.
Yöneticiler ve teknik liderler için: Performans problemlerinde Wait Events/Locks ve işlem günlüğüne bakmak faydalıdır. Toplu INSERT o zaman sadece tetikleyicidir, neden değildir.
4) Veri tipleri ve örtük dönüştürmeler
En sık karşılaşılan „Neden bu yavaş?“ nedenlerinden biri: Parametreler string olarak bağlanır, DB her satır için Integer/Date/Decimal’e cast eder. Bu görünmez ama maliyetlidir. Kararlı performans için:
- Parametre veri tiplerini uygun şekilde ayarlayın (tarih için tarih, sayı için sayı).
- Ondalıklarda locale tuzaklarına dikkat edin (virgül vs. nokta). FireDAC genellikle burada doğrudur, ancak karışık kaynaklar öyle değildir.
- Zaman dilimleri/UTC stratejisini önceden belirleyin (timestamp’ler importlarda klasik bir sorundur).
5) Hata metinleri insanlar içindir, otomasyon için değil
Hata metnini parse etmek cazip gelebilir („duplicate key value violates unique constraint …“). Bunu yalnızca son seçenek olarak yapın. Daha iyi olan yapısal kodlardır (SQLSTATE, Error Number). Ne yazık ki tüm sürücüler aynı şekilde bilgi sağlamaz. Bu nedenle her ikisini planlayın: Kod ve metin, artı isteğe bağlı „kısıtlama adını metinden alma“, ancak sert bir bağımlılık olmadan.
Hata ayıklama ipuçları: Kırık satırı hızlıca nasıl bulursunuz
Batch’i yeniden üretilebilir kılmak
Bir import aralıklı olarak başarısız oluyorsa, yeniden üretilebilirlik gerekir. Her batch için küçük bir teşhis dosyası veya bir log girdisi saklayın; içermelidir:
- Batch numarası ve zaman
- ArraySize ve işlem modu
- Batch’teki iş mantığı anahtarlarının listesi (örn. harici ID’ler)
Bu genellikle, sonradan sadece bu ID’ler için hedefe yönelik küçük bir import başlatmak için yeterlidir.
Nihai SQL’i görünür kılmak (ancak veri sızıntısı olmadan)
Hata ayıklamada bilmek istersiniz: SQL doğru mu? Parametreler doğru mu? FireDAC FDMoni bileşenleri ve sürücü loglaması üzerinden izleme/tracing sağlar. Üretime yakın ortamlarda önemli olan:
- Tracing’i hedefli ve geçici olarak etkinleştirin (performans ve veri gizliliği).
- Parametre değerlerini yalnızca güvenli bir ortamda veya maskelenmiş olarak kaydedin.
- Kişisel veriler söz konusuysa: loglarda yalnızca teknik anahtarlar (ID’ler) olsun, düz metin içerik bulunmasın.
Bölerek test yapıyorsanız: Durma kriterlerini tanımlayın
İkili bölmede sonsuza dek bölmek istemezsiniz. Bir alt sınır belirleyin, örn. „20 satırın altındaysa tekli moda geç“. Ayrıca importu durdurmadan önce toplam kaç hataya tolerans göstereceğinizi sınırlandırın (örn. sistematik eşleme sorunlarında). Aksi halde sonsuz hata listelerine girer ve sonraki işlemlerin tıkanmasına yol açarsınız.
Ne zaman çaba gerçekten değer (ve ne zaman değil)
Satır başına hata işleme ile Array DML özellikle şu durumlarda faydalıdır:
- Birçok satır işlendiğinde (binlerce ila milyonlarca).
- Sadece az sayıda satır hatalıysa, fakat yine de işlem akışının devam etmesini istiyorsunuz.
- Importun işletmede kararlı çalışması gerekiyorsa (örn. gece işleme, UI olmadan servis).
- Hata listesini ilgili birime/kaynağa geri göndermeniz gerekiyorsa (satır referanslı).
Aşağıdaki durumlarda daha az uygundur:
- sadece birkaç düzine satır yazıyorsan (tek tek insertler uygundur),
- veri kalitesi öyle kötüyse ki satırların %30–50’si başarısız oluyorsa (bu durumda bir staging stratejisi daha akıllıca olur),
- zaten DB-yerel bir toplu yükleme yöntemi kullanıyorsan (z. B. COPY in PostgreSQL, BCP/BULK INSERT in SQL Server) – bu durumda Array DML uygun araç değildir.
Alternatif mimari: „Doğrudan hedef“ yerine Staging tablosu
Düzenli olarak karışık veri kalitesiyle uğraşıyorsan, salt „veriyi doğrudan hedef tabloya insert etmek“ genellikle yanlış karardır. Bir Staging-Tablosu (ön aşama), veriyi önce teknik olarak doğru şekilde sakladığın (gerekirse gevşek tiplerle) bir tablodur; doğrulamayı yaptıktan sonra veriyi hedef tabloya aktarırsın.
İşletmede faydaları:
- Hatalı kayıtlar izlenebilir şekilde saklanır (ham veriler dahil).
- Doğrulamayı ayrı ve tekrarlanabilir şekilde çalıştırabilirsin.
- Arayüz kabulünü iş mantığından ayırırsın.
Array DML genellikle Staging-tablosuna hızlı yol olur; hedef tabloya aktarım ise set-temelli SQL (veya Stored Procedure) ile yapılır. Bu, hata işlemlerini daha çok DB tarafına kaydırır; bu durum organizasyona göre (DBA rolleri, Deployment) uygun veya istenmeyen olabilir.
İşletme ve yönetim: IT-Leads ve yöneticilerin bilmesi gerekenler
Monitoring: Hata oranı ve verim temel metriklerdir
Bir toplu importun stabil işletimi için „çalışma süresi“ tek başına yerine iki metrik daha anlamlıdır:
- Verim: satır/dakika (veya batch başına), zirve/medyan dahil.
- Hata oranı: çalışmadaki hatalı satırlar, ideal olarak hata sınıflarına göre gruplanmış (UNIQUE, FK, NOT NULL, tip uyuşmazlığı).
Bu iki değeri düzenli olarak izlersen, kaynağın değişip değişmediğini (ör. yeni bir format) ya da hedef sistemin (ör. yeni kısıtlar) daha katılaşıp katılaşmadığını erken fark edersin.
Kilitlenmeler ve yük pencereleri
Bulk-Inserts kilitlenme ve IO yükü oluşturabilir. Aynı tablolar üzerinde paralel kullanıcılar çalışıyorsa, izolasyon seviyesi, indeksler ve gerekiyorsa partitionlama üzerine düşünmelisin. Pratikte bu şu anlama gelir: importları yük pencerelerine almak ya da veri akışını çalışır sistemle birlikte koordine edecek şekilde kurmak (ör. staging + asenkron devralma).
Array DML ile sağlam bir Bulk-Insert için somut kontrol listesi
- Batch boyutu belirle (başlangıç değeri 500–1.000) ve ölçülebilir şekilde ayarla.
- Açık bir işlem: Varsayılan olarak batch başına Commit; „en sonunda Commit“ seçeneğini yalnızca bilinçli olarak kullan.
- Parametre tiplerini sabit tut, örtük dönüşümleri zorlamaktan kaçın.
- Her kayıt için hata bağlamı tut (harici ID, kaynak satır).
- Hata stratejisi: Önce batch, sonra ayırma/fallback, satır bazında loglama.
- Loglama: Kodlar + metin, ancak veri koruma uyumlu; Batch-ID ve Çalıştırma-ID kaydet.
- Yeniden çalıştırma: Idempotensi sağla (anahtar/Upsert/Import-ID).
Sonuç: Array DML hızlıdır – sağlamlığı süreç ve hata stratejisi sağlar
Bir FireDAC Bulk-Insert ile Array DML, hata yokmuş gibi davranmadığın sürece güçlü bir araçtır. Gerçek veri akışlarında her zaman sapmalar olur: çift kayıtlar, eksik referanslar, bozuk tarih değerleri. Bu nedenle doğru yaklaşım şudur: Performans için Array DML, kontrollü bir izolasyon stratejisi (Split veya Fallback) ile birleştirilmiş ve satır bazında izlenebilir bir hata listesi. Böylece hız ve işletme güvenliğini bir arada elde edersin — ve tam da bu önem kazanır; importlar sadece laboratuvarda değil, her gece güvenilir şekilde çalışmak zorunda olduklarında.
Eğer mevcut bir import veya arayüz sürecini Delphi/FireDAC içinde stabilize etmek istiyorsanız (performans, işlemler, yeniden başlatma, logging), bunu teknik bir görüşmede yapılandırılmış şekilde memnuniyetle ele alırız:
Bu konu için Delphi Bulk Insert ve Bulk Insert Delphi FireDAC da önemlidir. Yazı bu yönleri anlaşılır şekilde sınıflandırıyor ve günlük kullanımda nelere dikkat edilmesi gerektiğini gösteriyor.
Sonraki adım
Konu gerçek bir projeye dönüştüğünde, mimari, mevcut sistemler ve işletme erken dönemde birlikte değerlendirilmelidir.
Bireysel sorularda destek vermekle kalmıyoruz; kaynak kodu parçacıklarından, legacy konularından veya portal fikirlerinden sağlam bir kurumsal projeye dönüşene kadar da destek veriyoruz.
- Mevcut durum, hedef durum ve teknik riskler birlikte değerlendirilir.
- REST, veri erişimi, portallar ve Rollout daha sonra ortaya çıkan sonuçlar olarak ertelenmez.
- Hangi yolun ekonomik ve işletme açısından sürdürülebilir olduğunu erken görürsünüz.