测试并发与优化多线程、大批量处理SQL性能的一些心得(有心得就会更新)

来源:互联网 发布:mysql负载均衡 编辑:程序博客网 时间:2024/06/05 12:48

测试并发与优化性能的一些心得

最近在做之前提到的服务的并发测试,有一些心得和优化处理。

N个并发请求服务器时,先由服务器的线程池来处理请求(同java普通的线程池)也是当核心线程数用完以后后来的请求会进入队列中。所以当并发量到一定程度的时候必须使用负载均衡(就分摊一下请求就可以)分摊一下并发压力,不然就算硬件在好效率也不一定上的去。

负载均衡我就不说了,不是我配置的,也不归我管没办法提供真实参数了。

有几种情况导致数据丢失:

1.内存不足:导致服务器无法创建新的线程处理请求(请求的数据量大内存自然紧张),该线程所涉及的请求则没有处理(负载均衡+定制化处理服务,如:超过30M数据量就调用其他服务处理)。

2.超过最大线程与队列,服务器会拒绝请求(负载均衡)。

3.服务器宕机,断网,等等外部因素(写一个查询补偿服务,如:定时查询数据库是否存在该数据不存在由查询服务再次请求一次)。

4.超过数据库最大访问量,或访问数据库超时等数据库问题(根据需求设置访问量控制)。

5.请求等待超时(等待时间长一些)。

 

1.服务器线程池:

处理方式:配置服务器线程池评估并发量通过负载均衡区把并发稀释到N个服务器。

只写一下本地tomcat的(服务器是weblogic的内存配的2048,weblogic调优推荐一篇博客:http://blog.csdn.net/rznice/article/details/7283494):

1.set JAVA_OPTS=-XX:PermSize=64M -XX:MaxPermSize=128m -Xms512m -Xmx1024m;-Duser.timezone=GMT+08;
一定加在catalina.bat最前面。

 

2.Tomcat配置:server。Xml

    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"

        maxThreads="500" minSpareThreads="20" maxIdleTime="60000" />

 

 <Connector executor="tomcatThreadPool"

port="8080" protocol="org.apache.coyote.http11.Http11Protocol"

               connectionTimeout="600000"  keepAliveTimeout="150000"

               redirectPort="8443" URIEncoding="UTF-8"

   useBodyEncodingForURI="true" maxPostSize="0"

   minSpareThreads="100" maxSpareThreads="300"  acceptCount="10000" />

protocol="org.apache.coyote.http11.Http11Protocol" 写这个别写HTTP1.1否则有误。

 

参数解释:(摘抄自百度)

maxThreads:Tomcat可创建的最大的线程数,每一个线程处理一个请求;

minSpareThreads:最小备用线程数,tomcat启动时的初始化的线程数;

maxSpareThreads:最大备用线程数,一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程;

acceptCount:指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,就是被排队的请求数,超过这个数的请求将拒绝连接。

connnectionTimeout:网络连接超时,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常可设置为30000毫秒。 
enableLookups:是否允许DNS查询

 

注意:可以多个connector公用1个线程池。

l connectionTimeout - 网络连接超时,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常可设置为30000毫秒。

l keepAliveTimeout - 长连接最大保持时间(毫秒)。此处为15秒。

l maxKeepAliveRequests - 最大长连接个数(1表示禁用,-1表示不限制个数,默认100个。一般设置在100~200之间) the maximum number of HTTP requests that can be held in the pipeline until the connection is closed by the server. Setting this attribute to 1 disables HTTP/1.0 keep-alive, as well as HTTP/1.1 keep-alive and pipelining. Setting this to -1 allows an unlimited number of pipelined or keep-alive HTTP requests. If not specified, this attribute is set to 100.

l maxHttpHeaderSize - http请求头信息的最大程度,超过此长度的部分不予处理。一般8K。

l URIEncoding - 指定Tomcat容器的URL编码格式。

l acceptCount - 指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理,默认为10个。defines the maximum queue length for incoming connection requests when all possible request processing threads are in use. Any requests received when the queue is full are refused. The default value is 10.

l disableUploadTimeout - 上传时是否使用超时机制

l enableLookups - 是否反查域名,取值为:true或false。为了提高处理能力,应设置为false

l bufferSize - defines the size (in bytes) of the buffer to be provided for input streams created by this connector. By default, buffers of 2048 bytes are provided.

l maxSpareThreads - 做多空闲连接数,一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程 the maximum number of unused request processing threads that are allowed to exist until the thread pool starts stopping the unnecessary threads. The default value is 50.

l maxThreads - 最多同时处理的连接数,Tomcat使用线程来处理接收的每个请求。这个值表示Tomcat可创建的最大的线程数。。 the maximum number of request processing threads to be created by this Connector, which therefore determines the maximum number of simultaneous requests that can be handled. If not specified, this attribute is set to 200.

l minSpareThreads - 最小空闲线程数,Tomcat初始化时创建的线程数 the number of request processing threads that are created when this Connector is first started. The connector will also make sure it has the specified number of idle processing threads available. This attribute should be set to a value smaller than that set for maxThreads. The default value is 4.

l minProcessors - 最小空闲连接线程数,用于提高系统处理性能,默认值为10。(用于Tomcat4中)

l maxProcessors - 最大连接线程数,即:并发处理的最大请求数,默认值为75。(用于Tomcat4中)

2.数据连接池:

配置好服务器连接池后,在做并发测试时出现IO异常连接数据库异常,或者不能open session 请查看连接池:

<beanid="dataSource"class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close">

<propertyname="driverClassName"value="${jdbc.driverClassName}"  />

<propertyname="url"value="${jdbc.url}"  /> 

<propertyname="username"value="${jdbc.username}"  />

<propertyname="password"value="${jdbc.password}"  />

<propertyname="initialSize"value="10"  />

<property name="maxActive" value="0"  />

<property name="maxWait" value="12000000" />

<propertyname="poolPreparedStatements"value="false"/>

<propertyname="defaultAutoCommit"value="true"/> 

</bean>

意思就不说了,maxActive设置为0表示无限制最大访问数。根据并发情况自己调整,maxWait等待时长调长点,并发在排队过程中恐怕就超时了。

3.方法中的连接池:

之前配过全局的连接池了可以直接用。(我得上上篇线程池的:http://blog.csdn.net/liud1/article/details/53613236)

如果方法有需要(例如执行时间长,执行频率高,数据量大)可以再方法内NEW一个连接池。方法结束时shoutdown等任务都处理完在关闭线程池,然后如果不想及时返回用getPool来判断是否结束就可以return了。


你并发越大,所需要的服务器也就意味着越多,负载均衡分摊后每个服务器并发也就500-1000左右,每个请求携带数据不大的话应该不会有内存溢出的情况。总之不同情况需要看你服务器的NB程度了。几百的小并发就无所谓了以上配置足够,F5都免了。配置没有最好,没有最优,适合系统的才是最好的。


4.插入表的sql优化:

以前用的jdbc批处理执行N条SQL语句:
public int updateBatch(final List<String> sqlList){
//构造sql
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
Statement stmt = null;
int conunt = 0;
try {
conn = SessionFactoryUtils.getDataSource(sessionFactory).getConnection();
conn.setAutoCommit(false);
stmt  = conn.createStatement();
for (int i = 0;i < sqlList.size(); i++) {
stmt.addBatch(sqlList.get(i));
conunt++;
logger.info(sqlList.get(i));//打印SQL日志
if(i%20==0){
stmt.executeBatch();
stmt.clearBatch();
}
}
stmt.executeBatch();
conn.commit();
return conunt;
} catch (Exception ex) {
try {
conn.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
throw new RuntimeException(ex);
} finally {
try {
conn.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}
close(rs,ps,conn);
}
}
效率一般,后来采用数据库forall的方式效率大大增加

调用DEMO:

public int invokingPro(final List<Map<String, String>> list,Long batchNumber){
Connection con = null;  
CallableStatement cstmt = null;
if(list==null || list.size()==0){
return 0;
}
try {       
   con = SessionFactoryUtils.getDataSource(sessionFactory).getConnection();
   ArrayDescriptor tabDesc = ArrayDescriptor.createDescriptor("EIED_ARRAY", con);
   long start1 = System.currentTimeMillis();
   STRUCT[] structs = makeArray(list, con);//这个地方是构造数据请自由发挥
   long start2 = System.currentTimeMillis();
   System.out.println("解析:"+(start2 - start1)/1000.0 + " 秒");
   ARRAY vArray = new ARRAY(tabDesc, con, structs);   
   System.out.println("调用存储过程");
   cstmt = con.prepareCall("{call CG_LOAD_REAL_TIME_BATCHINSERT(?,?,?,?)}");       
   cstmt.setArray(1, vArray);
   cstmt.setLong(2,batchNumber);
   cstmt.setLong(3,2);
   cstmt.registerOutParameter(4,java.sql.Types.VARCHAR);
   cstmt.execute(); 
   long start3 = System.currentTimeMillis();
   System.out.println("调用结束:"+(start3 - start2)/1000.0 + " 秒");
   con.commit();
   System.out.println(cstmt.getString(4));
}catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("批量插入有误:"+e.getMessage());
}
return list.size();
}

存储过程:

 FORALL MODEL IN 1..P_EIEDMODEL.COUNT
    INSERT INTO EI_EXPENSES_LOADDET
      (EIED_ID,
       EIED_EBBU_CODE,
       EIED_ESCO_COMPANY_NO
       。。。)
      VALUES 
(SEQ_EI_EXPENSES_LOADDET.NEXTVAL,
P_EIEDMODEL(MODEL).EIED_EBBU_CODE,
P_EIEDMODEL(MODEL).EIED_ESCO_COMPANY_NO,
。。。)
;

创建数组类型:CREATE OR REPLACE TYPE EIED_ARRAY AS TABLE OF EIED_MODEL

对象:CREATE OR REPLACE TYPE EIED_MODEL AS OBJECT
(
  eied_ebbu_code             VARCHAR2(32),
  eied_esco_company_no       VARCHAR2(32),

。。。
)

如果有不理解的或者没用过的童鞋请点击链接,我不再做赘述:http://www.cnblogs.com/iyoume2008/p/6139925.html

测试结果和快速的原理

4000027.007秒-14.858秒(可以看出缓存了SQL后插入是很快的)这种forall的插入方式,SQL不变(如:insert into a values(a1:=x,a2:=y)),值在变化的好处就是可以利用缓存。比较适合关账期间每个时间段很多次上传数据的场景。有缓存的情况加效率很高。5000049.004秒我们每条SQL要插60-100+字段6000094秒 

使用插入语句频繁的服务可以用这种方式效率很高。


后续测试开发中有新的内容会再持续更新。

坚持做自己的百度,人!

0 0