go-mssqldb ile toplu işlemler

go-mssqldb sürücüsü, mssql.CopyIn işlevini kullanarak yüksek performanslı toplu ekleme işlemlerini destekler. Toplu ekleme, normal satır satır işleyen INSERT yolunu atlayarak TDS toplu kopyalama protokolünü kullanıp verileri doğrudan sunucuya aktarır.

Toplu kopya, TVP veya JSON seçin

SQL Server'a birden fazla satır veya karmaşık yük göndermeniz gerektiğinde aşağıdaki rehberi kullanın:

Seçin... En uygun olduğu zaman Tradeoff
Toplu kopya ile mssql.CopyIn Tek bir hedef tabloya birçok satırı en hızlı şekilde yüklemek için en hızlı yönteme ihtiyacınız var. En iyi işleme hızını sunar, ancak saklı yordam arabirimleri veya karma yapılı yükler yerine tablo yüklemelerini hedefler.
Tablo değerli bir parametre Güçlü tipli bir satır setini depolanmış bir prosedüre veya parametreli komuta aktarmanız gerekir. Şema ve prosedür sınırlarını korur, ancak kullanıcı tarafından tanımlanan bir tablo tipi ve eşleştirme alan sırası gerektirir.
JSON ile OPENJSON veya FOR JSON Uygulamanız zaten JSON değiştiriyor ya da yük şekli iç içe ya da esnek hale geliyor. Uygulama kodu için daha taşınabilirdir, ancak yapılandırılmış ekleme işlemlerinde genellikle TVP'lere veya toplu kopyalamaya göre daha yavaştır ve tür güvenliği daha düşüktür.

Büyük veri gruplarını bir hazırlama veya hedef tablosuna yüklüyorsanız, toplu kopyalamayla başlayın. Eğer yapılandırılmış satır kümeleriyle depolanmış prosedürleri çağırıyorsanız, TVP'lerle başlayın. İç içe belgeler veya gevşek şemalara ihtiyacınız varsa, JSON ile başlayın.

Bu makaledeki örnekler AdventureWorks2025 örnek veritabanıyla karşılaştırılıyor. Toplu kopyalama örnekleri HumanResources.Department ve Production.ProductCategory hedefler.

Temel toplu ekleme

Toplu kopya ifadesi oluşturmak için kullanın mssql.CopyIn , ardından satır göndermek için kullanın Exec :

import (
    "database/sql"
    "log"

    "github.com/microsoft/go-mssqldb"
)

func bulkInsert(db *sql.DB) error {
    txn, err := db.Begin()
    if err != nil {
        return err
    }
    defer txn.Rollback()

    stmt, err := txn.Prepare(mssql.CopyIn("HumanResources.Department", mssql.BulkOptions{},
        "Name", "GroupName"))
    if err != nil {
        return err
    }

    // Add rows
    _, err = stmt.Exec("Data Science", "Research and Development")
    if err != nil {
        return err
    }
    _, err = stmt.Exec("Cloud Ops", "Information Technology")
    if err != nil {
        return err
    }
    _, err = stmt.Exec("Developer Relations", "Sales and Marketing")
    if err != nil {
        return err
    }

    // Flush and finalize the bulk copy
    result, err := stmt.Exec()
    if err != nil {
        return err
    }

    if err = stmt.Close(); err != nil {
        return err
    }

    rowsAffected, _ := result.RowsAffected()
    log.Printf("Bulk inserted %d rows\n", rowsAffected)

    return txn.Commit()
}

Argümansız son stmt.Exec() çağrı, kalan satırları temizler ve toplu kopyalama işlemini tamamlar.

Toplu Seçenekler

Yapı, mssql.BulkOptions toplu kopyalama davranışını yapılandırır:

Veri Alanı Türü Description
CheckConstraints bool Toplu ekleme sırasında kısıtlamaları denetleyin.
FireTriggers bool Hedef tabloda INSERT tetikleyicileri tetikleyin.
KeepNulls bool Varsayılan değerleri eklemek yerine null değerleri koruyun.
KilobytesPerBatch int Parti başına kilobaytlar. 0 sunucu varsayılan özelliğini kullanır.
RowsPerBatch int Toplu işlem başına satır sayısı. 0 sunucu varsayılan özelliğini kullanır.
Order []string ORDER hedef kümelenmiş indeks için ipucu (örneğin, []string{"Id ASC"}).
Tablock bool Toplu kopya süresince tablo seviyesinde bir kilit edinin.

Opsiyonlu örnek

Kısıtlama denetimlerini, tetikleyicileri ve kilitlemeyi denetlemek için BulkOptions geçirin:

stmt, err := txn.Prepare(mssql.CopyIn("HumanResources.Department",
    mssql.BulkOptions{
        CheckConstraints: true,
        FireTriggers:     true,
        Tablock:          true,
        RowsPerBatch:     1000,
    },
    "Name", "GroupName"))

Hata yönetimi

Herhangi bir satır başarısız olursa, toplu kopyalama işlemi tamamen başarısız olur. Hem her satırın Exec çağrısından hem de son Exec flush işleminden kaynaklanan hataları kontrol edin:

for _, emp := range employees {
    _, err = stmt.Exec(emp.Name, emp.GroupName)
    if err != nil {
        txn.Rollback()
        return err
    }
}

// Final flush
_, err = stmt.Exec()
if err != nil {
    txn.Rollback()
    return err
}

Başarısız satırları tespit ve kaydetme

Toplu kopyalama işlemi başarısız olduğunda, SQL Server'dan gelen hata mesajı kısıtlama veya veri sorununu gösterir ancak belirli satırı belirtmez. Başarısız satırları belirlemek için bir toplu işlem yaklaşımı kullanın:

func bulkInsertWithRowTracking(db *sql.DB, departments []Department) error {
    txn, err := db.Begin()
    if err != nil {
        return err
    }

    stmt, err := txn.Prepare(mssql.CopyIn("HumanResources.Department",
        mssql.BulkOptions{RowsPerBatch: 500}, "Name", "GroupName"))
    if err != nil {
        txn.Rollback()
        return err
    }

    for i, dept := range departments {
        _, err = stmt.Exec(dept.Name, dept.GroupName)
        if err != nil {
            txn.Rollback()
            log.Printf("Bulk copy failed at row %d (Name=%q): %v", i, dept.Name, err)
            return fmt.Errorf("bulk copy failed at row %d: %w", i, err)
        }
    }

    _, err = stmt.Exec()
    if err != nil {
        txn.Rollback()
        return fmt.Errorf("bulk copy flush failed: %w", err)
    }

    if err = stmt.Close(); err != nil {
        txn.Rollback()
        return err
    }

    return txn.Commit()
}

İpucu

Kötü satırları atlayıp devam etmeniz gerekiyorsa, tek tek INSERT komutlarını veya bir hazırlama tablosu yaklaşımını kullanın: verileri kısıtlamalar olmadan bir hazırlama tablosuna toplu olarak kopyalayın, ardından verileri hata işleme ile hedef tabloya taşımak için bir MERGE ya da INSERT...SELECT kullanın.

CSV dosyalarından akış

Büyük CSV dosyaları için, tüm dosyayı belleğe yüklemeden satırları doğrudan dosyadan toplu kopyalama işlemine aktarın:

import (
    "encoding/csv"
    "io"
    "os"
)

func bulkInsertFromCSV(db *sql.DB, filePath string) error {
    f, err := os.Open(filePath)
    if err != nil {
        return err
    }
    defer f.Close()

    reader := csv.NewReader(f)

    // Skip the header row.
    _, err = reader.Read()
    if err != nil {
        return err
    }

    txn, err := db.Begin()
    if err != nil {
        return err
    }

    stmt, err := txn.Prepare(mssql.CopyIn("HumanResources.Department",
        mssql.BulkOptions{Tablock: true, RowsPerBatch: 5000},
        "Name", "GroupName"))
    if err != nil {
        txn.Rollback()
        return err
    }

    var rowCount int
    for {
        record, err := reader.Read()
        if err == io.EOF {
            break
        }
        if err != nil {
            txn.Rollback()
            return fmt.Errorf("CSV read error at row %d: %w", rowCount+1, err)
        }

        _, err = stmt.Exec(record[0], record[1])
        if err != nil {
            txn.Rollback()
            return fmt.Errorf("row %d: %w", rowCount+1, err)
        }
        rowCount++
    }

    // Flush remaining rows.
    result, err := stmt.Exec()
    if err != nil {
        txn.Rollback()
        return err
    }
    if err = stmt.Close(); err != nil {
        txn.Rollback()
        return err
    }

    affected, _ := result.RowsAffected()
    log.Printf("Bulk inserted %d rows from CSV", affected)

    return txn.Commit()
}

Performans karşılaştırması

Toplu kopyalama, büyük veri yükleri için bireysel eklemelerden önemli ölçüde daha hızlıdır. Aşağıdaki tablo, 100.000 satır eklemek için yaklaşık performans özelliklerini göstermektedir:

Method Göreli hız Ağ gidiş dönüşleri Kilitleme
Bireysel INSERT En yavaş (1x) 100,000 Her ekleme için satır düzeyi.
Toplu INSERT (her bir ifade için 1.000 satır) Orta (5-10x) 100 Her parti için sıra seviyesi.
TABLOCK olmaksızın toplu kopyalama Hızlı (20-50x) Parti büyüklüğüne bağlı Satır düzeyinde toplu işlem.
TABLOCK ile toplu kopyalama En hızlı (50-100x) Parti büyüklüğüne bağlı Tablo düzeyinde kilit, en az günlükleme.

Note

Gerçek performans, ağ gecikmesi, sunucu yapılandırması, tablo indeksleri ve minimum kayıt girişinin mevcut olup olmadığına bağlı olarak değişir. Size özel iş yükünüzle testing.B kullanarak kıyaslama yapın. Bkz. Performans ayarlama.

Sütun sıralaması ve tür eşlemesi

Sütunlar, mssql.CopyIn hedef tablonun beklediği sıraya ve türlere uymalıdır. Sürücü sütun isim eşleştirmesi yapmaz; konumsal eşleme kullanır.

Yaygın veri tipi sorunları

Go tipi SQL Server sütunu Issue Çözüm
string varchar Örtük nvarchar dönüşüm. mssql.VarChar sarmalayıcısını kullanın.
float64 decimal(18,4) Hassasiyet kaybı. Şöyle geçin: string.
time.Time datetime2 Saat dilimi dönüşümü. UTC zamanlarını kullanın.
nil Herhangi bir nullable sütun gerektirir KeepNulls: true. KeepNulls içinde BulkOptions ayarlayın.

Açık tiplerle ilgili bir örnek

Varsayılan tip eşlemesi şemanızla eşleşmediğinde sütun tiplerini açıkça belirtin:

stmt, err := txn.Prepare(mssql.CopyIn("Production.ProductCategory",
    mssql.BulkOptions{KeepNulls: true},
    "Name"))
if err != nil {
    return err
}

for _, p := range categories {
    _, err = stmt.Exec(p.Name)
    if err != nil {
        return err
    }
}

Performans ipuçları

  • Boş tablolara büyük eklemeler için Tablock kullanın. Bu seçenek, kilit çatışmasını azaltır ve minimum kayıt kaydını sağlar.
  • Set RowsPerBatch sürücünün veri gönderme sıklığını kontrol etmek için. Daha büyük toplu işlemler, gidiş gelişleri azaltır ancak daha fazla bellek tüketir.
  • Ağ yükünü azaltmak için bağlantı dizesindeki değeri packet size artırın (en fazla 32767).
  • Verileri hedef tablonun kümelenmiş indeksine uyduracak şekilde sıralayın ve seçeneği Order ayarlayın. Bu yaklaşım, sunucu tarafında sıralamayı önler.
  • Büyük toplu yüklerden önce kümelenmiş olmayan indeksleri bırakın, sonra yeniden oluşturun. Toplu ekleme sırasında indeks bakımı ek yük oluşturur.
  • Güvenilir veriler için toplu kopyalama sırasında kısıtlama denetimini atlamak üzere CheckConstraints: false (varsayılan) seçeneğini kullanın.

Sınırlamalar

  • Toplu kopyalama, Always Encrypted ile korunan sütunları desteklemez. Daha fazla bilgi için bkz . Sınırlamalar.
  • TDS seviyesinde toplu kopyalama Azure SQL Veritabanı'de desteklenmiyor. Azure SQL Veritabanı için bunun yerine toplu INSERT deyimleri veya hazırlama tablosu desenini kullanın.