爲何要作batch處理
這個問題我就不解釋了,由於我想大家確定能比
我解釋的更好!若是你真的不知道,那就到Google上去搜
索一下吧☻
Oracle回滾段
這個問題偶也不很明白,只是大概有個瞭解,如
果你是這方面的專家,或者對這方面有比較深的理解,
別忘了跟偶分享哦☻
在JDBC中如何作batch處理
JDBC提供了數據庫batch處理的能力,在數據大批量操做(新增、刪除等)的狀況下能夠大幅度提高系統的性能。我之前接觸的一個項目,在沒有采用batch處理時,刪除5萬條數據大概要半個小時左右,後來對系統進行改造,採用了batch處理的方式,刪除5萬條數據基本上不會超過1分鐘。看一段JDBC代碼:html
本例中禁用了自動執行模式,從而在調用 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 能夠象下面這樣寫代碼:java
iBatis框架對batch處理的支持
iBatis框架對batch處理提供了很好的支持,底層的實現方式就是JDBC。下面看一段示例代碼:程序員
iBatis框架作batch處理的問題
在一個batch中只能對一個表進行操做,例如插入或刪除。當有多個表須要處理時,只能放在多個batch中進行處理。看下面的一段代碼:sql
代碼1
這段代碼的目的就是要刪除數據庫中3個表的數據,sqlWrapper是iBatis的SqlMapClient的一個包裝器,主要是封狀對事物的控制。當批次(既to-from的值)很小的時候,這樣寫是沒有問題的。儘管這段代碼的本意是要享受batch處理帶來的好處,可是事實上這段代碼並不會真正達到預期的效果,至於緣由,咱們一會在進行分析☻。咱們先來看下面一段代碼:數據庫
代碼2
正如你所看到的,和代碼1相比它只是作了3次循環,每一個循環執行一個表的操做。雖然麻煩,可是卻真正的享受到了batch處理的好處!下面是時候解釋一下這兩段代碼幕後的祕密了☻。 在前面的章節裏已經解釋了JDBC如何作batch處理,若是還不清楚的話請查看前面的章節。要解釋這兩段代碼裏面的玄機,還得看一段代碼☻下面的代碼是從iBatis源碼中提取的:數組
這就是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大小,以免發生問題,提升執行效率。
上述是我我的的觀點,有些地方可能不是很準確。若是你的程序中存在相似的問題,能夠適當參考一下個人意見,最好仍是向專業人士諮詢。安全