SQL預編譯


1.數據庫預編譯起源

(1)數據庫SQL語句編譯特性:
數據庫接受到sql語句之后,需要詞法和語義解析,優化sql語句,制定執行計划。這需要花費一些時間。但是很多情況,我們的一條sql語句可能會反復執行,或者每次執行的時候只有個別的值不同(比如query的where子句值不同,update的set子句值不同,insert的values值不同)。
(2)減少編譯的方法
如果每次都需要經過上面的詞法語義解析、語句優化、制定執行計划等,則效率就明顯不行了。為了解決上面的問題,於是就有了預編譯,預編譯語句就是將這類語句中的值用占位符替代,可以視為將sql語句模板化或者說參數化。一次編譯、多次運行,省去了解析優化等過程。
(3)緩存預編譯
預編譯語句被DB的編譯器編譯后的執行代碼被緩存下來,那么下次調用時只要是相同的預編譯語句就不需要編譯,只要將參數直接傳入編譯過的語句執行代碼中(相當於一個涵數)就會得到執行。
並不是所以預編譯語句都一定會被緩存,數據庫本身會用一種策略(內部機制)。
(4) 預編譯的實現方法
預編譯是通過PreparedStatement和占位符來實現的。

2.預編譯作用:

  • 預編譯階段可以優化 sql 的執行
    預編譯之后的 sql 多數情況下可以直接執行,DBMS 不需要再次編譯,越復雜的sql,編譯的復雜度將越大,預編譯階段可以合並多次操作為一個操作。可以提升性能。
  • 防止SQL注入
    使用預編譯,而其后注入的參數將不會再進行SQL編譯。也就是說其后注入進來的參數系統將不會認為它會是一條SQL語句,而默認其是一個參數,參數中的or或者and 等就不是SQL語法保留字了。

3.預編譯開啟

(1)數據庫是否默認開啟預編譯和JDBC版本有關。
也可以配置jdbc鏈接時強制開啟預編譯和緩存:useServerPrepStmts和cachePrepStmts參數。預編譯和預編譯緩存一定要同時開啟或同時關閉。否則會影響執行效率

conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/prepare_stmt_test?user=root&password=root&useServerPrepStmts=true&cachePrepStmts=true");  

 

(2)mysql的預編譯

  • 開啟了預編譯緩存后,connection之間,預編譯的結果是獨立的,是無法共享的,一個connection無法得到另外一個connection的預編譯緩存結果。
  • 經過試驗,mysql的預編譯功能對性能影響不大,但在jdbc中使用PreparedStatement是必要的,可以有效地防止sql注入。
  • 相同PreparedStatement的對象 ,可以不用開啟預編譯緩存。
          conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/prepare_stmt_test?user=root&password=root&useServerPrepStmts=true");  
          PreparedStatement stmt = conn.prepareStatement(sql);  
          stmt.setString(1, "aaa");  
          ResultSet rs1 = stmt.executeQuery();//第一次執行  
          s1.close();  
          stmt.setString(1, "ddd");  
          ResultSet rs2 = stmt.executeQuery();//第二次執行  
          rs2.close();  
          stmt.close(); 


 

 

4.mybatis是如何實現預編譯的

mybatis 默認情況下,將對所有的 sql 進行預編譯。mybatis底層使用PreparedStatement,過程是先將帶有占位符(即”?”)的sql模板發送至mysql服務器,由服務器對此無參數的sql進行編譯后,將編譯結果緩存,然后直接執行帶有真實參數的sql。核心是通過#{ } 實現的
在預編譯之前,#{ } 解析為一個 JDBC 預編譯語句(prepared statement)的參數標記符?。

//sqlMap 中如下的 sql 語句
select * from user where name = #{name};
//解析成為預編譯語句
select * from user where name = ?;

 

如果${ },SQL 解析階段將會進行變量替換。不能實現預編譯。

 

select * from user where name = '${name}'
//傳遞的參數為 "ruhua" 時,解析為如下,然后發送數據庫服務器進行編譯。
select * from user where name = "ruhua";

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM