單資料庫,多資料庫,單例項,多例項不同情況下的資料訪問效率測試
最近公司的專案準備優化一下系統的效能,希望在資料庫方面看有沒有提升的空間,目前壓力測試發現數據庫伺服器壓力還不夠大,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機的計算負載太大,已經失去了測試的意義。