| 查詢條件 | 筆數 | DataReader | DataSet | 差距倍數 |
| id <= 1 | 1 | 0 | 0 | - |
| id <= 10 | 10 | 1 | 1 | 1x |
| id <= 100 | 100 | 2 | 3 | 1.5x |
| id <= 1,000 | 1,000 | 5 | 6 | 1.2x |
| id <= 10,000 | 10,000 | 8 | 50 | 6.25x |
關鍵發現:DataSet 填充階段非線性增長
- 小資料量 (<1,000 筆):**差異可忽略**
- 中大資料量 (>10,000 筆):**DataSet 顯著劣於 DataReader**
- 原因:DataSet 需建立 DataTable、DataColumn、DataRow 物件樹、維護關聯與條件約束、計算索引
原始碼層面深度剖析 (Reflector 反編譯)
SqlDataAdapter.Fill() 核心流程
protected virtual int Fill(DataSet dataSet, string srcTable, IDataReader dataReader, ...)
{
// 參數驗證...
var container = DataReaderContainer.Create(dataReader, ...);
// 關鍵:委託給 FillFromReader
return this.FillFromReader(dataSet, null, srcTable, container, ...);
}
發現:SqlDataAdapter.Fill 內部依賴 IDataReader (SqlDataReader) 讀取資料,再將每行資料轉換為 DataRow 加入 DataTable。DataSet 效能較差的根因在於資料轉換與物件建構開銷,而非讀取本身。
SqlCommand.ExecuteReader() → TDS 協議處理
internal bool Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, ...)
{
// TDS 封包解析迴圈
byte token = stateObj.ReadByte();
switch (token)
{
case 0xA4: // TABLE_NAME token
dataStream.TableNames = this.ProcessTableName(...); break;
case 0xA5: // COL_METADATA token
dataStream.SetMetaData(this.ProcessColInfo(...)); break;
case 0xD1: // ROW token
this.ProcessRow(stateObj._cleanupMetaData, ..., stateObj); break;
case 0xFD/0xFE/0xFF: // DONE tokens
this.ProcessDone(...); break;
}
}
SqlDataReader 直接解析 TDS (Tabular Data Stream) 協議封包,逐行產生結果,零中間物件分配。
修正結論:比較對象應為「填充策略」而非「讀取器」
原標題比較有誤導性:
- SqlDataReader:資料讀取器 (茶杯)
- DataSet:資料容器模型 (茶壺 + 茶杯 + 茶盤)
- SqlDataAdapter:填充器 (倒茶動作)
真實比較應為:SqlDataReader 直接讀取 vs SqlDataAdapter.Fill 透過 DataReader 填充 DataSet。後者額外負擔:DataTable 建構、DataRow 建立、條件約束驗證、索引維護、事件觸發。
實務選型決策矩陣
| 場景 | 推薦 | 理由 |
| 唯讀、順向、大量資料、報表/匯出 | SqlDataReader | 最低記憶體、最高吞吐 |
| 需隨機存取、關聯查詢、離線操作、資料繫結 UI | DataSet | 豐富物件模型支援 |
| 單筆/少量資料、簡單 CRUD | 任一均可 | 差異可忽略,優先開發效率 |
| 高併發 Web API、微服務 | SqlDataReader / Dapper | 降低 GC 壓力、延遲 |
現代替代方案
- Dapper:微型 ORM,直接對應 SqlDataReader 到 POCO,效能接近原生
- Entity Framework Core:追蹤變更、LINQ 查詢,開發效率優先
- SqlBulkCopy:大量寫入專用,繞過 EF/ADO.NET 管線
工具無絕對優劣,理解底層機制、匹配業務場景、測試驗證才是架構師的核心能力。
- 上一篇文章: .NET 深度學習筆記 (2):C# 判斷空字串 4 種方法效能實測與 IL 深度剖析
- 下一篇文章: Emit 實戰教學 (三):AOP 框架核心——配置檔驅動的切面解析與動態植入引擎
Copyright ©
程式碼之家 All Rights Reserved