單數(shù)據(jù)庫vs多數(shù)據(jù)庫,單實例vs多實例 效率測試
最近公司的項目準(zhǔn)備優(yōu)化一下系統(tǒng)的性能,希望在數(shù)據(jù)庫方面看有沒有提升的空間,目前壓力測試發(fā)現(xiàn)數(shù)據(jù)庫服務(wù)器壓力還不夠大,Web服務(wù)器壓力也不是很大的情況下,前臺頁面訪問卻很慢,看有沒有辦法充分利用數(shù)據(jù)庫服務(wù)器的性能,于是做了一個單數(shù)據(jù)庫,多數(shù)據(jù)庫,單實例,多實例不同情況下的數(shù)據(jù)訪問效率測試。
測試環(huán)境:
- CPU:Inter Core2 Quad,Q8300,2.50GHz;
- 內(nèi)存:4.00GB
- 系統(tǒng):Windows 7 32位系統(tǒng)
- 數(shù)據(jù)庫系統(tǒng):SqlServer 2008,有兩個實例,一個是默認(rèn)實例,一個是命名實例QE2
測試數(shù)據(jù):
67萬真實的基金收益數(shù)據(jù),將這個表的數(shù)據(jù)放到了3個數(shù)據(jù)庫中,詳細(xì)內(nèi)容見下面的連接字符串配置:
- <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"/>
測試內(nèi)容:
首先篩選出表中所有的基金代碼,然后統(tǒng)計每只基金的最新收益率日期,對應(yīng)的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())
直接執(zhí)行這個T-SQL腳本,在數(shù)據(jù)庫表沒有索引的情況下,耗費的時間是:
- T-SQL Execute Times(ms):
- 58796
根據(jù)這個功能,寫了一個.net控制臺程序來測試,測試程序沒有使用任何數(shù)據(jù)訪問框架,直接使用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,區(qū)別不大
- 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!========");
- }
#p#
下面是測試結(jié)果:
第一次,數(shù)據(jù)庫沒有創(chuàng)建索引,進(jìn)行全表掃描:
- ------單數(shù)據(jù)庫,單線程測試---------
- used all time is(ms):59916
- ------同一實例,雙數(shù)據(jù)庫,單線程測試---------
- used all time is(ms):59150
- ------同一實例,雙數(shù)據(jù)庫,多線程測試---------
- Tread 2 used all time is(ms):51223
- Tread 1 used all time is(ms):58175
- ====All thread completed!========
- ------雙實例,雙數(shù)據(jù)庫,單線程測試---------
- used all time is(ms):58230
- ------雙實例,雙數(shù)據(jù)庫,多線程測試---------
- Tread 2 used all time is(ms):52705
- Tread 1 used all time is(ms):58293
- ====All thread completed!========
第二次,數(shù)據(jù)庫響應(yīng)的字段創(chuàng)建索引,下面是測試結(jié)果:
- ------單數(shù)據(jù)庫,單線程測試---------
- used all time is(ms):1721
- ------同一實例,雙數(shù)據(jù)庫,單線程測試---------
- used all time is(ms):1737
- ------同一實例,雙數(shù)據(jù)庫,多線程測試---------
- Tread 2 used all time is(ms):1684
- Tread 1 used all time is(ms):1714
- ====All thread completed!========
- ------雙實例,雙數(shù)據(jù)庫,單線程測試---------
- used all time is(ms):1874
- ------單數(shù)據(jù)庫,單線程測試---------
- used all time is(ms):1699
- ------同一實例,雙數(shù)據(jù)庫,單線程測試---------
- used all time is(ms):1754
- ------同一實例,雙數(shù)據(jù)庫,多線程測試---------
- Tread 1 used all time is(ms):1043
- Tread 2 used all time is(ms):1103
- ====All thread completed!========
- ------雙實例,雙數(shù)據(jù)庫,單線程測試---------
- used all time is(ms):1838
- ------雙實例,雙數(shù)據(jù)庫,多線程測試---------
- Tread 1 used all time is(ms):1072
- Tread 2 used all time is(ms):1139
- ====All thread completed!========
測試結(jié)論:
綜合全表掃描訪問和有索引方式的訪問,
單線程訪問:
- 在同一個數(shù)據(jù)庫實例上,雙數(shù)據(jù)庫沒有體現(xiàn)出優(yōu)勢,甚至單數(shù)據(jù)庫稍微優(yōu)勝于多數(shù)據(jù)庫;
- 在兩個數(shù)據(jù)庫實例上,雙實例雙實例要落后于單實例單數(shù)據(jù)庫;
多線程訪問:
- 雙數(shù)據(jù)庫實例稍微落后于單數(shù)據(jù)庫實例;
綜合結(jié)論,看來不論是雙數(shù)據(jù)庫還是雙實例,對比與單實例或者單數(shù)據(jù)庫,都沒有體現(xiàn)出優(yōu)勢,看來前者的優(yōu)勢不在于訪問效率,一位朋友說,數(shù)據(jù)庫實例是不同的服務(wù),控制粒度更小,維護(hù)影響比較低。但我想,雙數(shù)據(jù)庫實例,雙數(shù)據(jù)庫,多核CPU,應(yīng)該跟兩臺數(shù)據(jù)庫服務(wù)器差不多的性能吧,怎么沒有體現(xiàn)優(yōu)勢呢?也許是我的測試機(jī)器僅僅有一個磁盤,這里磁盤IO成了瓶頸。
這個測試有沒有意義,或者這個結(jié)果的原因,還請大牛們多多指教!
意外發(fā)現(xiàn):
1,有人說頻繁的查詢在完全數(shù)據(jù)庫中進(jìn)行效率最高,測試發(fā)現(xiàn),在查詢分析器上直接運行上面的那個T-SQL腳本,跟程序從數(shù)據(jù)庫取出數(shù)據(jù),再加工計算查詢,效率上沒有明顯的區(qū)別,所以哪些支持“將復(fù)雜的業(yè)務(wù)邏輯寫在存儲過程中效率最高的觀點是站不住腳的!” ,ADO.NET從數(shù)據(jù)庫來回操作數(shù)據(jù)一樣有效率,如果加上復(fù)雜的字符函數(shù)計算和大批量的循環(huán)操作,存儲過程的效率不一定高。
2,在使用程序進(jìn)行頻繁的數(shù)據(jù)庫操作的時候,使用一個連接對象還是在每個方法中使用新的連接對象,一直是很糾結(jié)的問題,心想頻繁的數(shù)據(jù)操作還是用一個連接對象快吧?在本文給出的測試代碼中,有下列語句:
- //SqlConnection conn = new SqlConnection(connString);
- //conn.Open();
注釋掉這些語句,在被調(diào)用的方法中使用自己的連接對象,與取消注釋,全部使用一個連接對象,效率上沒有任何區(qū)別!
究其原因,可能是ADO.NET自動使用了連接池,實際上程序在不同的情況下,使用的都是一個連接,所以操作上效率沒有區(qū)別。
后續(xù)測試
在真正的服務(wù)器上進(jìn)行測試,發(fā)現(xiàn)測試結(jié)論又不一樣,我們有服務(wù)器A,擁有16個核,32G內(nèi)存,另外一臺服務(wù)器B,擁有8個核,16G內(nèi)存。在服務(wù)器A上有一個SqlServer實例,兩個一樣的數(shù)據(jù)庫;在在服務(wù)器B上有一個SqlServer實例,一個數(shù)據(jù)庫,下面是測試結(jié)果:
- ------單數(shù)據(jù)庫,單線程測試---------
- used all time is(ms):650
- ------同一實例,雙數(shù)據(jù)庫,單線程測試---------
- used all time is(ms):418
- ------同一實例,雙數(shù)據(jù)庫,多線程測試---------
- Tread 2 used all time is(ms):221
- Tread 1 used all time is(ms):223
- ====All thread completed!========
- ------雙實例,雙數(shù)據(jù)庫,單線程測試---------
- used all time is(ms):1283
- ------雙實例,雙數(shù)據(jù)庫,多線程測試---------
- Tread 1 used all time is(ms):228
- Tread 2 used all time is(ms):542
- ====All thread completed!========
可以看到,同一實例,多數(shù)據(jù)庫,還是有明顯的優(yōu)勢,而多線程優(yōu)勢更大;由于兩臺服務(wù)器性能差距較大,雙實例測試沒有顯示出優(yōu)勢,但多線程下還是比單實例單數(shù)據(jù)庫好!
為什么PC機(jī)跟服務(wù)器測試的結(jié)論不一致?也許還是跟計算能力相關(guān),PC機(jī)的計算負(fù)載太大,已經(jīng)失去了測試的意義。
原文鏈接:http://www.cnblogs.com/bluedoctor/archive/2011/06/28/2092113.html
【編輯推薦】