1. 程式人生 > 其它 >單資料庫,多資料庫,單例項,多例項不同情況下的資料訪問效率測試

單資料庫,多資料庫,單例項,多例項不同情況下的資料訪問效率測試

最近公司的專案準備優化一下系統的效能,希望在資料庫方面看有沒有提升的空間,目前壓力測試發現數據庫伺服器壓力還不夠大,Web伺服器壓力也不是很大的情況下,前臺頁面訪問卻很慢,看有沒有辦法充分利用資料庫伺服器的效能,於是做了一個單資料庫,多資料庫,單例項,多例項不同情況下的資料訪問效率測試。

測試環境:

  • CPU:Inter Core2 Quad,Q8300,2.50GHz;
  • 記憶體:4.00GB
  • 系統:Windows 7 32位系統
  • 資料庫系統:SqlServer 2008,有兩個例項,一個是預設例項,一個是命名例項QE2 

測試資料:

67萬真實的基金收益資料,將這個表的資料放到了3個數據庫中,詳細內容見下面的連線字串配置:

<add name ="Ins1_DB1" connectionString ="Data Source=.;Initial Catalog=TestDB;Integrated Security=True"/>
    <add name ="Ins1_DB2" connectionString ="Data Source=.;Initial Catalog=LocalDB;Integrated Security=True"/>
    <add name ="Ins2_DB" connectionString ="Data Source=.QE2;Initial Catalog=TestDB;Integrated Security=True"/>

測試內容:

首先篩選出表中所有的基金程式碼,然後統計每隻基金的最新收益率日期,對應的T-SQL程式碼如下:

declare @max_fsrq datetime
  declare @currJJDM varchar(10)
  declare @temp table (jjdm2 varchar(10))
  declare @useTime datetime
  set @useTime =GETDATE ();
  
  insert into @temp(jjdm2)
   select jjdm from [FundYield] group by jjdm order by jjdm asc
   
  while EXISTS (select jjdm2 from @temp)
  begin
    set @currJJDM=(select top 1 jjdm2 from @temp)
    select @max_fsrq = MAX(fsrq) from [FundYield] where jjdm=@currJJDM
    delete from @temp where jjdm2 =@currJJDM 
    print @max_fsrq
  end
  
print 'T-SQL Execute Times(ms):'  
 print datediff(ms,@useTime,getdate())

直接執行這個T-SQL指令碼,在資料庫表沒有索引的情況下,耗費的時間是: 

T-SQL Execute Times(ms): 58796

根據這個功能,寫了一個.net控制檯程式來測試,測試程式沒有使用任何資料訪問框架,直接使用ADO.NET,下面是多執行緒測試的程式碼,其它程式碼略:

public static void Test2(string connName1,string connName2)
        {
            System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
            watch.Start();
            string allJjdmList = "";
            string connString = getConnectionString();
            //SqlConnection conn = new SqlConnection(connString);
            //conn.Open();
            string sql = "select jjdm from [FundYield] group by jjdm order by jjdm asc";
            DataSet ds = getData(connString, sql);
            int allCount = ds.Tables[0].Rows.Count;
            int p = (int)(allCount * 0.5);
            System.Threading.Thread t1=new System.Threading.Thread (new System.Threading.ParameterizedThreadStart (tp1=>
                {
                    for (int i = 0; i < p; i++)
                    {
                        string jjdm = ds.Tables[0].Rows[i][0].ToString();
                        object result = getSclar(ConfigurationManager.ConnectionStrings[connName1].ConnectionString,
                       string.Format("select MAX(fsrq) from [FundYield] where jjdm='{0}'", jjdm));
                        if (result != DBNull.Value)
                        {
                            DateTime dt = Convert.ToDateTime(result);
                            //Console.WriteLine("Thread 2 No {0} ,jjdm[{1}] last FSRQ is:{2}", i, jjdm, dt);
                        }
                        allJjdmList = allJjdmList + "," + jjdm;
                    }
                    Console.WriteLine("Tread 1 used all time is(ms):{0}", watch.ElapsedMilliseconds);
                }
            ));
            System.Threading.Thread t2 = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(tp2 =>
            {
                for (int i = p; i < allCount; i++)
                {
                    string jjdm = ds.Tables[0].Rows[i][0].ToString();
                    //這裡不論使用default還是express,區別不大
                    object result = getSclar(ConfigurationManager.ConnectionStrings[connName2].ConnectionString,
                        string.Format("select MAX(fsrq) from [FundYield] where jjdm='{0}'", jjdm));
                    if (result != DBNull.Value)
                    {
                        DateTime dt = Convert.ToDateTime(result);
                        //Console.WriteLine("Thread 2 No {0} ,jjdm[{1}] last FSRQ is:{2}", i, jjdm, dt);
                    }
                    
                    allJjdmList = allJjdmList + "," + jjdm;
                }
                Console.WriteLine("Tread 2 used all time is(ms):{0}", watch.ElapsedMilliseconds);
            }
            ));
            t1.Start();
            t2.Start();
            t1.Join();
            t2.Join();
            Console.WriteLine("====All thread completed!========");
        }

下面是測試結果:

第一次,資料庫沒有建立索引,進行全表掃描:

------單資料庫,單執行緒測試--------- used all time is(ms):59916 ------同一例項,雙資料庫,單執行緒測試--------- used all time is(ms):59150 ------同一例項,雙資料庫,多執行緒測試--------- Tread 2 used all time is(ms):51223 Tread 1 used all time is(ms):58175 ====All thread completed!======== ------雙例項,雙資料庫,單執行緒測試--------- used all time is(ms):58230 ------雙例項,雙資料庫,多執行緒測試--------- Tread 2 used all time is(ms):52705 Tread 1 used all time is(ms):58293 ====All thread completed!========

第二次,資料庫響應的欄位建立索引,下面是測試結果:

------單資料庫,單執行緒測試--------- used all time is(ms):1721 ------同一例項,雙資料庫,單執行緒測試--------- used all time is(ms):1737 ------同一例項,雙資料庫,多執行緒測試--------- Tread 2 used all time is(ms):1684 Tread 1 used all time is(ms):1714 ====All thread completed!======== ------雙例項,雙資料庫,單執行緒測試--------- used all time is(ms):1874

------單資料庫,單執行緒測試--------- used all time is(ms):1699 ------同一例項,雙資料庫,單執行緒測試--------- used all time is(ms):1754 ------同一例項,雙資料庫,多執行緒測試--------- Tread 1 used all time is(ms):1043 Tread 2 used all time is(ms):1103 ====All thread completed!======== ------雙例項,雙資料庫,單執行緒測試--------- used all time is(ms):1838 ------雙例項,雙資料庫,多執行緒測試--------- Tread 1 used all time is(ms):1072 Tread 2 used all time is(ms):1139 ====All thread completed!========

測試結論:

綜合全表掃描訪問和有索引方式的訪問,

單執行緒訪問:

  • 在同一個資料庫例項上,雙資料庫沒有體現出優勢,甚至單資料庫稍微優勝於多資料庫;
  • 在兩個資料庫例項上,雙例項雙例項要落後於單例項單資料庫;

多執行緒訪問:

  • 雙資料庫例項稍微落後於單資料庫例項;

綜合結論,看來不論是雙資料庫還是雙例項,對比與單例項或者單資料庫,都沒有體現出優勢,看來前者的優勢不在於訪問效率,一位朋友說,資料庫例項是不同的服務,控制粒度更小,維護影響比較低。但我想,雙資料庫例項,雙資料庫,多核CPU,應該跟兩臺資料庫伺服器差不多的效能吧,怎麼沒有體現優勢呢?也許是我的測試機器僅僅有一個磁碟,這裡磁碟IO成了瓶頸。

這個測試有沒有意義,或者這個結果的原因,還請大牛們多多指教!

--------------------------------------------------------------

意外發現:

1,有人說頻繁的查詢在完全資料庫中進行效率最高,測試發現,在查詢分析器上直接執行上面的那個T-SQL指令碼,跟程式從資料庫取出資料,再加工計算查詢,效率上沒有明顯的區別,所以哪些支援“將複雜的業務邏輯寫在儲存過程中效率最高的觀點是站不住腳的!”  ,ADO.NET從資料庫來回操作資料一樣有效率,如果加上覆雜的字元函式計算和大批量的迴圈操作,儲存過程的效率不一定高。

2,在使用程式進行頻繁的資料庫操作的時候,使用一個連線物件還是在每個方法中使用新的連線物件,一直是很糾結的問題,心想頻繁的資料操作還是用一個連線物件快吧?在本文給出的測試程式碼中,有下列語句:

//SqlConnection conn = new SqlConnection(connString); //conn.Open(); 註釋掉這些語句,在被呼叫的方法中使用自己的連線物件,與取消註釋,全部使用一個連線物件,效率上沒有任何區別!

究其原因,可能是ADO.NET自動使用了連線池,實際上程式在不同的情況下,使用的都是一個連線,所以操作上效率沒有區別。

後續測試

在真正的伺服器上進行測試,發現測試結論又不一樣,我們有伺服器A,擁有16個核,32G記憶體,另外一臺伺服器B,擁有8個核,16G記憶體。在伺服器A上有一個SqlServer例項,兩個一樣的資料庫;在在伺服器B上有一個SqlServer例項,一個數據庫,下面是測試結果:

------單資料庫,單執行緒測試--------- used all time is(ms):650 ------同一例項,雙資料庫,單執行緒測試--------- used all time is(ms):418 ------同一例項,雙資料庫,多執行緒測試--------- Tread 2 used all time is(ms):221 Tread 1 used all time is(ms):223 ====All thread completed!======== ------雙例項,雙資料庫,單執行緒測試--------- used all time is(ms):1283 ------雙例項,雙資料庫,多執行緒測試--------- Tread 1 used all time is(ms):228 Tread 2 used all time is(ms):542 ====All thread completed!========

可以看到,同一例項,多資料庫,還是有明顯的優勢,而多執行緒優勢更大;由於兩臺伺服器效能差距較大,雙例項測試沒有顯示出優勢,但多執行緒下還是比單例項單資料庫好!

為什麼PC機跟伺服器測試的結論不一致?也許還是跟計算能力相關,PC機的計算負載太大,已經失去了測試的意義。