Bulk Insert/Update: Performans Karşılaştırması

Büyük veri kümelerini veritabanına yazarken SqlBulkCopy, EF Core BulkExtensions, tablo değerli parametreler ve transaction yönetimi gibi yöntemler karşılaştırılır.

Bulk Insert/Update: Performans Karşılaştırması

Bulk Data Insert / Update: Milyonlarca Veriyi En Hızlı Şekilde DB'ye Yazma

Uygulamanız büyüdükçe, yüzbinlerce hatta milyonlarca satırı veritabanına eklemeniz veya güncellemeniz gerekebilir. Tek tek INSERT veya UPDATE komutları göndermek, hem ağ trafiğini şişirir hem de veritabanı sunucusunda gereksiz yük oluşturur. Bu tür büyük veri işlemleri için özel olarak tasarlanmış yöntemler kullanmak, performansı kat kat artırabilir. Bu yazıda, .NET ekosisteminde en çok kullanılan 4 toplu veri yazma yöntemini (SqlBulkCopy, EF Core Bulk Extensions, Table-Valued Parameters ve Batch INSERT) karşılaştıracak, hangi senaryoda hangisinin tercih edilmesi gerektiğini ele alacağız.


1. Karşılaştırma Tablosu: Hangi Yöntem Ne Zaman?

Yöntem Performans Esneklik Kullanım Kolaylığı Veritabanı Bağımlılığı Update Desteği
SqlBulkCopy 🟢 Çok Yüksek 🟡 Orta 🟡 Orta 🔴 Yüksek (SQL Server) ❌ Hayır (Sadece Insert)
EF Core Bulk Extensions 🟢 Yüksek 🟢 Yüksek 🟢 Yüksek 🟡 Orta (Çoklu DB) ✅ Evet
Table-Valued Parameters (TVP) 🟢 Yüksek 🟢 Yüksek 🔴 Zor 🔴 Yüksek (SQL Server) ❌ Hayır (Sadece Insert)
Batch INSERT (Multiple Rows) 🟡 Orta 🟢 Yüksek 🟢 Çok Kolay 🟢 Düşük ✅ Evet (MERGE ile)

2. Yöntem 1: SqlBulkCopy (SQL Server Native)

SqlBulkCopy, SQL Server'a yüksek hızlı toplu veri kopyalama (bulk copy) işlemi yapmak için kullanılan, .NET Framework'ün yerleşik sınıfıdır. Verileri doğrudan veritabanına "tablo dökümü" (table dump) şeklinde yazar.

Nasıl Çalışır?

  1. Bir DataTable veya IDataReader hazırlanır.

  2. SqlBulkCopy nesnesi oluşturulur, hedef tablo belirtilir.

  3. WriteToServer metodu çağrılır ve veriler toplu halde gönderilir.

csharp

using SqlBulkCopy bulkCopy = new SqlBulkCopy(connectionString);
bulkCopy.DestinationTableName = "Products";
bulkCopy.BatchSize = 10000; // Her seferinde 10.000 satır gönder

// DataTable veya IDataReader ile verileri hazırla
DataTable table = new DataTable();
// ... tabloyu doldur ...

bulkCopy.WriteToServer(table);

Artıları:

  • En Yüksek Performans: Milyonlarca satırı saniyeler içinde yazabilir.

  • Transaction Desteği: SqlBulkCopy bir transaction içinde çalıştırılabilir.

Eksileri:

  • Sadece SQL Server: SQL Server dışındaki veritabanlarında çalışmaz.

  • Update Desteği Yok: Sadece INSERT işlemi yapar. Mevcut verileri güncellemek için geçici bir tabloya yazıp MERGE ile birleştirme gibi ek işlemler gerekir.

  • Veri Dönüşümü Zor: Kaynak ve hedef sütunlar arasında esnek eşleme yapmak zordur.


3. Yöntem 2: EF Core Bulk Extensions (Z.EntityFramework.Extensions / EFCore.BulkExtensions)

Entity Framework Core'un doğasında toplu işlem desteği yoktur. Ancak, popüler üçüncü parti kütüphaneler bu açığı kapatır. EFCore.BulkExtensions (ücretsiz, açık kaynak) ve Z.EntityFramework.Extensions (ticari) en bilinen iki seçenektir.

Nasıl Çalışır?
EF Core context'i üzerinden çağrılan BulkInsert, BulkUpdate, BulkInsertOrUpdate gibi extension metotlar kullanılır. Kütüphane, verileri SQL Server, PostgreSQL, MySQL gibi farklı veritabanlarına uygun toplu komutlara (ör. SQL Server için SqlBulkCopy, PostgreSQL için COPY) dönüştürür.

csharp

// EFCore.BulkExtensions (Ücretsiz)
using EFCore.BulkExtensions;

context.BulkInsert(products); // Toplu Insert
context.BulkUpdate(products); // Toplu Update
context.BulkInsertOrUpdate(products); // Insert veya Update (MERGE)

// Z.EntityFramework.Extensions (Ticari, daha fazla özellik)
context.BulkSaveChanges(); // Tüm değişiklikleri toplu olarak kaydet

Artıları:

  • Kullanım Kolaylığı: EF Core ile doğal entegrasyon. LINQ sorgularıyla çalışır.

  • Çoklu Veritabanı Desteği: SQL Server, PostgreSQL, MySQL, SQLite gibi birçok veritabanını destekler.

  • Update Desteği: BulkUpdate ve BulkInsertOrUpdate (MERGE) ile mevcut verileri güncelleyebilirsiniz.

  • Transaction Desteği: EF Core transaction'ları ile entegre çalışır.

Eksileri:

  • Performans: SqlBulkCopy kadar hızlı değildir (özellikle Z.EntityFramework.Extensions ticari sürümü daha optimize olsa da).

  • Maliyet: Z.EntityFramework.Extensions ücretlidir. EFCore.BulkExtensions ücretsizdir ancak daha az özellik sunar.

  • Veri Dönüşümü: Entity modelleri ile çalıştığı için, veri dönüşümleri otomatiktir ancak esneklik sınırlıdır.


4. Yöntem 3: Table-Valued Parameters (TVP) - Tablo Değerli Parametreler

TVP, SQL Server'a tablo şeklinde parametre göndermenizi sağlayan bir özelliktir. Bir stored procedure veya sorguya, özel bir tablo tipi (User-Defined Table Type) kullanarak birden çok satırı tek bir parametre olarak iletebilirsiniz.

Nasıl Çalışır?

  1. Veritabanında bir tablo tipi (UDTT) oluşturulur.

  2. Bu tablo tipini parametre olarak alan bir stored procedure yazılır.

  3. .NET tarafında, bir DataTable bu tablo tipiyle eşleşecek şekilde hazırlanır.

  4. SqlParameter ile bu DataTable parametre olarak gönderilir.

sql

-- Veritabanında tablo tipi oluştur
CREATE TYPE dbo.ProductType AS TABLE (
    Id INT,
    Name NVARCHAR(100),
    Price DECIMAL(18,2)
);

-- Stored procedure
CREATE PROCEDURE dbo.InsertProducts
    @Products dbo.ProductType READONLY
AS
BEGIN
    INSERT INTO Products (Id, Name, Price)
    SELECT Id, Name, Price FROM @Products;
END

csharp

// .NET tarafı
DataTable table = new DataTable();
table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Price", typeof(decimal));
// ... tabloyu doldur ...

using SqlCommand cmd = new SqlCommand("dbo.InsertProducts", connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("@Products", SqlDbType.Structured)
{
    TypeName = "dbo.ProductType",
    Value = table
});
cmd.ExecuteNonQuery();

Artıları:

  • Yüksek Performans: SqlBulkCopy'e yakın performans.

  • Esneklik: Stored procedure içinde karmaşık iş mantığı (validasyon, loglama, transaction) uygulanabilir.

  • Update Desteği: Stored procedure içinde MERGE kullanarak update de yapılabilir.

Eksileri:

  • Karmaşıklık: Stored procedure ve tablo tipi yönetimi ek iş yükü getirir.

  • Veritabanı Bağımlılığı: Sadece SQL Server'da çalışır.

  • DataTable Zorunluluğu: .NET tarafında DataTable veya DbDataReader kullanmayı gerektirir (modern IEnumerable ile doğrudan çalışmaz).


5. Yöntem 4: Batch INSERT (Tek Sorguda Çoklu Satır)

Bu yöntem, tek bir INSERT sorgusu içinde birden çok satırı aynı anda göndermektir. Tüm veritabanları tarafından desteklenir.

sql

INSERT INTO Products (Id, Name, Price) VALUES
(1, 'Laptop', 1500),
(2, 'Mouse', 25),
(3, 'Keyboard', 45);

csharp

// .NET'te sorguyu oluşturma (örnek, SQL Injection riskine karşı dikkatli olun!)
var values = products.Select(p => $"({p.Id}, '{p.Name}', {p.Price})");
var sql = $"INSERT INTO Products (Id, Name, Price) VALUES {string.Join(",", values)}";

Artıları:

  • Çok Basit: Anlaşılması ve uygulaması kolaydır.

  • Evrensel: Tüm veritabanlarında çalışır.

  • Transaction Desteği: Kolayca bir transaction içine alınabilir.

Eksileri:

  • Performans Sınırı: Çok büyük veri kümelerinde sorgu boyutu (query size) artar ve veritabanı sınırlamalarına takılabilir (ör. SQL Server'da max 2100 parametre).

  • SQL Injection Riski: Dinamik sorgu oluştururken dikkatli olunmalıdır (parametreli sorgu kullanmak daha güvenlidir).

  • Update Desteği: Sadece INSERT için doğaldır. UPDATE için MERGE veya birden çok UPDATE sorgusu gerekir.


6. Yöntem Karşılaştırması: Hızlı Seçim Rehberi

İhtiyacınız Önerilen Yöntem
En yüksek performans, sadece INSERT, SQL Server SqlBulkCopy
EF Core ile doğal entegrasyon, INSERT+UPDATE, çoklu DB EF Core Bulk Extensions (EFCore.BulkExtensions)
Karmaşık iş mantığı, stored procedure ile kontrol, SQL Server Table-Valued Parameters (TVP)
Basit, orta ölçekli veri, tüm DB'lerde çalışsın Batch INSERT (Multiple Rows)

7. Performans İpuçları ve Dikkat Edilmesi Gerekenler

  1. Transaction Yönetimi: Toplu işlemlerde, tüm veriler ya başarılı olmalı ya da hiçbiri olmamalıdır. Tüm yöntemler transaction ile kullanılabilir. Ancak, çok büyük transaction'lar log dosyalarını şişirebilir. İşlemi parçalara (batch) bölmek (ör. 10.000'er kayıt) genellikle daha iyidir.

  2. Batch Size (Toplu Boyut): Çok küçük batch'ler performansı düşürür, çok büyük batch'ler bellek ve ağ sorunlarına yol açar. Genellikle 5000 - 10000 arası iyi bir başlangıç noktasıdır.

  3. İndeksler ve Kısıtlamalar: Toplu işlemler sırasında indeksler güncellenir, bu da performansı yavaşlatır. Mümkünse, toplu işlem öncesi indeksleri devre dışı bırakıp (disable) sonra yeniden oluşturmak (rebuild) daha hızlı olabilir.

  4. Veritabanı Log Yönetimi: SqlBulkCopy ve TVP, minimal loglama (minimal logging) yapabilir, bu da performansı artırır. Ancak, bu durum veritabanı kurtarma modeline (recovery model) bağlıdır.

  5. Veri Doğrulama (Validation): Toplu işlem öncesi verileri uygulama tarafında doğrulamak, veritabanına hatalı veri göndermeyi engeller. Ancak, bu doğrulama işlemi de performansı etkileyebilir.

Sonuç:

Milyonlarca veriyi veritabanına yazmak, doğru aracı seçmekle başlar. SqlBulkCopy, saf performans gerektiğinde tartışmasız liderdir. EF Core Bulk Extensions, EF Core kullanıyorsanız ve update ihtiyacınız varsa en pratik çözümdür. Table-Valued Parameters, stored procedure'lerle çalışmayı sevenler için güçlü bir alternatiftir. Batch INSERT ise basit ve orta ölçekli işlemler için yeterlidir. Hangi yöntemi seçerseniz seçin, batch size, transaction yönetimi ve indeks stratejileri gibi faktörleri göz önünde bulundurmayı unutmayın.

Tüm yazılar

İlgili Yazılar