首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 記憶體列表查詢優化實戰:從 LINQ 到 PLINQ 再到受控並行

     編輯:關於C語言

前言

在 ASP.NET MVC 專案中,若需頻繁對約 6.5 萬筆藥品資料進行名稱與拼音碼的下拉式模糊查詢,將資料載入記憶體是提升回應速度的常見策略。本文記錄一次真實的效能調優歷程,展示如何從基礎 LINQ 出發,逐步引入預處理、並行查詢(PLINQ),最終以 Parallel.For 搭配並行度控制,在高併發環境下穩定服務的完整過程。

資料實體定義

public class DrugInfo
{
    public int DrugNameId { get; set; }
    public string DrugName { get; set; }
    public string DrugSearchCode { get; set; }
    // 優化後新增的小寫快取欄位
    public string LowerDrugName { get; set; }
    public string LowerDrugSearchCode { get; set; }
}

第一階段:原始 LINQ 查詢

最直觀的寫法是在查詢時即時轉小寫比對:

var sw = Stopwatch.StartNew();
key = key.ToLower();
var result = cacheList
    .Where(m => m.DrugName.ToLower().Contains(key) 
             || m.DrugSearchCode.ToLower().Contains(key))
    .ToList();
sw.Stop();
Console.WriteLine($"耗時:{sw.Elapsed.TotalMilliseconds:F2} ms");

實測平均約 35 ms。每次查詢都要對 6.5 萬筆資料執行兩次 ToLower() 並建立新字串,CPU 開銷明顯。

第二階段:預先轉小寫,以空間換時間

在資料載入記憶體時,一次性計算並儲存小寫版本:

// 資料初始化時
foreach (var item in cacheList)
{
    item.LowerDrugName = item.DrugName.ToLower();
    item.LowerDrugSearchCode = item.DrugSearchCode.ToLower();
}

// 查詢時
var sw = Stopwatch.StartNew();
key = key.ToLower();
var result = cacheList
    .Where(m => m.LowerDrugName.Contains(key) 
             || m.LowerDrugSearchCode.Contains(key))
    .ToList();
sw.Stop();

實測平均降至 16 ms,效能提升超過 50%。雖然每筆資料多佔兩個字串欄位,但在 6.5 萬筆規模下記憶體增加極小,極具性價比。

第三階段:引入 PLINQ 並行查詢

.NET 4.0 引入的 PLINQ 可自動利用多核心:

var result = cacheList
    .AsParallel()
    .Where(m => m.LowerDrugName.Contains(key) 
             || m.LowerDrugSearchCode.Contains(key))
    .ToList();

單一請求平均約 10 ms。看似完美,但部署至 IIS 後發現嚴重問題:單一請求即佔用大量執行緒,200 併發下應用程式集區直接假死。

第四階段:受控並行 — Parallel.For + MaxDegreeOfParallelism

PLINQ 的 WithDegreeOfParallelism 會**強制**分配指定數量執行緒,且不動態調整;而 Parallel.For 搭配 ParallelOptions.MaxDegreeOfParallelism 則是**上限**,TPL 會視執行緒池負載彈性調度,極適合多使用者共存的 Web 環境。

var bag = new ConcurrentBag<DrugInfo>();
Parallel.For(0, cacheList.Count,
    new ParallelOptions { MaxDegreeOfParallelism = 4 },
    i =>
{
    var item = cacheList[i];
    if (item.LowerDrugName.Contains(key) 
     || item.LowerDrugSearchCode.Contains(key))
    {
        bag.Add(item);
    }
});
var result = bag.ToList(); // 若需排序再補 OrderBy
  • 耗時維持約 10 ms,與 PLINQ 相當。
  • 200 併發壓測通過,應用程式集區不再假死。
  • 結果順序不固定,但結果集通常較小,二次排序成本可忽略。

核心差異總結

特性PLINQ WithDegreeOfParallelismParallel.For MaxDegreeOfParallelism
執行緒分配啟動時**固定分配**指定數量**上限控制**,TPL 視負載動態調度
適用場景獨佔運算資源、級聯查詢需同步Web 多使用者、共享執行緒池
風險高併發易耗盡執行緒池低,自動避免過度佔用

結語

優化並非單純追求單請求極致速度,在生產環境中「可控的並行」遠比「無限制的並行」更有價值。本案例中,預先小寫化奠定基礎,Parallel.For + MaxDegreeOfParallelism 兼顧吞吐與穩定,最終以極小代價解決了生產事故。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved