測試併發與優化多線程、大批量處理SQL性能的一些心得(有心得就會更新)

測試併發與優化性能的一些心得

最近在做之前提到的服務的併發測試,有一些心得和優化處理。

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

測試結果和快速的原理

40000 27.007秒-14.858秒(可以看出緩存了SQL後插入是很快的) 這種forall的插入方式,SQL不變(如:insert into a values(a1:=x,a2:=y)),值在變化的好處就是可以利用緩存。比較適合關賬期間每個時間段很多次上傳數據的場景。有緩存的情況加效率很高。
50000 49.004秒 我們每條SQL要插60-100+字段
60000 94秒  

使用插入語句頻繁的服務可以用這種方式效率很高。


後續測試開發中有新的內容會再持續更新。

堅持做自己的百度,人!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章