都說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
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...