MySQL之Prepared Statements


1.概述

prepared statement在MySQL4.1中引進並且增加了一些新的命令:

  • COM_STMT_PREPARE
  • COM_STMT_EXECUTE
  • COM_STMT_CLOSE
  • COM_STMT_RESET
  • COM_STMT_SEND_LONG_DATA

它還定義了一個更緊湊簡潔的結果集格式代替ProtocolText::Resultset來返回結果集。

記住不是所有的語句都是可以預處理的:

1.1 預處理說明

sql預處理首先要求客戶端提交需要執行的sql,這時提交的sql不傳遞真實的參數值,參數以問號的形式傳遞過去。

eg:

insert into user(id, name) values(?, ?);
select * from user where id = ?;

在mysql服務端完成預編譯解析,這里的預編譯解析包括解析參數的個數、類型等,然后響應給客戶端。接下來客戶端第二次交互只需傳遞參數過去就可以完成一個完整的sql的執行。相比傳統的sql執行,預處理需要兩次交互,才能完成一次sql執行。

預處理的優勢:
(1)預處理sql能一定程度上防止sql注入

(2)sql預編譯效率更高

(3)二進制包協議讓sql預處理更加高效。

mysql預處理命令參數的封裝以及結果集的返回,均采用二進制格式封裝數據,體積更小,面向底層,能直接被mysql服務端利用。相比普通sql文本協議傳輸的數據,二進制協議傳輸數據更加高效。

2.二進制協議結果集

二進制協議結果集類似ProtocolText::Resultset.它僅包含二進制協議結果集行格式。

ProtocolBinary::Resultset:

Packet:

  • lenenc_int column_cout > 0
  • column_count * Protocol::ColumnDefinition
  • 沒有或者很多ProtocolBinary::ResultsetRow
  • EOF_Packet

注意:如果CLIENT_DEPRECATE_EOF客戶端性能標志被設置,發送OK_Packet,否則發送EOF_Packet。

例如:

3. 二進制協議結果集行

3.1 NULL-Bitmap

二進制協議結果集行由NULL位圖組成,該位圖包含與結果集+ 2中的列一樣多的位以及二進制協議值格式中非NULL的列的值。

ProtocolBinary::ResultsetRow:

3.2 二進制結果集的行(COM_STMT_EXECUTE)

payload:

1      packet header[00]

string[$len]      NULL-bitmap,length: (列數 + 7 + 2 )/8

string[$len]      values

例子:

3.2.1 NULL-Bitmap

二進制協議將NULL值作為位發送到位圖內而不是像ProtocolText :: ResultsetRow那樣發送完整字節。 如果發送了許多NULL值,則它比舊方法更有效。

警告:

對於二進制協議結果集行,num-fields和field-pos需要添加2的偏移量。對於COM_STMT_EXECUTE,此偏移量為0。

NULL位圖需要足夠的空間來為發送的每個列存儲可能的NULL位。 其空間計算如下:

NULL-bitmap-bytes = (num-fields + 7 + offset) / 8

導致:

4. 預處理命令說明

4.1 COM_STMT_PREPARE

COM_STMT_PREPARE命令用於客戶端往服務端提交一個預處理的sql,如上面提到的:

1 insert into user(id, name) values(?, ?);

4.2 COM_STMT_EXECUTE

COM_STMT_EXECUTE用於執行預處理sql,正如前面說到的,如果預處理sql需要傳遞參數,這個命令會發送預處理語句所需要的參數到服務端。如上面的例子,需要傳遞兩個參數id和user的具體值到服務端。

4.3 COM_STMT_CLOSE

COM_STMT_CLOSE用於關閉服務端預處理sql,每一個預處理預處理的sql提交后都保存在mysql服務端的內存當中,每個預處理sql都有一個唯一的id標識,這個命令將發送需要關閉的sql的id,通知服務端可以將所有該預處理sql的資源釋放掉(過多的預處理sql保留在服務端會占用較多的內存,因此有必要執行該命令清理無用的預處理sql)。

4.4 COM_STMT_RESET

COM_STMT_RESET命令用於重置COM_STMT_SEND_LONG_DATA命令發送的blob數據。

4.5 COM_STMT_SEND_LONG_DATA

COM_STMT_SEND_LONG_DATA用於往服務端發送字節流數據,通常來說只有在發送blob字段數據才用到該命令。可以多次調用該命令連續傳同一個字段的字節的數據,這個命令必須在COM_STMT_EXECUTE命令發送之前執行。

5. 預處理協議結果包說明

mysql預處理結果集采用了二進制協議包進行封裝,與普通的查詢結果集格式不同。(普通的結果集包采用文本協議包進行封裝)。

5.1 普通查詢結果集協議包

普通sql查詢(相比預處理sql查詢)返回的結果集包用文本協議(官方稱為Text Protocol)封裝。文本協議的結果集包格式根據官網的一個圖來說明:

 

 一個結果集包主要包括以下部分(順序傳輸):

  • one pakcet show field count(第一個packet用於表示返回結果集列數)
  • column defines packets(一個列就是一個packet, 格式參考Column Define Pakcet)
  • EOF Packet
  • row packets(一行數據就是一個packet, 格式參考ResultsetRow Packet)
  • EOF Packet

5.2 預處理結果集協議包

預處理結果集包的組成和普通協議包類似,區別只在於row packet(數據以二進制協議格式存放)。

  • one packet show field count(第一個packet用於表示返回結果集列數)
  • column define packets(一列就是一個packet,格式參考普通協議包的Column Define Packet)
  • EOF Packet
  • binary row packets(一行數據一個packet,格式參考Binary Row Pakcet)
  • EOF Packet

說明:

Binary Row Packet的第一個字節恆為0, 表示paket header, 接下來,由NULL-Bitmap標識那些值為NULL的列,NULL-Bitmap的長度計算方式為(column-count +7 + 2)/8,其中column-count表示列數,而非空的列值以二進制協議格式(協議格式參考Binary Protocol Value)順序存儲在NULL-Bitmap的后面。

提示:
返回相同結果行,預處理協議包所占字節比普通協議包小,在列數越多,列越長的情況下,相差的大小越明顯。

5.3 mysql jdbc 預處理

java.sql.preparestatement可以執行預處理sql,mysql jdbc實現了該接口,並且將預處理分為客戶端和服務端預處理

5.3.1 jdbc客戶端預處理

mysql jdbc默認情況下采用的就是客戶端預處理。客戶端預處理的意思是,所有預處理參數都將被緩存在mysql jdbc層,而不是緩存在mysql server。在PrepareStatement執行的時候,在jdbc端完成sql語句的拼接(主要是使用緩存的參數對sql中問號?進行替換, 最終發送到mysql的就是完整的sql語句)。客戶端預處理走得是普通的查詢協議,而不是真正的mysql預處理協議。

6 Mycat預處理實現機制

Mycat也實現了mysql的預處理協議,可以接收預處理命令的處理。當使用預處理查詢,也可以返回正確的二進制結果集包。Mycat預處理的實現是一種取巧的設計,查詢走到后端mysql實際上不是發送了預處理命令,而是普通的COM_QUERY命令,后端mysql返回給Mycat的結果集包也是文本協議包,只是在Mycat將結果集包發送往客戶端的中間過程,將普通的文本協議結果集包包裝成為二進制協議結果集包,然后再返回給客戶端。

Mycat預處理的處理流程:

(1)Mycat接收到客戶端發送的COM_STMT_PREPARE命令后,解析協議包的內容得到預處理sql語句,eg:insert into user(id, name)value(?, ?),將這些預處理語句緩存在Mycat里面;

(2)當Mycat再次接收到客戶端發送的COM_STMT_EXECUTE命令,就把相應的問號替換為實際傳遞過來的參數值,這時候已經得到了完整的sql語句。

(3)接下來,直接把這個語句丟給Mycat sql查詢處理器去執行,中間會經過sql解析模塊,路由解析模塊以及最后的執行。

(4)最后,當收到后端mysql傳遞給Mycat的數據准備發往客戶端的時候,做一個協議轉換,將普通文本結構集協議包轉換為二進制結果集協議包並發往客戶端。


免責聲明!

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



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