gtxyzz

单数据库vs多数据库,单实例vs多实例 效率测试

gtxyzz 运维技术 2022-11-19 594浏览 0

最近公司的项目准备优化一下系统的性能,希望在数据库方面看有没有提升的空间,目前压力测试发现数据库服务器压力还不够大,Web服务器压力也不是很大的情况下,前台页面访问却很慢,看有没有办法充分利用数据库服务器的性能,于是做了一个单数据库,多数据库,单实例,多实例不同情况下的数据访问效率测试。

测试环境:

  • CPU:Inter Core2 Quad,Q8300,2.50GHz;
  • 内存:4.00GB
  • 系统:Windows 7 32位系统
  • 数据库系统:SqlServer 2008,有两个实例,一个是默认实例,一个是命名实例QE2

测试数据:

67万真实的基金收益数据,将这个表的数据放到了3个数据库中,详细内容见下面的连接字符串配置:

<addname="Ins1_DB1"connectionString="DataSource=.;InitialCatalog=TestDB;IntegratedSecurity=True"/> 
<addname="Ins1_DB2"connectionString="DataSource=.;InitialCatalog=LocalDB;IntegratedSecurity=True"/> 
<addname="Ins2_DB"connectionString="DataSource=.\QE2;InitialCatalog=TestDB;IntegratedSecurity=True"/> 

测试内容:

首先筛选出表中所有的基金代码,然后统计每只基金的最新收益率日期,对应的T-SQL代码如下:

declare@max_fsrqdatetime 
declare@currJJDMvarchar(10) 
declare@temptable(jjdm2varchar(10)) 
declare@useTimedatetime 
set@useTime=GETDATE(); 
 
insertinto@temp(jjdm2) 
selectjjdmfrom[FundYield]groupbyjjdmorderbyjjdmasc
 
whileEXISTS(selectjjdm2from@temp) 
begin
set@currJJDM=(selecttop1jjdm2from@temp) 
select@max_fsrq=MAX(fsrq)from[FundYield]wherejjdm=@currJJDM 
deletefrom@tempwherejjdm2=@currJJDM 
print@max_fsrq 
end
 

print'T-SQLExecuteTimes(ms):' 
printdatediff(ms,@useTime,getdate()) 

直接执行这个T-SQL脚本,在数据库表没有索引的情况下,耗费的时间是:

T-SQLExecuteTimes(ms): 
58796

根据这个功能,写了一个.net控制台程序来测试,测试程序没有使用任何数据访问框架,直接使用ADO.NET,下面是多线程测试的代码,其它代码略:

publicstaticvoidTest2(stringconnName1,stringconnName2) 
{ 
System.Diagnostics.Stopwatchwatch=newSystem.Diagnostics.Stopwatch(); 
watch.Start(); 
stringallJjdmList=""; 
stringconnString=getConnectionString(); 
//SqlConnectionconn=newSqlConnection(connString); 
//conn.Open(); 

stringsql="selectjjdmfrom[FundYield]groupbyjjdmorderbyjjdmasc"; 
DataSetds=getData(connString,sql); 
intallCount=ds.Tables[0].Rows.Count; 
intp=(int)(allCount*0.5); 

System.Threading.Threadt1=newSystem.Threading.Thread(newSystem.Threading.ParameterizedThreadStart(tp1=> 
{ 
for(inti=0;i<p;i++) 
{ 
stringjjdm=ds.Tables[0].Rows[i][0].ToString(); 

objectresult=getSclar(ConfigurationManager.ConnectionStrings[connName1].ConnectionString, 
string.Format("selectMAX(fsrq)from[FundYield]wherejjdm='{0}'",jjdm)); 
if(result!=DBNull.Value) 
{ 
DateTimedt=Convert.ToDateTime(result); 
//Console.WriteLine("Thread2No{0},jjdm[{1}]lastFSRQis:{2}",i,jjdm,dt); 
} 

allJjdmList=allJjdmList+","+jjdm; 
} 

Console.WriteLine("Tread1usedalltimeis(ms):{0}",watch.ElapsedMilliseconds); 
} 
)); 

System.Threading.Threadt2=newSystem.Threading.Thread(newSystem.Threading.ParameterizedThreadStart(tp2=> 
{ 
for(inti=p;i<allCount;i++) 
{ 
stringjjdm=ds.Tables[0].Rows[i][0].ToString(); 
//这里不论使用default还是express,区别不大 
objectresult=getSclar(ConfigurationManager.ConnectionStrings[connName2].ConnectionString, 
string.Format("selectMAX(fsrq)from[FundYield]wherejjdm='{0}'",jjdm)); 
if(result!=DBNull.Value) 
{ 
DateTimedt=Convert.ToDateTime(result); 
//Console.WriteLine("Thread2No{0},jjdm[{1}]lastFSRQis:{2}",i,jjdm,dt); 
} 
 
allJjdmList=allJjdmList+","+jjdm; 
} 

Console.WriteLine("Tread2usedalltimeis(ms):{0}",watch.ElapsedMilliseconds); 
} 
)); 

t1.Start(); 
t2.Start(); 
t1.Join(); 
t2.Join(); 

Console.WriteLine("====Allthreadcompleted!========"); 

}

#p#

下面是测试结果:

第一次,数据库没有创建索引,进行全表扫描:

------单数据库,单线程测试--------- 
usedalltimeis(ms):59916 
------同一实例,双数据库,单线程测试--------- 
usedalltimeis(ms):59150 
------同一实例,双数据库,多线程测试--------- 
Tread2usedalltimeis(ms):51223 
Tread1usedalltimeis(ms):58175 
====Allthreadcompleted!======== 
------双实例,双数据库,单线程测试--------- 
usedalltimeis(ms):58230 
------双实例,双数据库,多线程测试--------- 
Tread2usedalltimeis(ms):52705 
Tread1usedalltimeis(ms):58293 
====Allthreadcompleted!========

第二次,数据库响应的字段创建索引,下面是测试结果:

------单数据库,单线程测试--------- 
usedalltimeis(ms):1721 
------同一实例,双数据库,单线程测试--------- 
usedalltimeis(ms):1737 
------同一实例,双数据库,多线程测试--------- 
Tread2usedalltimeis(ms):1684 
Tread1usedalltimeis(ms):1714 
====Allthreadcompleted!======== 
------双实例,双数据库,单线程测试--------- 
usedalltimeis(ms):1874 


------单数据库,单线程测试--------- 
usedalltimeis(ms):1699 
------同一实例,双数据库,单线程测试--------- 
usedalltimeis(ms):1754 
------同一实例,双数据库,多线程测试--------- 
Tread1usedalltimeis(ms):1043 
Tread2usedalltimeis(ms):1103 
====Allthreadcompleted!======== 
------双实例,双数据库,单线程测试--------- 
usedalltimeis(ms):1838 
------双实例,双数据库,多线程测试--------- 
Tread1usedalltimeis(ms):1072 
Tread2usedalltimeis(ms):1139 
====Allthreadcompleted!========

测试结论:

综合全表扫描访问和有索引方式的访问,

单线程访问:

  • 在同一个数据库实例上,双数据库没有体现出优势,甚至单数据库稍微优胜于多数据库;
  • 在两个数据库实例上,双实例双实例要落后于单实例单数据库;

多线程访问:

  • 双数据库实例稍微落后于单数据库实例;

综合结论,看来不论是双数据库还是双实例,对比与单实例或者单数据库,都没有体现出优势,看来前者的优势不在于访问效率,一位朋友说,数据库实例是不同的服务,控制粒度更小,维护影响比较低。但我想,双数据库实例,双数据库,多核CPU,应该跟两台数据库服务器差不多的性能吧,怎么没有体现优势呢?也许是我的测试机器仅仅有一个磁盘,这里磁盘IO成了瓶颈。

这个测试有没有意义,或者这个结果的原因,还请大牛们多多指教!

意外发现:

1,有人说频繁的查询在完全数据库中进行效率最高,测试发现,在查询分析器上直接运行上面的那个T-SQL脚本,跟程序从数据库取出数据,再加工计算查询,效率上没有明显的区别,所以哪些支持“将复杂的业务逻辑写在存储过程中效率最高的观点是站不住脚的!” ,ADO.NET从数据库来回操作数据一样有效率,如果加上复杂的字符函数计算和大批量的循环操作,存储过程的效率不一定高。

2,在使用程序进行频繁的数据库操作的时候,使用一个连接对象还是在每个方法中使用新的连接对象,一直是很纠结的问题,心想频繁的数据操作还是用一个连接对象快吧?在本文给出的测试代码中,有下列语句:

//SqlConnectionconn=newSqlConnection(connString); 
//conn.Open();

注释掉这些语句,在被调用的方法中使用自己的连接对象,与取消注释,全部使用一个连接对象,效率上没有任何区别!

究其原因,可能是ADO.NET自动使用了连接池,实际上程序在不同的情况下,使用的都是一个连接,所以操作上效率没有区别。

后续测试

在真正的服务器上进行测试,发现测试结论又不一样,我们有服务器A,拥有16个核,32G内存,另外一台服务器B,拥有8个核,16G内存。在服务器A上有一个SqlServer实例,两个一样的数据库;在在服务器B上有一个SqlServer实例,一个数据库,下面是测试结果:

------单数据库,单线程测试--------- 
usedalltimeis(ms):650 
------同一实例,双数据库,单线程测试--------- 
usedalltimeis(ms):418 
------同一实例,双数据库,多线程测试--------- 
Tread2usedalltimeis(ms):221 
Tread1usedalltimeis(ms):223 
====Allthreadcompleted!======== 
------双实例,双数据库,单线程测试--------- 
usedalltimeis(ms):1283 
------双实例,双数据库,多线程测试--------- 
Tread1usedalltimeis(ms):228 
Tread2usedalltimeis(ms):542 
====Allthreadcompleted!========

可以看到,同一实例,多数据库,还是有明显的优势,而多线程优势更大;由于两台服务器性能差距较大,双实例测试没有显示出优势,但多线程下还是比单实例单数据库好!

为什么PC机跟服务器测试的结论不一致?也许还是跟计算能力相关,PC机的计算负载太大,已经失去了测试的意义。

继续浏览有关 SQL Server 的文章
发表评论