本站不再更新,歡迎光臨 java開發技術網
          隨筆-230  評論-230  文章-8  trackbacks-0
          為什么要做batch處理????

          ????這個問題我就不解釋了,因為我想你們肯定能比

          我解釋的更好!如果你真的不知道,那就到Google上去搜

          索一下吧?

          Oracle回滾段

          ????這個問題偶也不很明白,只是大概有個了解,如

          果你是這方面的專家,或者對這方面有比較深的理解,

          別忘了跟偶分享哦?

          在JDBC中如何做batch處理

          ????JDBC提供了數據庫batch處理的能力,在數據大批量操作(新增、刪除等)的情況下可以大幅度提升系統的性能。我以前接觸的一個項目,在沒有采用batch處理時,刪除5萬條數據大概要半個小時左右,后來對系統進行改造,采用了batch處理的方式,刪除5萬條數據基本上不會超過1分鐘。看一段JDBC代碼:
          1. //?關閉自動執行
          2. con.setAutoCommit(false);
          3. Statement?stmt?=?con.createStatement();
          4. stmt.addBatch("INSERT?INTO?employees?VALUES?(1000,?'Joe?Jones')");
          5. stmt.addBatch("INSERT?INTO?departments?VALUES?(260,?'Shoe')");
          6. stmt.addBatch("INSERT?INTO?emp_dept?VALUES?(1000,?260)");
          7. //?提交一批要執行的更新命令
          8. int[]?updateCounts?=?stmt.executeBatch();



          ????本例中禁用了自動執行模式,從而在調用?Statement.executeBatch()?時可以防止?JDBC?執行事務處理。禁用自動執行使得應用程序能夠在發生錯誤及批處理中的某些命令不能執行時決定是否執行事務處理。因此,當進行批處理更新時,通常應該關閉自動執行。

          ????在JDBC?2.0?中,Statement?對象能夠記住可以一起提交執行的命令列表。創建語句時,與它關聯的命令列表為空。Statement.addBatch()?方法為調用語句的命令列表添加一個元素。如果批處理中包含有試圖返回結果集的命令,則當調用?Statement.?executeBatch()?時,將拋出?SQLException。只有?DDL?和?DML?命令(它們只返回簡單的更新計數)才能作為批處理的一部分來執行。如果應用程序決定不提交已經為某語句構
          造的命令批處理,則可以調用方法?Statement.clearBatch()(以上沒有顯示)來重新設置批處理。

          ????Statement.executeBatch()?方法將把命令批處理提交給基本?DBMS?來執行。命令的執行將依照在批處理中的添加順序來進行。ExecuteBatch()?為執行的命令返回更新計數數組。數組中對應于批處理中的每個命令都包含了一項,而數組中各元素依據命令的執行順序(這還是和命令的最初添加順序相同)來排序。調用executeBatch()?將關閉發出調用的?Statement?對象的當前結果集(如果有一個結果集是打開的)。executeBatch()?返回后,將重新將語句的內部批處理命令列表設置為空。

          ????如果批處理中的某個命令無法正確執行,則?ExecuteBatch()?將拋出BatchUpdateException。可以調用BatchUpdateException.getUpdateCounts()?方法來為批處理中成功執行的命令返回更新計數的整型數組。因為當有第一個命令返回錯誤時,Statement.executeBatch()?就中止,而且這些命令是依據它們在批處理中的添加順序而執行的。所以如果?BatchUpdateException.getUpdateCounts()?所返回的數組包含?N?個元素,這就意味著在調用?executeBatch()?時批處理中的前?N?個命令被成功執行。用PreparedStatement?可以象下面這樣寫代碼:

          1. //?關閉自動執行
          2. con.setAutoCommit(false);
          3. PreparedStatement?stmt?=?con.prepareStatement("INSERT?INTO?employees?VALUES?(?,??)");
          4. stmt.setInt(1,?2000);
          5. stmt.setString(2,?"Kelly?Kaufmann");
          6. stmt.addBatch();
          7. ???
          8. //?提交要執行的批處理
          9. int[]?updateCounts?=?stmt.executeBatch();



          iBatis框架對batch處理的支持

          ????iBatis框架對batch處理提供了很好的支持,底層的實現方式就是JDBC。下面看一段示例代碼:

          1. ????private?void?execute(SqlMapClient?client){
          2. ????????if(log.isDebugEnabled()){
          3. ????????????log.debug("execute?start...");
          4. ????????}
          5. ????????client.startBatch();
          6. ????????
          7. ????????for(int?i=0;i<2000;i++){
          8. ????????????client.delete("delete?from?order?where?id=?",i);
          9. ????????????
          10. ????????}
          11. ????????client.executeBatch();
          12. ????????if(log.isDebugEnabled()){
          13. ????????????log.debug("execute?end...");
          14. ????????}
          15. ????}


          iBatis框架做batch處理的問題

          ????在一個batch中只能對一個表進行操作,例如插入或刪除。當有多個表需要處理時,只能放在多個batch中進行處理。看下面的一段代碼:
          1. ????private?void?execute(int?from,int?to,List?list){
          2. ????????if(log.isDebugEnabled()){
          3. ????????????log.debug("STRGHousekeepTask?execute?start...");
          4. ????????}
          5. ????????HKSqlMapWrapper?sqlWrapper?=?HKSqlMapWrapper.newInstance();
          6. ????????sqlWrapper.startBatch();
          7. ????????
          8. ????????for(int?i=from;i<to;i++){
          9. ????????????sqlWrapper.delete(STRGHousekeepConstants.DELETE_STRG_CNTR_BL,list.get(i));
          10. ????????????sqlWrapper.delete(STRGHousekeepConstants.DELETE_STRG_CNTR,list.get(i));
          11. ????????????sqlWrapper.delete(STRGHousekeepConstants.DELETE_CNTR,list.get(i));
          12. ????????}
          13. ????????sqlWrapper.execBatch();
          14. ????????if(log.isDebugEnabled()){
          15. ????????????log.debug("STRGHousekeepTask?execute?end...");
          16. ????????}
          17. ????}
          ????????????????????????????????????????????代碼1

          ????這段代碼的目的就是要刪除數據庫中3個表的數據,sqlWrapper是iBatis的SqlMapClient的一個包裝器,主要是封狀對事物的控制。當批次(既to-from的值)很小的時候,這樣寫是沒有問題的。盡管這段代碼的本意是要享受batch處理帶來的好處,但是事實上這段代碼并不會真正達到預期的效果,至于原因,我們一會在進行分析?。我們先來看下面一段代碼:
          1. ????private?void?execute(int?from,int?to,List?list){
          2. ????????if(log.isDebugEnabled()){
          3. ????????????log.debug("STRGHousekeepTask?execute?start...");
          4. ????????}
          5. ????????HKSqlMapWrapper?sqlWrapper?=?HKSqlMapWrapper.newInstance();
          6. ????????sqlWrapper.startBatch();
          7. ????????
          8. ????????for(int?i=from;i<to;i++){
          9. ????????????sqlWrapper.delete(STRGHousekeepConstants.DELETE_STRG_CNTR_BL,list.get(i));
          10. ????????}
          11. ????????for(int?i=from;i<to;i++){
          12. ????????????sqlWrapper.delete(STRGHousekeepConstants.DELETE_STRG_CNTR,list.get(i));
          13. ????????}
          14. ????????for(int?i=from;i<to;i++){
          15. ????????????sqlWrapper.delete(STRGHousekeepConstants.DELETE_CNTR,list.get(i));
          16. ????????}
          17. ????????sqlWrapper.execBatch();
          18. ????????if(log.isDebugEnabled()){
          19. ????????????log.debug("STRGHousekeepTask?execute?end...");
          20. ????????}
          21. ????}

          ????????????????????????????????????????????代碼2

          ????正如你所看到的,和代碼1相比它只是做了3次循環,每個循環執行一個表的操作。雖然麻煩,但是卻真正的享受到了batch處理的好處!下面是時候解釋一下這兩段代碼幕后的秘密了?。
          ????在前面的章節里已經解釋了JDBC如何做batch處理,如果還不清楚的話請查看前面的章節。要解釋這兩段代碼里面的玄機,還得看一段代碼?下面的代碼是從iBatis源碼中提取的:
          1. ????public?void?addBatch(RequestScope?request,?Connection?conn,?String?sql,?Object[]?parameters??)?{
          2. ??????PreparedStatement?ps?=?null;
          3. ??????if?(currentSql?!=?null
          4. ??????????&&?sql.hashCode()?==?currentSql.hashCode()
          5. ??????????&&?sql.length()?==?currentSql.length())?{
          6. ????????int?last?=?statementList.size()?-?1;
          7. ????????ps?=?(PreparedStatement)?statementList.get(last);
          8. ??????}?else?{
          9. ????????ps?=?conn.prepareStatement(sql);
          10. ????????currentSql?=?sql;
          11. ????????statementList.add(ps);
          12. ??????}
          13. ??????request.getParameterMap().setParameters(request,?ps,?parameters);
          14. ??????ps.addBatch();
          15. ??????size++;
          16. ????}

          ????這就是iBatis中batch處理的做法,在這里不想對這段代碼做一一解釋,有興趣的可以自己查看一下iBatis的源碼,我們只關心它如何對一條語句進行處理。參數sql是要進行batch處理的語句,parameters是sql的參數列表,如果sql和實例變量currentSql相等,則從statementList列表里面得到一個PreparedStatement,然后進行batch處理,如果不等就新生成一個PreparedStatement對象,并把它加到statementList列表里面,并把當前sql的值附給currentSql,下次傳遞來sql的時候就會和這個新的currentSql比較。這就是為什么在一個循環里面只對一個表進行處理的原因了。如果在一個循環里面對多個表進行處理,每次傳給addBatch方法的sql都是新的,都會生成一個新的PreparedStatement,所以也就享受不到batch處理帶來的好處了!????

          ???按照代碼1的方式執行程序,當batch?size很小的時候盡管享受不到batch處理帶來的好處,但是也不至于會出什么大問題,但是當batch?size值很大的時候(我在程序中試驗過1000-5000范圍),數據庫就會報錯了!錯誤是"too?many?courses",原因是每生成一個PreparedStatement實例,就會相應的生成一個course。假設batch?size是5000,要刪除10個表的數據,那么產生的course的數目就是5000*10=50000,這對數據庫來說是不能接受
          的,所以就會報錯。

          ????如果按照代碼2的的方式寫程序肯定是沒有問題的,只會生成10個PreparedStatement實例,相應的也只會生成10個course,這樣就真正的享受到了batch處理帶來的好處。但是,作為一名“挑剔”的程序員,我們怎么能容忍這樣的寫法呢?明明一個循環就可以搞定,現在要分成10個循環來做,非但效率上存在問題,大量重復的代碼也讓我們的程序顯得很沒“水準”。

          ????既然第一種方式不能享受batch處理帶來的好處,并且還會出錯,第二種方式代碼又非常的丑陋,那么我們就得想個辦法來解決這個問題了。請記住:解決問題的過程就是一種享受?。

          修改底層代碼,支持多表batch處理

          ????既然出問題的地方找到了,那么解決它就很容易了。什么,你說還不知道問題出在哪?My?God!?Kill?me?,pleale?!?

          ????在這里分享一下我的思路,把每次傳近來的sql作為key、把生成的PreparedStatement實例作為value放在一個Map里以后每次傳來sql時先判斷在Map里有沒有這個key,如果有就直接拿到它的value作為PreparedStatement實例,如果沒有就新生成一個PreparedStatement實例并把它放到Map里。這樣有幾個sql就有幾個PreparedStatement
          實例,和寫多個循環效果是一樣的。但寫一個循環會更爽?!?

          后記:

          ??????在一般的項目中做batch處理的地方似乎都是先取得一個條件列表list,然后直接根據這個list的大小作為batch size做一個循環。如果你在這個循環里同時進行多個表的CUD操作,那么這里就有一個安全隱患存在。當你的list不太大的時候,你怎么測試程序它都不會出問題,盡管可能會有執行效率上的問題,但是當突然有一天這個list變的很大的時候,你的程序可能就突然“罷工”了。?

            對于這個問題,我在上面的文檔里提出了改進batch處理的方法,另外還有需要注意的一個問題就是這個list的大小的問題。如果這個list的size有可能會很大,那么我們應該考慮根據這個list的大小“分批”執行。因為并不是batch size越大效果就越好,如果batch的size很大的話很可能產生效率和性能上的問題。至于這個batch size的值為多少比較合適就沒有一個固定的說法,這個可能要取決于你所使用的服務器和數據庫的性能了,另外不同廠商的JDBC驅動也會有不同的性能表現,你可以向DBA咨詢相關的問題。?

            我們應該盡可能把問題扼殺在搖籃之中。除了改進iBatis的batch處理機智外,還應該適當的規劃batch size大小,以避免發生問題,提高執行效率。?

            上述是我個人的觀點,有些地方可能不是很準確。如果你的程序中存在類似的問題,可以適當參考一下我的意見,最好還是向專業人士咨詢。
          posted on 2006-12-18 15:11 有貓相伴的日子 閱讀(2455) 評論(0)  編輯  收藏 所屬分類: ibatis/hibernate/持久層技術
          本站不再更新,歡迎光臨 java開發技術網
          主站蜘蛛池模板: 西峡县| 建瓯市| 洞口县| 进贤县| 昌宁县| 额济纳旗| 鄂尔多斯市| 武城县| 博野县| 西和县| 桦南县| 沁源县| 平乐县| 清涧县| 星子县| 吉林市| 义马市| 小金县| 攀枝花市| 祁门县| 来凤县| 汨罗市| 阿拉善左旗| 柏乡县| 凤城市| 阿拉尔市| 宽甸| 巫山县| 兴国县| 乌什县| 平武县| 娱乐| 阳江市| 建始县| 昌宁县| 中卫市| 肇源县| 常德市| 崇阳县| 星子县| 如皋市|