都說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
  • Github / Gitee QQ群(1群) : 813100564 / QQ群(2群) : 579033769 視頻教學 介紹 MiniWord .NET Word模板引擎,藉由Word模板和數據簡單、快速生成文件。 Getting Started 安裝 nuget link : https:// ...
  • Array.Sort Array類中相當實用的我認為是Sort方法,相比起冗長的冒泡排序,它的出現讓排序更加的簡化 結果如下: 還可以聲明一個靜態方法用來專門調用指定數組排序,從名為 array 的一維數組中 a 索引處開始,到 b 元素 從小到大排序。 註意: a + b 不能大於 array 的 ...
  • 前言 在上一篇文章CLR類型系統概述里提到,當運行時掛起時, 垃圾回收會執行堆棧遍歷器(stack walker)去拿到堆棧上值類型的大小和堆棧根。這裡我們來翻譯BotR里一篇專門介紹Stackwalking的文章,希望能加深理解。 順便說一句,StackWalker在中文里似乎還沒有統一的翻譯,J ...
  • 使用過 nginx 的小伙伴應該都知道,這個中間件是可以設置跨域的,作為今天的主角,同樣的 反向代理中間件的 YARP 毫無意外也支持了跨域請求設置。 有些小伙伴可能會問了,怎樣才算是跨域呢? 在 HTML 中,一些標簽,例如 img、a 等,還有我們非常熟悉的 Ajax,都是可以指向非本站的資源的 ...
  • 什麼是Git Git 是一個開源的分散式版本控制系統,用於敏捷高效地處理任何或小或大的項目。 Git 是 Linus Torvalds 為了幫助管理 Linux 內核開發而開發的一個開放源碼的版本控制軟體。 Git 與常用的版本控制工具 CVS, Subversion 等不同,它採用了分散式版本庫的 ...
  • 首先CR3是什麼,CR3是一個寄存器,該寄存器內保存有頁目錄表物理地址(PDBR地址),其實CR3內部存放的就是頁目錄表的記憶體基地址,運用CR3切換可實現對特定進程記憶體地址的強制讀寫操作,此類讀寫屬於有痕讀寫,多數驅動保護都會將這個地址改為無效,此時CR3讀寫就失效了,當然如果能找到CR3的正確地址... ...
  • 說明 onlyoffice為一款開源的office線上編輯組件,提供word/excel/ppt編輯保存操作 以下操作均基於centos8系統,officeonly鏡像版本7.1.2.23 鏡像下載地址:https://yunpan.360.cn/surl_y87CKKcPdY4 (提取碼:1f92 ...
  • 二叉樹查找指定的節點 前序查找的思路 1.先判斷當前節點的no是否等於要查找的 2.如果是相等,則返回當前節點 3.如果不等,則判斷當前節點的左子節點是否為空,如果不為空,則遞歸前序查找 4.如果左遞歸前序查找,找到節點,則返回,否繼續判斷,當前的節點的右子節點是否為空,如果不為空,則繼續向右遞歸前 ...
  • ##Invalid bound statement (not found)出現原因和解決方法 ###前言: 想必各位小伙伴在碼路上經常會碰到奇奇怪怪的事情,比如出現Invalid bound statement (not found),那今天我就來分析以下出現此問題的原因。 其實出現這個問題實質就是 ...
  • ###一、背景知識 爬蟲的本質就是一個socket客戶端與服務端的通信過程,如果我們有多個url待爬取,只用一個線程且採用串列的方式執行,那隻能等待爬取一個結束後才能繼續下一個,效率會非常低。 需要強調的是:對於單線程下串列N個任務,並不完全等同於低效,如果這N個任務都是純計算的任務,那麼該線程對c ...