都說Dapper性能好,突然就遇到個坑,還是個性能問題

来源:https://www.cnblogs.com/s0611163/archive/2022/08/12/16578447.html
-Advertisement-
Play Games

本來閑來無事,準備看看Dapper擴展的源碼學習學習其中的編程思想,同時整理一下自己代碼的單元測試,為以後的進一步改進打下基礎。 突然就發現問題了,源碼也不看了,開始改代碼,改了好久。 測試Dapper.LiteSql數據批量插入的時候,耗時20秒,感覺不正常,於是我測試了非Dapper版的Lite ...


本來閑來無事,準備看看Dapper擴展的源碼學習學習其中的編程思想,同時整理一下自己代碼的單元測試,為以後的進一步改進打下基礎。
突然就發現問題了,源碼也不看了,開始改代碼,改了好久。

測試Dapper.LiteSql數據批量插入的時候,耗時20秒,感覺不正常,於是我測試了非Dapper版的LiteSql的批量插入,僅需100毫秒,速度差了200倍。
同樣的資料庫、同樣的Npgsql.dll、同樣的測試代碼,產生的SQL和參數集合也是一樣的,最後不得不懷疑Dapper。
引用Dapper的源碼,修改調試之後,我決定提個PR。我之前沒想過提PR,我想我也不是為了提PR而提PR,我也不想費時費力。

沒想到提PR的過程很不順利,原來提的PR需要單元測試全部通過才行。
我本機沒環境不方便測,不過我發現提交申請之後,GitHub上立馬自動編譯測試,能看到哪些單元測試對了,哪些錯了,真強大,真方便,原來大工程是這樣的。

先是提交了一行代碼,認為沒有問題,結果被打了個叉。仔細一看才發現,原來是單元測試不通過。
改了又改,有幾個存儲過程相關的單元測試總是不通過。
都快要放棄了,後來想到是不是我定義的cleanNames變數隨著DynamicParameters類的創建,又清空了,但cleanNames又不能定義成全局的。後來我加了幾行代碼。
一共提交了12次,單元測試終於全部通過,共增加了11行代碼。
PR是提交了,是否被採納就不知道了。也許代碼寫的比較挫,也許審核人員不認可我對Dapper的這種用法,是我用錯了,沒有修改的必要。

修改DynamicParameters.cs文件

變數定義:

private readonly HashSet<string> cleanNames = new HashSet<string>();

單元測試始終無法全部通過,我添加了下麵幾行代碼,終於成功了。原來cleanNames被重新new了之後,command.Parameters里可能是有值的,它的作用域更大一些。

foreach (IDbDataParameter param in command.Parameters)
{
    if (!cleanNames.Contains(param.ParameterName))
    {
        cleanNames.Add(param.ParameterName);
    }
}

關鍵的修改就一行
原代碼(參數很多時性能不行,參數什麼情況下會很多呢?就是通過一條SQL批量Insert時會有很多參數,我是500條數據插入一批,參數根據表欄位多少可能有幾千個):

bool add = !command.Parameters.Contains(name);

當command.Parameters中有幾千個參數的時候,性能就慘不忍睹了。
集合查找的時間複雜度是O(N)。(原來我寫的是O(N/2),現更正為O(N))

修改為(HashSet性能很高):

bool add = !cleanNames.Contains(name);

通過HashSet查找,時間複雜度是O(1)。

往cleanNames中添加欄位名:

if (add)
{
    command.Parameters.Add(p);
    cleanNames.Add(name);
}

總結

  1. 關於List集合的Contains方法

    當你使用Contains方法的時候,你要考慮這個集合有沒有可能突然變的數據量很大?如果是在迴圈中頻繁調用,並且List的數據量比較大,它的性能就比較差,建議使用HashSet或Dictionary來判查找。
    但是HashSet、Dictionary和List轉來轉去也有代價,IDbCommand介面的Parameters屬性的類型是IDataParameterCollection,它是一個集合,並沒有HashSet或Dictionary類型的屬性,又必須要轉換才能得到。

提的issue和PR

issue:https://github.com/DapperLib/Dapper/issues/1817
PR:https://github.com/DapperLib/Dapper/pull/1816

LiteSql源碼

LiteSql源於DBHelper,裡面的介面是做過實際項目的,主要是ERP、CRM系統。
簡單支持了Lambda表達式、增加了SqlString之後,使用上似乎變複雜了一點,不過原來的使用方式依然支持。
也許這裡面的介面和設計思想,體現的是我上家公司的前輩們的技術水準。比如實體類用partial修飾分成兩個文件,可能有利有弊吧。自動生成的Model類是不建議修改的,否則資料庫變動的時候你還怎麼自動生成?不把你的改動衝掉了?
一個ORM有它的設計思想和理念,比如DapperExtensions就不建議對實體類加特性,而是通過獨立的映射類來處理表、欄位別名,優缺點我還不清楚。
LiteSql的後續改進,還沒有新的指導思想,所以一直都是小改,基本沒怎麼動。

https://gitee.com/s0611163/Dapper.LiteSql

https://gitee.com/s0611163/LiteSql
即使是大名鼎鼎的Dapper我依然不放心,所以保留了ADO.NET的版本。

最後

性能測試,我測試了MySQL和PostgreSQL的批量插入,奇怪的是,MySQL是沒有該性能問題的,PostgreSQL有性能問題,可是跑的是一樣的源碼,我想問題出在command.Parameters.Contains,MySql.Data.dll(版本6.9.12)中做了優化沒有問題,Npgsql.dll(4.0.13版本、4.1.12版本(大版本4的最後一個小版本)和5.0.14版本(大版本5的最後一個小版本))中沒有做優化存在性能問題,不過Npgsql.dll(6.0.6版本(最新版本))是做了優化的沒有性能問題。

這不是Dapper的問題,是Npgsql.dll 5.0.14 以及之前版本的問題,最後,我關閉了提了issue和PR。

最後發現不是Dapper的問題,也是尷尬。我之前也給NLog提過issue,測到最後發現NLog是真的沒有問題,是我自己配置的問題,尷尬。這都能水一篇博客,是我大意了,大家輕噴。

不過以後遇到這種情況,我怎麼知道我調用的Contains方法有沒有經過優化呢?每個都測一遍?


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 我國目前並未出台專門針對網路爬蟲技術的法律規範,但在司法實踐中,相關判決已屢見不鮮,K 哥特設了“K哥爬蟲普法”專欄,本欄目通過對真實案例的分析,旨在提高廣大爬蟲工程師的法律意識,知曉如何合法合規利用爬蟲技術,警鐘長鳴,做一個守法、護法、有原則的技術人員。 案情介紹 深圳市快鴿互聯網科技有限公司 2 ...
  • 一、應用場景 大家在使用Mybatis進行開發的時候,經常會遇到一種情況:按照月份month將數據放在不同的表裡面,查詢數據的時候需要跟不同的月份month去查詢不同的表。 但是我們都知道,Mybatis是ORM持久層框架,即:實體關係映射,實體Object與資料庫表之間是存在一一對應的映射關係。比 ...
  • 背景 在 CI/CD 流程當中,測試是 CI 中很重要的部分。跟開發人員關係最大的就是單元測試,單元測試編寫完成之後,我們可以使用 IDE 或者 dot cover 等工具獲得單元測試對於業務代碼的覆蓋率。不過我們需要一個獨立的 CLI 工具,這樣我們才能夠在 Jenkins 的 CI 流程集成。 ...
  • 一:背景 上一篇我們聊到瞭如何去找 熱點函數,這一篇我們來看下當你的程式出現了 非托管記憶體泄漏 時如何去尋找可疑的代碼源頭,其實思路很簡單,就是在 HeapAlloc 或者 VirtualAlloc 時做 Hook 攔截,記錄它的調用棧以及分配的記憶體量, PerfView 會將這個 分配量 做成一個 ...
  • .NET 項目預設情況下 日誌是使用的 ILogger 介面,預設提供一下四種日誌記錄程式: 控制台 調試 EventSource EventLog 這四種記錄程式都是預設包含在 .NET 運行時庫中。關於這四種記錄程式的詳細介紹可以直接查看微軟的官方文檔 https://docs.microsof ...
  • 航天和軍工領域的數字化轉型和建設正在積極推進,在與航天二院、航天三院、航天六院、航天九院、無線電廠、兵工廠等單位交流的過程中,用戶更聚焦試驗和生產過程中的痛點,迫切需要解決軟體平臺統一監測和控制設備及軟體與設備協同的問題。 ...
  • 前兩天微軟偷偷更新了Visual Studio 2022 正式版版本 17.3 發佈,發佈摘要: MAUI 工作負荷 GA 生成 MAUI/Blazor CSS 熱重載支持 現在,你將能夠使用我們的新增功能在 Visual Studio 中使用每個更新試用一系列新功能。 選擇每個功能以瞭解有關特定功 ...
  • 需求如下,在DEV框架項目中,需要在表格中增加一列顯示圖片,並且能編輯該列圖片,然後進行保存等操作,最終效果如下 這裡使用的是PictureEdit控制項來實現,打開DEV GridControl設計器,在ColumnEdit選擇PictureEdit: 綁定圖片代碼如下: DataTable dtO ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...