1、預處理干了什么
當我們提交一條資料庫陳述句時,陳述句到達資料庫服務那邊,資料庫服務需要決議這條sql陳述句,比如說語法檢查,查詢條件先后優化,然后才執行,對于預處理,簡單來說就是把客戶端與資料庫服務原本一次互動的分成兩次,首先,提交資料庫陳述句,讓資料庫服務先決議這條陳述句,其次,提交引數,呼叫陳述句并執行,這樣對于多次重復執行的陳述句來說,可以提交并決議一次資料庫陳述句就可以了,然后不斷的呼叫剛剛決議過得陳述句并執行,這樣就省去了多次決議同一條陳述句的時間,從而達到提高效率的目的,
預處理陳述句支持占位符(place holder),通過系結占位符的方式提交引數,一個非常重要的一點是,能與占位符系結的只能是值,而不能是sql陳述句的一些關鍵詞,例如陳述句:“select * from student where student.id = ?”,如果放入占位符(?)中的是“1 or 1=1”,那么“1 or 1=1”就會被當成一個值,即用``符號包括起來,最終這條非法的陳述句就出錯了,從而達到放sql注入的漏洞(sql injestion),
預處理機制主要的三步驟:
1、將陳述句進行預處理
2、執行陳述句
3、析構掉預處理陳述句,
2、關于`performance_schema`.`prepared_statements_instances` 表的介紹
運行sql腳本:show global variable like ‘%prepare%’, 可以看到一個叫‘performance_schema_max_prepared_statement_instances’的系統變數,其值為0表示不啟用預處理陳述句性能資料記錄表`performance_schema`.`prepared_statements_instances`;-1表示記錄的數量動態處理;其他正整數值則表示performance_schema_max_prepared_statement_instances記錄的最大條數,
表`performance_schema`.`prepared_statements_instances`又是什么呢?它是用來記錄預處理陳述句的一些基本資訊和性能資料,比如預處理陳述句的ID,預處理陳述句的名字,預處理陳述句的具體陳述句內容,預處理陳述句被執行的次數,每次執行耗時,每條預處理陳述句所屬的執行緒id等,當我們創建一條預處理陳述句時,就會插入一條資料到這張表里,預處理陳述句是基于連接的,連接斷開,則預處理陳述句自動洗掉,但`performance_schema`.`prepared_statements_instances`表是全域的,它與資料庫連接沒關系,有了這些資料,我們就可以知道,1、代碼中執行的陳述句是否真的做了預處理,2、通過了解預處理陳述句的執行情況來決定業務中是否需要把一個陳述句進行預處理,
3、qt prepare函式說明
根據我自己本身的專案需求,這次測驗的客戶端代碼使用的是Qt,這里記錄一個關鍵的函式:QSqlQuery類的prepare函式,呼叫prepare函式即是向資料庫提交一個創建預處理陳述句的命令,意味著呼叫期間,是會與資料庫服務進行一次互動的,需要注意的是,當同一個QSqlQuery類物件呼叫第二次prepare時,會將第一次呼叫prepare創建的預處理陳述句洗掉掉,然后再創建一條預處理陳述句,即便是這兩條預處理陳述句是一模一樣的,在呼叫QSqlQuery的exec函式時,也會將QSqlQuery先前創建的預處理陳述句洗掉掉,所以在查詢結束,關閉掉連接,或者查詢又執行了其他陳述句,從而導致`performance_schema`.`prepared_statements_instances`表沒有相關預處理陳述句的記錄,就會誤認為預處理陳述句創建失敗,其實Qt的這種做法,也省去了要我們人為的洗掉預處理陳述句,
4、實驗猜想
常規執行的陳述句和預處理后執行的陳述句不同點在于,在多次執行的情況下,預處理陳述句只需決議一次sql陳述句,而之后多花時間在傳輸引數和系結引數上,預處理陳述句在回傳結果時,使用的是二進制傳輸協議,而普通陳述句使用的是文本格式的傳輸協議,因此我們做出以下猜想并驗證,
1、如果執行的是簡單陳述句,那么普通執行和預處理執行性能上差別不大,預處理陳述句在重復執行復雜的陳述句情況下才展現出優勢,
2、在查詢結果集是大資料量的情況下,預處理陳述句會展現出性能優勢,
5、實驗資料記錄
| 序號 | 是否預處理 | 陳述句 | 是否遠程資料庫 | 回傳資料量 | 每次實驗陳述句執行總次數 | 三次實驗平均總耗時/單位毫秒 |
| 1 | 是 | select * from task where task.taskId in (?) | 是 | 1000 | 1000 | 69822 |
| 2 | 否 | select * from task where task.taskId in (arr) | 是 | 1000 | 1000 | 66778 |
| 3 | 是 | select * from task where task.taskId = ? | 是 | 1 | 1000 | 1260 |
| 4 | 否 | select * from task where task.taskId = id | 是 | 1 | 1000 | 951 |
| 5 | 是 | select * from task a LEFT JOIN task_file b ON a.taskId = b.task_id where a.taskName like '%s%' and b.file_id > 100000 and b.file_id < 200000 and a.taskId = ?"; | 是 | 2 | 1000 | 2130 |
| 6 | 否 | select * from task a LEFT JOIN task_file b ON a.taskId = b.task_id where a.taskName like '%s%' and b.file_id > 100000 and b.file_id < 200000 and a.taskId = 32327"; | 是 | 2 | 1000 | 1480 |
| 7 | 是 | select * from task where task.taskId in (?) | 否 | 1000 | 1000 | 57051 |
| 8 | 否 | select * from task where task.taskId in (arr) | 否 | 1000 | 1000 | 56235 |
| 9 | 是 | select * from task where task.taskId = ? | 否 | 1 | 1000 | 217 |
| 10 | 否 | select * from task where task.taskId = id | 否 | 1 | 1000 | 204 |
| 11 | 是 | select * from task a LEFT JOIN task_file b ON a.taskId = b.task_id where a.taskName like '%s%' and b.file_id > 100000 and b.file_id < 200000 and a.taskId = ?"; | 否 | 2 | 1000 | 366 |
| 12 | 否 | select * from task a LEFT JOIN task_file b ON a.taskId = b.task_id where a.taskName like '%s%' and b.file_id > 100000 and b.file_id < 200000 and a.taskId = 32327"; | 否 | 2 | 1000 | 380 |
6、結論
實驗的資料結果和我預期的相差有點兒大,但經過反復檢查測驗代碼和測驗程序,確認測驗本身應該沒有問題,尊重實驗資料,我們得出以下結論:
1、通過實驗5和實驗6對比,實驗11和實驗12對比,可得猜想1是錯誤的,結論應該是:MySQL預處理和常規查詢在簡單陳述句和復雜陳述句下,都沒有顯著性的性能差別,
2、通過實驗1和實驗2對比,實驗7和實驗8對比,可得猜想2是錯誤的,結論應該是:MySQL預處理和常規查詢的結果在資料傳輸上沒有顯著性的性能差距,
3、此外,對比遠程資料庫和本地資料庫實驗資料,可得結論:MySQL資料庫在本地會給資料操作帶來顯著性的性能提高,
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/255598.html
標籤:其他
上一篇:decimal型別怎么算位元組數
