1. 程式人生 > >Prepared Statements 如何大幅度提高效能

Prepared Statements 如何大幅度提高效能

 
本文講述瞭如何正確的使用prepared statements。為什麼它可以讓你的應用程式執行的更快,和同樣的讓資料庫操作變的更快。
 
 
為什麼Prepared Statements非常重要?如何正確的使用它?
 
資料庫有著非常艱苦的工作。它們接受來自眾多併發的客戶端所發出的SQL查詢,並儘可能快的執行查詢並返回結果。處理statements是一個開銷昂貴的操作,不過現在有了Prepared Statements這樣的方法,可以將這種開銷降到最低。可是這種優化需要開發者來完成。所以本文會為大家展示如何正確的使用Prepared Statements才能使資料庫操作達到最優化。
 
 
資料庫是如何執行一個statement的?
 
顯然,我不會在這裡寫出很多的細節,我們只關注最關鍵的部分。當一個數據庫收到一個statement後,資料庫引擎會先解析statement,然後檢查其是否有語法錯誤。一旦statement被正確的解析,資料庫會選出執行statement的最優途徑。遺憾的是這個計算開銷非常昂貴。資料庫會首先檢查是否有相關的索引可以對此提供幫助,不管是否會將一個表中的全部行都讀出來。資料庫對資料進行統計,然後選出最優途徑。當決建立查詢方案後,資料庫引擎會將它執行。



存取方案(Access Plan)的生成會佔用相當多的CPU。理想的情況是,當我們多次傳送一個statement到資料庫,資料庫應該對statement的存取方案進行重用。如果方案曾經被生成過的話,這將減少CPU的使用率。
 
 
Statement Caches
 
資料庫已經具有了類似的功能。它們通常會用如下方法對statement進行快取。使用statement本身作為key並將存取方案存入與statement對應的快取中。這樣資料庫引擎就可以對曾經執行過的statements中的存取方案進行重用。舉個例子,如果我們傳送一條包含SELECT a, b FROM t WHERE c = 2的statement到資料庫,然後首先會將存取方案進行快取。當我們再次傳送相同的statement時,資料庫會對先前使用過的存取方案進行重用,這樣就降低了CPU的開銷。

 
注意,這裡使用了整個statement為key。也就是說,如果我們傳送一個包含SELECT a, b FROM t WHERE c = 3的statement的話,快取中會沒有與之對應的存取方案。這是因為“c=3”與曾經被快取過的“c=2”不同。所以,舉個例子:
 
for (int i = 0; i < 1000; i++)  {
PreparedStatement ps = conn.prepareStatement("select a,b from t where c = " + i);
ResultSet rs = Ps.executeQuery();
rs.close();
ps.close();
}
 
在這裡快取不會被使用,因為每一次迭代都會發送一條包含不同SQL語句的statement給資料庫。並且每一次迭代都會生成一個新的存取方案。現在讓我們來看看下一段程式碼:
 
PreparedStatement ps = conn.prepareStatement("select a,b from t where c = ?");
for (int i = 0; i < 1000; i++)  {
ps.setInt(1, i);
ResultSet rs = ps.executeQuery();
rs.close();
ps.close();
}
 
這樣就具有了更好的效率,這個statement傳送給資料庫的是一條帶有引數“?”的SQL語句。這樣每次迭代會發送相同的statement到資料庫,只是引數“c=?”不同。這種方法允許資料庫重用statement的存取方案,這樣就具有了更好的效率。這可以讓你的應用程式速度更快,並且使用更少的CPU,這樣資料庫伺服器就可以為更多的人提供服務。

 
 
PreparedStatement與J2EE伺服器
 
當我們使用J2EE伺服器時事情會變的比較複雜。通常,一個perpared statement會同一個單獨的資料庫連線相關聯。當資料庫連線被關閉時prepared statement也會被丟棄。通常,一個胖客戶端會獲取一個數據庫連線並將其一直保持到退出。它會用“餓漢”(eagerly)或“懶漢”(lazily)方式建立所有的parepared statements。“餓漢”方式會在應用啟動時建立一切。“懶漢”方式意味著只有在使用的時候才去建立。“餓漢”方式會使應用程式在啟動的時候梢有延遲,但一旦啟動後就會執行的相當理想。“懶漢”方式使應用程式啟動速度非常快(但不會做任何準備工作),當需要使用prepared statement的時候再去建立。這樣,在建立全部statement的過程中,效能是非常不穩定的,但一旦建立了所有statement後,它會像“餓漢”式應用程式一樣具有很好的執行效果。請根據你的需要來選擇最好的方式,是快速啟動?還是前後一致的效能。
 
J2EE應用的問題是它不會像這樣工作,連線只會在請求期間被保持。那意味著必須每一次請求的時候都建立prepared statement。這遠沒有胖客戶端那種一直保持prepared statement的執行效能好。J2EE廠商已經注意到了這個問題,並且提供了連線池(ConnectionPool)以避免這種問題。

當J2EE伺服器提供了一個連線給你的應用程式時,其實它並沒有給你真正的資料庫連線,你只是獲得了一個包裝器(Wrapper)。你可以去看看你所獲得的連線的類名以證實這一點。它並不是一個JDBC連線,而是一個由應用伺服器建立的類。所有的JDBC操作都會被應用伺服器的連線池管理器所代理。所有的JDBC ResultSets,statements,CallableStatements,preparedStatements等都會被包裝並以一個“代理物件”(Proxy Object)的形式返回給應用程式。當你關閉了連線,這些物件會被標記為失效,並被垃圾回收器所回收。

通常,如果你對一個數據庫連線執行close,那這個連線會被JDBC驅動程式關閉。但我們需要在J2EE伺服器執行close的時候資料庫連線會被返回連線池。我們可以建立一個像真正的連線一樣的JDBC Connection代理類來解決這個問題。它有一個對真正連線的引用。當我們執行一個連線上的方法時,代理會將操作轉給真正的連線。但是,當我們對一個連線執行close時,這個連線並不會關閉,而是會送回連線池,並可以被其他請求所使用。一個已被準備過的prepared statement也會因此而得到重用。
 
 
J2EE PreparedStatement Cache
 
J2EE伺服器的連線池管理器已經實現了快取的使用。J2EE伺服器保持著連線池中每一個連線準備過的prepared statement列表。當我們在一個連線上呼叫preparedStatement時,應用伺服器會檢查這個statement是否曾經準備過。如果是,這個PreparedStatement會被返回給應用程式。如果否,呼叫會被轉給JDBC驅動程式,然後將新生成的statement物件存入連線快取。

每個連線都有一個快取的原因是因為:JDBC驅動程式就是這樣工作的。任何prepared statement都是由指定的連線所返回的。

如果我們想利用這個快取的優勢,那就如前面所說的,使用引數化的查詢語句可以在快取中找到曾經使用過的statement。大部分應用伺服器允許你調整prepared statements快取的大小。
 
 
摘要
 
我們絕對應該使用包含引數化的查詢語句的prepared statement。這樣資料庫就會重用準備過的存取方案。快取適用於整個資料庫,所以,如果你安排所有的應用程式使用相同的引數化SQL語句,然後你的其他應用程式就可以重用被準備過的prepared statement。這是應用伺服器的一個優勢,因為所有的資料庫操作都集中在資料庫操作層(Database Access Layer,包括O/R對映,實體Bean,JDBC等)。
 
第二,正確的使用prepared statement也是利用prepared statement的快取優勢的關鍵。由於應用程式可以重用準備過的prepared statement,也就減少了呼叫JDBC驅動程式的次數,從而提高了應用程式的效能。這樣就擁有了可以與胖客戶端比肩的效率,卻又不需要總維持一個連線。
 
使用引數化的prepared statement,你的應用程式會具有更好的效能。

///////////////

1.PreparedStatement是預編譯的,對於批量處理可以大大提高效率. 也叫JDBC儲存過程 
2.使用 Statement 物件。在對資料庫只執行一次性存取的時侯,用 Statement 物件進行處理。PreparedStatement 物件的開銷比Statement大,對於一次性操作並不會帶來額外的好處。 
3.statement每次執行sql語句,相關資料庫都要執行sql語句的編譯,preparedstatement是預編譯得,preparedstatement支援批處理 
4.
Code Fragment 1: 

String updateString = "UPDATE COFFEES SET SALES = 75 " + "WHERE COF_NAME LIKE ′Colombian′"; 
stmt.executeUpdate(updateString); 

Code Fragment 2: 

PreparedStatement updateSales = con.prepareStatement("UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ? "); 
updateSales.setInt(1, 75); 
updateSales.setString(2, "Colombian"); 
updateSales.executeUpdate(); 

片斷2和片斷1的區別在於,後者使用了PreparedStatement物件,而前者是普通的Statement物件。 PreparedStatement物件不僅包含了SQL語句,而且大多數情況下這個語句已經被預編譯過,因而當其執行時,只需DBMS執行SQL語句, 而不必先編譯。當你需要執行Statement物件多次的時候,PreparedStatement物件將會大大降低執行時間,當然也加快了訪問資料庫的 速度。 
這種轉換也給你帶來很大的便利,不必重複SQL語句的句法,而只需更改其中變數的值,便可重新執行SQL語句。選擇PreparedStatement對 象與否,在於相同句法的SQL語句是否執行了多次,而且兩次之間的差別僅僅是變數的不同。如果僅僅執行了一次的話,它應該和普通的物件毫無差異,體現不出 它預編譯的優越性。 
5.執行許多SQL語句的JDBC程式產生大量的Statement和PreparedStatement物件。通常認為 PreparedStatement物件比Statement物件更有效,特別是如果帶有不同引數的同一SQL語句被多次執行的時候。 PreparedStatement物件允許資料庫預編譯SQL語句,這樣在隨後的執行中可以節省時間並增加程式碼的可讀性。 

然而,在Oracle環境中,開發人員實際上有更大的靈活性。當使用Statement或PreparedStatement物件時,Oracle資料庫 會快取(和JDBC快取是兩回事)SQL語句以便以後使用。在一些情況下,由於驅動器自身需要額外的處理和在Java應用程式和Oracle伺服器間增加的網路活動,執行 PreparedStatement物件實際上會花更長的時間(因此,使用statement的最好時機是頻繁的引數--是引數專案而不是引數的值--變化時的SQL語句)。 

然而,除了緩衝的問題之外,至少還有一個更好的原因使我們在企業應用程式中更喜歡使用PreparedStatement物件,那就是安全性。傳遞給 PreparedStatement物件的引數可以被強制進行型別轉換,使開發人員可以確保在插入或查詢資料時與底層的資料庫格式匹配。 

當處理公共Web站點上的使用者傳來的資料的時候,安全性的問題就變得極為重要。傳遞給PreparedStatement的字串引數會自動被驅動器忽 略。最簡單的情況下,這就意味著當你的程式試著將字串“D'Angelo”插入到VARCHAR2中時,該語句將不會識別第一個“,”,從而導致悲慘的 失敗。幾乎很少有必要建立你自己的字串忽略程式碼。 

在Web環境中,有惡意的使用者會利用那些設計不完善的、不能正確處理字串的應用程式。特別是在公共Web站點上,在沒有首先通過 PreparedStatement物件處理的情況下,所有的使用者輸入都不應該傳遞給SQL語句。此外,在使用者有機會修改SQL語句的地方,如HTML的 隱藏區域或一個查詢字串上,SQL語句都不應該被顯示出來。 
在執行SQL命令時,我們有二種選擇:可以使用PreparedStatement物件,也可以使用Statement物件。無論多少次地使用同一個 SQL命令,PreparedStatement都只對它解析和編譯一次。當使用Statement物件時,每次執行一個SQL命令時,都會對它進行解析 和編譯。 


第一: 

prepareStatement會先初始化SQL,先把這個SQL提交到資料庫中進行預處理,多次使用可提高效率。 
createStatement不會初始化,沒有預處理,沒次都是從0開始執行SQL 

第二: 

prepareStatement可以替換變數 
在SQL語句中可以包含?,可以用ps=conn.prepareStatement("select * from Cust where ID=?"); 
int sid=1001; 
ps.setInt(1, sid); 
rs = ps.executeQuery(); 
可以把?替換成變數。 
而Statement只能用 int sid=1001; 
Statement stmt = conn.createStatement(); 
ResultSet rs = stmt.executeQuery("select * from Cust where ID="+sid); 
來實現。 

第三: 

prepareStatement會先初始化SQL,先把這個SQL提交到資料庫中進行預處理,多次使用可提高效率。 
createStatement不會初始化,沒有預處理,沒次都是從0開始執行SQL

相關推薦

Prepared Statements 如何大幅度提高效能

  本文講述瞭如何正確的使用prepared statements。為什麼它可以讓你的應用程式執行的更快,和同樣的讓資料庫操作變的更快。     為什麼Prepared Statements非常重要?如何正確的使用它?   資料庫有著非常艱苦的工作。它們接受來

Prepared Statements 如何大幅度提高效能

  本文講述瞭如何正確的使用prepared statements。為什麼它可以讓你的應用程式執行的更快,和同樣的讓資料庫操作變的更快。     為什麼Prepared Statements非常重要?如何正確的使用它?   資料庫有著非常艱苦的工作。它們接受來自眾多併發的客戶

使用Tcmalloc的效能測試結果報告,比malloc有大幅度提高

使用Tcmalloc的效能測試結果今天研究了一下tcmalloc的使用,感覺效果驚人,很是激動,特此寫出來以饗讀者。關於tcmalloc的介紹,參考文章:TCMalloc:執行緒快取的Malloc,是從google官方網站翻譯出來的。Tcmalloc的使用很簡單,只需要-l

SylixOS使用RAM檔案系統大幅度提高系統性能

在某些特定使用場景中可使用RAM檔案系統提高系統性能,解決業務問題。一下已MPC8313作為測試平臺,測試使用RAM和yaffs2檔案系統時,FTP檔案傳輸速率與應用程式載入時間的效能變化。 硬體平臺:MPC8313 base版本:V1.8.0 FTP檔案傳輸樣本:bspmpc8313.elf&

Go 提高效能的特性

1、值的高效處理和儲存,允許建立緊湊的資料結構,避免不必要的填充位元組。緊湊的資料結構能更好地利用快取。更好的快取利用率可帶來更好的效能。 2、函式的呼叫有開銷,減少函式呼叫開銷的解決方案是內聯。簡單的函式可以被 Go 編譯器內聯。 3、強制垃圾回收使 Go 成為一種更簡單,更安全的語言。這意味著在堆上分

自定義資料結構設計JDBC連線池提高效能

  // 後面貼上原始碼 之前寫一個小工具,發現了一個問題,發現部署到雲端計算上面,由於需要操作其他環境的資料庫,發現獲取66   175環境的獲取連線非常緩慢。 通過列印日誌,然後部署到伺服器,發現獲取175環境的資料庫連線池居然要6s?? 獲取66環境需

瞭解JavaScript中的Memoization以提高效能,再看React的應用

英文: Understanding Memoization in JavaScript to Improve Performance 中文: 瞭解JavaScript中的Memoization以提高效能--react的應用(歡迎star) 我們渴望提高應用程式的效能,Memoization是JavaScr

Java高併發(五)——Lock優化,提高效能

       前邊我們講了,多執行緒的世界,多執行緒的基礎操作,多執行緒協作,多執行緒管理——執行緒池。其中多執行緒為什麼麻煩,就因為執行緒並行操作,對共享資源的爭奪,會出現執行緒安全問題。而我們解決執行緒安全問題的方案是同步(鎖資源,序列使用),序列就會出現

Windows 10如何設定虛擬記憶體,這個技能學會了可大幅度提高電腦記憶體!

1、點選桌面【此電腦】-屬性,如下圖: 注意:如果發現開啟是如下介面,則考慮點選紅框中的選擇,然後在彈出的視窗中找到 此電腦,右擊屬性即可; 2、選擇高階系統設定,如下圖: 3、【高階】-【效能】-【設定】-【高階】-【虛擬記憶體】-【更改】,如

最全idea快捷鍵總結,大幅度提高工作效率

前言 剛開始使用idea的同學可能不熟悉idea的快捷鍵方式,覺得沒有eclipse的好用,但是不建議把idea的keymap改成eclipse,因為idea的快捷鍵設計有它獨到的一面,剛開始使用不習慣,但是用多了就會覺得這些快捷鍵非常好用,大幅度提高工作效率,聽說大神們合理使用id

快取 React 事件監聽器來提高效能

在 js 裡面有個不被重視的概念:物件和函式的引用,而這個卻直接地影響了 React 的效能。如果你打算建立兩個相同的函式,但是卻又不相等。你可以試著: JavaScript const functionOne = function() { ale

前端提高效能的方式

1、DNS預解析 可以通過預解析的方式來預先獲取域名所對應的IP。 2、瀏覽器快取   強快取與協商快取。   強快取表示在快取期間不需要請求。    如果快取過期了,我們就可以使用協商快取來解決問題。協商快取需要請求,如果快取有效會返回 304。 協商快取需要客戶端和服務端共同實現。 3、預載

tomcat使用APR提高效能

作業系統:Centos6.3 Tomcat:7.0.42 JDK:1.6.0_45 配置:8G,4核. 最近tomcat負載比較高,預設的配置和bio的處理方式已經無力支撐.據說APR能提升50%~60%的效能,所以嘗試下APR優化. APR介紹: Tomcat可以使用APR來提供超強的可伸縮性和效能,

為什麼資料庫讀寫分離可以提高效能

雖然知道處理大資料量時,資料庫要做讀寫分離,但是為什麼讀寫分離可以提高效能呢? 下面是搜來的一些解釋,看看再說! 一 什麼是讀寫分離 MySQL Proxy最強大的一項功能是實現“讀寫分離(Read/Write Splitting)”。基本的原理是讓主資料庫處理事務性查

使用JS提高效能--選擇器篇

前言:jquery選擇器提供了簡潔方便的查詢dom元素的API,經過jquery封裝過的dom元素是一個代理物件,包含dom本身,長度等等,可以列印jquery選擇器檢視。一個jquery選擇器代理了諸多。所以在dom查詢的速度上是慢於原生js選擇器的。 document

千萬級MySQL資料庫建立索引的事項及提高效能的手段

1.對查詢進行優化,應儘量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。 2.應儘量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:select id from t where nu

如何大幅度提高 Mac 開發效率

本文是視訊直播的文字整理,錄影可以在:優酷 上看到 關於 Mac 工作效率的文章一直層出不窮,然而並非所有內容都適合程式設計師,比如某些 Unix 命令,其實使用頻率非常低。作為一名初級 iOS 程式設計師,我嘗試著和大家分享一些能夠切實提高我們開發效率的小技巧。 我是無滑鼠主義者,任何需要滑鼠的操作在我看

CSS 優化、提高效能的方法有哪些?

CSS優化主要是4個方面: 載入效能 主要是從減少檔案體積,減少阻塞載入,提高併發方面入手 選擇器效能 渲染效能 可維護性 較為具體的優化方案: 慎重使用高性屬性:浮動、定位; 去除空規則; 屬性值為0時,不加單位; 屬性值為浮點數0.**時

【Java】緩衝流如何提高效能

前言         本文寫的粗糙,僅作於工作間隙的隨筆。         傳統的Java IO是基於阻塞的,他的工作狀態就是“讀/寫,等待,讀/寫,等待······”。         緩衝流有位元組和字元兩種,原理上幾乎差不讀,本處以位元組緩衝路來進行討論。 一、

#define 巨集函式,為什麼能夠提高效能

#define S(a,b) a*b //正確的巨集定義是#define S(r) ((r)*(r)) area=S(3,2);第一步被換為area=a*b; ,第二步被換為area=3*2; 類似於函式呼叫,有一個啞實結合的過程: 預處理(預編譯)工作也