TrinityCore中的PreparedStatement

trinitycore中的數據庫即席查詢包括兩種方式:Statement 和 PreparedStatement


什麼是Statement:

Statement:一個Statement操作(執行sql語句),數據庫就會做三步: 1:語法分析 2:優化 3:執行並返回結果.

拋出問題:

對於頻繁操作的sql語句,每一次不同的地方僅僅是參數,sql語句的大部分都是相同的,比如:“select * from user where userID = ? ”,每次不同的只是參數userID,那麼對於這麼頻繁的並且只是參數不同的sql語句用Statement每次都去做語法分析 和 優化 是不是太浪費數據庫資源了?答案肯定是的。所以對於大部分情況sql語句都基本一致並且還頻繁操作的情況是不合適用Statement的,因爲它太浪費數據庫資源了。

詳細描述:

Statement方式是現在幾乎所有數據庫都支持的即席操作方式,也就是以一個數據庫操作語句作爲execute的參數,然後通過連接把這個sql語句傳給數據庫,數據庫就把該sql語句作語法分析生成語法分析樹,然後再做優化,最後數據庫才真正的execute這一條sql語句並返回操作結果。試想,如果在一個應用中某一sql語句操作特別頻繁,比如:玩家登陸的時候查詢玩家信息,那麼所有玩家在數據庫端都要執行這三步,其實對於玩家登陸時這一sql語句的操作在大部分時間基本上是一樣的,區別只是參數不同而已。那麼生成語法數 和 優化這兩部對於數據庫來說是沒必要做的。既然這樣頻繁操作的sql語句大部分都是一樣的,區別只是參數不一致。那麼,我們可不可以對於這樣的頻繁操作的sql語句生成一個模板語句,然後把這個sql語句模板交給數據庫做語法分析和優化這兩步。以後所有玩家登陸去查詢玩家信息時,數據庫就可以直接執行最後一步了。PreparedStatement就是這樣做的。


====================================================華麗的分割線=====================================================================


PreparedStatement的詳細描述:

之前看JDBC規範的時候對PreparedStatement只是簡單的知道會進行sql預編譯,能提高性能。具體原理也沒怎麼理解。

最近在性能測試遇到一個連接池的調優剛好是和PreparedStatement和PreparedStatementCache相關的。固重新系統的看了點資料學習了點,簡單記錄一下。

 

1.java.sql.PreparedStatement

首先看wiki對使用PS的解釋:

The typical workflow of using a prepared statement is as follows:
    Prepare: The statement template is created by the application and sent to the database management system (DMBS). Certain values are left unspecified, called parameters, placeholders or bind variables (labelled "?" below):
        INSERT INTO PRODUCT (name, price) VALUES (?, ?)
    The DBMS parses, compiles, and performs query optimization on the statement template, and stores the result without executing it.
Execute: At a later time, the application supplies (or binds) values for the parameters, and the DBMS executes the statement (possibly returning a result). The application may execute the statement as many times as it wants with different values. In this example, it might supply 'Bread' for the first parameter and '1.00' for the second parameter.

 簡單翻譯

一個 PreparedStatement 的執行過程:

1.        prepare :語句模板被創建,併發送給 DBMS ,具體參數沒有指定,知識用佔位符替代。(會有一次連接開銷,等會介紹PreparedStatemetCache會優化這個開銷。 )

2.        編譯:數據庫收到語句後會預先編譯和優化產生執行計劃。

3. 執行:客戶端傳進來綁定參數,數據庫根據動態參數拼裝並執行產生結果返回給客戶端

 

 

再看JDBC java.sql.Connection類的prepareStatement 方法註釋:

 

 

Java代碼  收藏代碼
  1. public PreparedStatement prepareStatement(String sql)  
  2.                                    throws SQLException  
  3.   
  4.     Creates a PreparedStatement object for sending parameterized SQL statements to the database.  
  5.   
  6.     A SQL statement with or without IN parameters can be pre-compiled and stored in a PreparedStatement object. This object can then be used to efficiently execute this statement multiple times.  
  7.   
  8.     Note: This method is optimized for handling parametric SQL statements that benefit from precompilation. If the driver supports precompilation, the method prepareStatement will send the statement to the database for precompilation. Some drivers may not support precompilation. In this case, the statement may not be sent to the database until the PreparedStatement object is executed. This has no direct effect on users; however, it does affect which methods throw certain SQLException objects.  

 

 

基本意識就是:

創建一個 PreparedStatement object 給數據庫發送參數化的 sql 語句。一個沒有參數的語句可以被預先編譯並存儲在 PreparedStatement 語句裏面。這個對象可以用來高效的多次執行語句。

 

以前理解預編譯和這個不太一致,罪過罪過。

 

 

2. oracle 的PreparedStatement如何發生效果

具體可以見附件   oracle preparedStatements.ppt   裏面有很詳細的描述。

Oracle 的 SQL 執行過程可以簡單描述如下:

 

 

 

 


 

預編譯後的語句的執行計劃存在於DBMS的shared pool中,可以省去前兩個階段的操作。直接進入第三階段。
也有很多DBMS是不支持的。好像mysql就不支持,這樣api上雖然看似使用PreparedStatement,實際上驅動實現並沒有真正實現這個功能,而是生成statement。

 

3.連接池的PreparedStatement cache 
這個概念也是之前我所不瞭解的。
PreparedStatement是JDBC裏面提供的對象,很多連接池都引入了PreparedStatementCache的概念。如Jboss連接池、C3P0,DBCP等。PreparedStatementCache即用於保存與數據庫交互的prepareStatement對象。在cache裏的ps對象,不需要重新走一次DBMS連接請求去創建。
如Jboss連接池的PreparedStatementCache實現,支付寶DBA的一個總結:JBOSS連接池1-PreparedStatementCache參數的作用及原理 
PreparedStatementCache是跟着connection走的。一個connection就會有一個cache。比如一個cache允許緩存20條語句,20個connection就可能緩存400個。

一般連接池可以支持這個的配置。由於會佔用較大內存,所以一般配置的時候要特別注意。DBCP裏對應的是poolPreparedStatements和maxOpenPreparedStatements兩個參數。
具體可以參考:dbcp configuration 
這個對性能的優化還是很有價值的。尤其對於應用內sql比較固定的場景,會有很大的性能提升。之前在我們項目裏,一個壓力場景沒有設置這兩個參數之前的tps大約是1700,設置之後的tps大約是3100.當然也有一定的內存開銷需要特別注意。



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