| 限制 | 影響 | 生產環境真實做法 |
| 單欄位 NVARCHAR(50) | 無真實業務模型 (通常 10-50 欄位、多型別) | 真實寬表、大欄位、LOB |
| 禁用預存程序 / TVP | 切斷最高效路徑 (SqlBulkCopy + TVP / BULK INSERT) | **首選** SqlBulkCopy + DataTable/IDataReader / TVP |
| 禁用最小日誌 / 簡單模式 | 強制完整日誌記錄,I/O 爆增 | 大量載入**必開**簡單/大量記錄模式 + TABLOCK |
| 2GB RAM / 舊 CPU | 記憶體壓力大,緩存命中率低 | 現代伺服器 64GB+ RAM、NVMe SSD |
正確的「極速導入」標準姿勢 (生產級)
1. SqlBulkCopy + IDataReader (記憶體友善、流式處理)
public class CsvDataReader : IDataReader
{
private readonly StreamReader _reader;
private string[] _current;
// 實作 IDataReader 介面... (逐行讀取 CSV yield return)
}
using (var conn = new SqlConnection(cs))
using (var bulk = new SqlBulkCopy(conn))
{
bulk.DestinationTableName = "TableB";
bulk.BatchSize = 100000; // 批次大小
bulk.BulkCopyTimeout = 0; // 無限等待
bulk.EnableStreaming = true; // 大物件流式
bulk.SqlRowsCopied += (s,e) => Console.WriteLine($"已寫入 {e.RowsCopied:N0} 列");
conn.Open();
// 關鍵:最小日誌記錄需滿足條件
// 1. 資料庫處於 Simple/Bulk-Logged 模式
// 2. 目標表無觸發器、無 CDC、無複寫
// 3. 使用 TABLOCK (SqlBulkCopyOptions.TableLock)
bulk.BulkCopyOptions = SqlBulkCopyOptions.TableLock | SqlBulkCopyOptions.CheckConstraints;
using (var reader = new CsvDataReader(@"D:\Data\100wan.csv"))
bulk.WriteToServer(reader);
}
2. 資料庫端必備設定 (事半功倍)
-- 1. 切換復原模式 (大量載入期間)
ALTER DATABASE TestDB SET RECOVERY SIMPLE; -- 或 BULK_LOGGED
GO
-- 2. 目標表:無索引、無約束、無觸發器 (載入後再建)
-- 若有叢集索引,資料檔**必須依叢集鍵排序**,否則分頁分裂殺效能
-- 3. 檔案群組預增長 (避免自動增長卡頓)
ALTER DATABASE TestDB MODIFY FILE (NAME = TestDB_Data, SIZE = 2GB, FILEGROWTH = 512MB);
ALTER DATABASE TestDB MODIFY FILE (NAME = TestDB_Log, SIZE = 1GB, FILEGROWTH = 256MB);
GO
3. 硬體層面:TempDB 放 SSD、資料/記錄分離、啟用瞬間檔案初始化
- 授予 SQL 服務帳號
執行量維護工作 (SeManageVolumePrivilege) → 瞬間檔案初始化,資料檔擴容零填充。
TempDB 多資料檔 (核心數/8) 放最快 NVMe。
實測基準 (參考值,現代硬體)
| 方案 | 100 萬筆 (NVARCHAR(50)) | 1000 萬筆 (寬表 20 欄位) | 關鍵點 |
| 單執行緒 SqlBulkCopy + TableLock | ~1.5 - 3 秒 | ~15 - 30 秒 | 基準線 |
| 多執行緒分區並行 (Partitioned Table + 多 SqlBulkCopy) | ~0.8 - 1.5 秒 | ~8 - 15 秒 | CPU 密集型 |
| BULK INSERT / bcp (本地檔案) | ~1 - 2 秒 | ~10 - 20 秒 | 繞過網路層 |
**結論**:在挑戰賽規則 (單執行緒、完整日誌、舊硬體) 下 4 秒極難;在**生產級最佳實踐**下,100 萬筆**遠低於 1 秒**是常態。
給挑戰者的建議
- 用
SqlBulkCopy + TableLock + 自訂 IDataReader (避免 DataTable 佔記憶體)。
- 預熱:先跑一次小批次讓 JIT 編譯、連線池建立、頁面載入緩存。
- 關閉防毒軟體、關閉非必要服務、設定電源計畫「高效能」。
- 測試前
DBCC FREEPROCCACHE; DBCC DROPCLEANBUFFERS; (僅測試環境)。
- **別為了挑戰而挑戰**——掌握
SqlBulkCopy 最佳實踐,才是解決真實 ETL/同步/遷移問題的鑰匙。
- 上一篇文章: 不挑戰極限:VFP 導入 1000 萬筆資料僅需 5 秒 — 重新定義「資料庫效能」
- 下一篇文章: SQL Server 分頁存儲過程效能大比拚:ROW_NUMBER() vs 傳統 NOT IN — 50 萬筆實測
Copyright ©
程式碼之家 All Rights Reserved