性能優化你必須知道的那些事兒

来源:http://www.cnblogs.com/shouce/archive/2016/06/02/5551769.html
-Advertisement-
Play Games

最近有客戶反饋系統導入EXECL進行數據處理超時了,我當時的第一反應,不可能啊我明明是做過性能優化的啊,怎麼還會超時呢,這是要有多少條數據才可能發生啊!於是找客戶要來了EXECL,發現有7500多條數據,備份完客戶資料庫進行代碼調試找出性能差的地方。都是一些平時老生常談的東西,可是又是很容易忽略的地 ...


       最近有客戶反饋系統導入EXECL進行數據處理超時了,我當時的第一反應,不可能啊我明明是做過性能優化的啊,怎麼還會超時呢,這是要有多少條數據才可能發生啊!於是找客戶要來了EXECL,發現有7500多條數據,備份完客戶資料庫進行代碼調試找出性能差的地方。都是一些平時老生常談的東西,可是又是很容易忽略的地方,這裡面就只談兩個點,使用String還是StringBuilder,校驗數據正確性是在迴圈裡面一條一條的使用SQL取數呢,還是一次性取出來在代碼裡面進行校驗!下麵將用實際數據結合圖表,給出準確的答案。

閱讀目錄

回到頂部

String和StringBuilder性能差異比較

   String和StringBuilder的差別這裡就不提了,學習和工作中常常會聽到拼接字元串要使用StringBuilder對象速度很快,但是可能你只是知道這個知識,實際開發工作中有關註過這一點嗎?我也是當客戶反饋之後自己跟蹤用實際效果才學會這個知識,後續開發中也會銘記這一點!下麵的實際數據或許能說明些問題。

      分別調用了這個函數,   迴圈次數為 1,5,15,200,500,1500,2500,5500,8500,20000  後面數據可以下載最後的DEMO實驗一下,String在這時已經是慢到不行了。為了保證數據的準確性,這裡每個量級的數據都取了十次值,然後求出平均值。

複製代碼
 /// <summary>
        /// 對比String和StringBuilder拼接字元串的速度
        /// 每種量級測試,取十次時間平均值
        /// </summary>
        /// <param name="Total">迴圈次數</param>
        public static void StringSpeedComparer(int Total){
            List<string> list = new List<string>();
            for (int i = 0; i < Total; i++)
            {
                list.Add(Guid.NewGuid().ToString());
            }

            int iTest = 10;
            //總執行時間 ms
            double TotalMilliseconds = 0;


            //String拼接
            string strGUID = String.Empty;
            while (iTest > 0)
            {
                DateTime dtBegin = DateTime.Now;
                foreach (string temp in list)
                {
                    strGUID = strGUID + temp + ";";
                }
                DateTime dtEnd = DateTime.Now;
                TotalMilliseconds += (dtEnd - dtBegin).TotalMilliseconds;
                iTest--;
            }
            Console.WriteLine("String拼接{0}個字元串耗時{1}ms", Total, TotalMilliseconds / 10);

            //StringBuilder拼接
            StringBuilder sb = new StringBuilder();
            iTest = 10;
            TotalMilliseconds = 0;
            while (iTest > 0)
            {
                DateTime dtBegin = DateTime.Now;
                foreach (string temp in list)
                {
                    sb.AppendFormat("{0};", temp);
                }
                DateTime dtEnd = DateTime.Now;
                TotalMilliseconds += (dtEnd - dtBegin).TotalMilliseconds;
                iTest--;
            }
            Console.WriteLine("StringBuilder拼接{0}個字元串耗時{1}ms", Total, TotalMilliseconds / 10);
        }
複製代碼

執行結果如下圖:

  

 繪製成曲線圖:

   

   從上圖可直觀看出來,String拼接是呈幾何形遞增的,而StringBuilder呈線性的,遞增趨勢很慢。在迴圈次數多的情況下使用哪種拼接,相信大家都清楚了吧!在7500的數量時,可以節省整整4s的時間,性能是不是提升很多呢?

回到頂部

迴圈取數還是一次性取數?

  背景:EXECL中有7500行學生信息數據,要把這些數據導入到學生表(p_Student)裡面,但是要保證學生編號(StudentNo)唯一,不唯一導入的時候需要給出提示信息。這就需要在後臺代碼裡面讀取EXECL裡面的學生信息然後校驗學生編碼在資料庫中是否存在,當然EXECL中填寫的學生編號也要校驗唯一。下麵就來模擬這個過程,以兩種方式比較性能。、

  首先創建學生信息表,插入7500條數據,下麵是SQL腳本,學生編號這裡插入的是newid,實際情況不會是這樣的,這裡只是會了保證唯一,但是又是無序的,儘可能模擬真實情形。

複製代碼
/*---------------------------數據字典生成工具(V2.1)--------------------------------*/
GO
IF NOT EXISTS(SELECT 1 FROM sysobjects WHERE id=OBJECT_ID('[p_Student]'))
BEGIN
/*==============================================================*/
/* Table: p_Student                                              */
/*==============================================================*/
CREATE TABLE [dbo].[p_Student](
    [StudentGUID] uniqueidentifier   ,
    [Name] varchar(40)   ,
    [Major] varchar(100)   ,
    [Sex] varchar(8)   ,
    [StudentNo] varchar(100)   ,
    PRIMARY KEY(StudentGUID)
)
    

declare @CurrentUser sysname
select @CurrentUser = user_name()
execute sp_addextendedproperty 'MS_Description', '學生信息表','user', @CurrentUser, 'table', 'p_Student'
execute sp_addextendedproperty 'MS_Description',  '學生信息GUID' ,'user', @CurrentUser, 'table', 'p_Student', 'column', 'StudentGUID'
execute sp_addextendedproperty 'MS_Description',  '姓名' ,'user', @CurrentUser, 'table', 'p_Student', 'column', 'Name'
execute sp_addextendedproperty 'MS_Description',  '專業' ,'user', @CurrentUser, 'table', 'p_Student', 'column', 'Major'
execute sp_addextendedproperty 'MS_Description',  '性別' ,'user', @CurrentUser, 'table', 'p_Student', 'column', 'Sex'
execute sp_addextendedproperty 'MS_Description',  '學生編號' ,'user', @CurrentUser, 'table', 'p_Student', 'column', 'StudentNo'

END
GO
--插入7500條模擬數據
DECLARE @Count AS INT
SELECT @Count=COUNT(1) FROM p_Student
IF @Count=0
BEGIN
    DECLARE @i AS INT
    SET @i=7500
    WHILE @i>0
    BEGIN
        INSERT INTO dbo.p_Student
                ( StudentGUID ,
                  Name ,
                  Major ,
                  Sex ,
                  StudentNo
                )
        VALUES  ( NEWID() , -- StudentGUID - uniqueidentifier
                  @i , -- Name - varchar(40)
                  '軟體工程' , -- Major - varchar(100)
                  '' , -- Sex - varchar(8)
                  NEWID()  -- StudentNo - varchar(100)
                )
        SET @i=@i-1
    END
END
GO
複製代碼

      基礎信息準備好以後,進入後臺代碼

複製代碼
 /// <summary>
        /// 統計迴圈校驗和一次性校驗性能差異
        /// </summary>
        public static void Check(int Total)
        {
            //這裡模擬學生編號
            List<string> listStudetNo = new List<string>();
            for (int i = 0; i < Total; i++)
            {
                listStudetNo.Add(Guid.NewGuid().ToString());
            }
            using (SqlConnection con = new SqlConnection(SqlCon))
            {
                con.Open();
                string strSQL = "SELECT COUNT(1) FROM dbo.p_Student WHERE StudentNo='{0}'";
                SqlCommand cmd = con.CreateCommand();

                //迴圈校驗
                double TotalMilliseconds = 0;
                for (int i = 0; i < 10; i++)
                {
                    foreach (string studentNo in listStudetNo)
                    {
                        DateTime dtBegin = DateTime.Now;
                        cmd.CommandText = String.Format(strSQL, studentNo);
                        int count = (int)cmd.ExecuteScalar();
                        if (count > 0)
                        {
                            Console.WriteLine("{0}編號重覆,請重新錄入!", studentNo);
                            return;
                        }
                        DateTime dtEnd = DateTime.Now;
                        TotalMilliseconds += (dtEnd - dtBegin).TotalMilliseconds;
                    }
                }
                Console.WriteLine("迴圈校驗{0}個學生編號耗時{1}ms", Total, TotalMilliseconds / 10);

                //一次性校驗
                TotalMilliseconds = 0;
                strSQL = "SELECT TOP 1 StudentNo FROM dbo.p_Student WHERE StudentNo IN ('{0}')";
                for (int i = 0; i < 10; i++)
                {
                    DateTime dtBegin = DateTime.Now;
                    StringBuilder sb = new StringBuilder();
                    foreach (string studentNo in listStudetNo)
                    {
                        sb.AppendFormat("{0};", studentNo);
                    }
                    cmd.CommandText = String.Format(strSQL,sb.ToString().Substring(0, sb.ToString().Length - 1).Replace(";","','"));
                    string no = (string)cmd.ExecuteScalar();
                    if (!string.IsNullOrEmpty(no))
                    {
                        Console.WriteLine("{0}編號重覆,請重新錄入!", no);
                        return;
                    }
                    DateTime dtEnd = DateTime.Now;
                    TotalMilliseconds += (dtEnd - dtBegin).TotalMilliseconds;
                }
                Console.WriteLine("一次性校驗{0}個學生編號耗時{1}ms", Total, TotalMilliseconds / 10);
            }
        }
複製代碼

    從上圖可直觀看出來,迴圈校驗和一次性校驗都是線性遞增的,一次性校驗速度差不多比迴圈的快一倍左右。

回到頂部

示例下載及總結

         示例sql示例代碼DEMO

         其實性能優化不僅僅只有這麼一點,需要在日常工作中總結,這次性能優化還有一點也令我驚嘆,有一條SQL未優化之前執行需要20s左右,給表添加了索引,速度刷的一下變成0s了,最終性能問題圓滿解決了。

        性能優化思想:

        1:大量字元串拼接請採用StringBuilder

        2:千萬不要在大量迴圈裡面迴圈查SQL,考慮是否能用一次性查詢代替,或者一次性把數據查詢出來在代碼裡面進行邏輯判斷

        3:SQL執行速度慢,可以採用執行計劃看看是否表缺少索引。

      好了本篇到這裡就要結束了,如果覺得對你有益,記住點贊哦!

  

   相關閱讀:附加沒有日誌文件的資料庫方法  刪除資料庫日誌文件的方法  數據字典生成工具系列文章

如果,您認為閱讀這篇博客讓您有些收穫,不妨點擊一下右下角的推薦按鈕。
如果,您希望更容易地發現我的新博客,不妨點擊一下綠色通道的關註我

如果,想給予我更多的鼓勵,求打

因為,我的寫作熱情也離不開您的肯定支持。

感謝您的閱讀,如果您對我的博客所講述的內容有興趣,請繼續關註我的後續博客,我是焰尾迭 。


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

-Advertisement-
Play Games
更多相關文章
  • 1:W 查看系統整體負載,無法查看具體負載,比如記憶體,磁碟 23:25:20 up 13 min, 2 users, load average: 0.00, 0.01, 0.01 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT flex tty1 - 23:13 1 ...
  • 屏幕捕捉快捷鍵動作............................保存到............-快捷鍵 全屏捕捉........................桌面(.PDF文件)......Command + Shift + 3 (win+shift+3)屏幕部分畫面.......... ...
  • ApiView是.net api的介面文檔查看網站,用於解決分散式開發過程中的Api介面管理和溝通問題。 自動生成api文檔; 方便調試及第三方開發人員對接,可以應用在asp.net mvc,wcf,webservice 中使用; 代碼及原理都很簡單,方便修改和完善。 ...
  • 各級省份代碼: 各級市代碼: ...
  • 上次分享了控制器向視圖傳遞數據的4種方式,今天再來給大家講講MVC視圖中的數據如何提交到控制器。 我們可以通過以下幾種方式提交數據到控制器: 1、通過Request.Form讀取表單數據 在 控制器動作方法(Action)中,POST方法提交的表單可以使用Request.Form讀取其中的數據: < ...
  • 過濾器 過濾器(Filter)把附加邏輯註入到MVC框的請求處理,實現了交叉關註。所謂交叉關註(Cross-Cutting Concerns),是指可以用於整個應用程式,而又不適合放置在某個局部位置的功能,否則會打破關註分離模式。典型的例子有:登錄、授權、緩存等等。 使用過濾器 如果希望動作方法只能 ...
  • 一 什麼是LINQ? 二 簡單的Linq例子。(簡單形式我就不介紹了,主要以Lambda形式介紹)。 1 首先創建一個實體對象類並賦值。 public class Person { public int PId { get; set; } // 自增Id public string Name { g ...
  • 到達應用程式的每一個請求都是由控制器處理的。但要註意,不要把事務或數據存儲邏輯放到控制器中,也不要生成用戶界面。 在ASP.NET MVC框架中,控制器是含有請求處理邏輯的.NET類。其作用是封裝應用程式邏輯。也就是說,控制器要負責處理輸入請求、執行域模型上的操作,並選擇渲染給用戶的視圖。 控制器的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...