MySQL中普通sql與預編譯sql 區別

aoerqileng發表於2023-04-23

MySQL中的預編譯提到的不多,oracle上一次編譯多次執行,是有很大的效能提升的,這裡看下mysql為什麼沒有那麼大的提升,以及跟普通sql相比有哪些區別。


執行緒在初始化建立的時候,會使用init_sql_alloc分配main_mem_root部分,main_mem_root也是mem_root, 在sql執行時候使用。


執行普通的sql,在轉發sql後,回進行記憶體的分配,分配到mem_root中,會先在free列表中查詢是否有足夠的空間,沒有就分配,有就返回分配的地址。呼叫堆疊如下:

mysqld!alloc_root (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/mysys/my_alloc.c:274)
mysqld!Query_arena::alloc(unsigned long) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_class.h:783)
mysqld!alloc_query(THD*, char const*, unsigned long) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:2158)
mysqld!dispatch_command(THD*, COM_DATA const*, enum_server_command) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:1467)
mysqld!do_command(THD*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:1032)
mysqld!::handle_connection(void *) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/conn_handler/connection_handler_per_thread.cc:313)
mysqld!::pfs_spawn_thread(void *) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/perfschema/pfs.cc:2197)
libsystem_pthread.dylib!_pthread_start (未知源:0)
libsystem_pthread.dylib!thread_start (未知源:0)


在普通sql執行完成後,會呼叫free_root 進行mem_root記憶體的釋放

mysqld!free_root (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/mysys/my_alloc.c:487)
mysqld!dispatch_command(THD*, COM_DATA const*, enum_server_command) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:1947)
mysqld!do_command(THD*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:1032)
mysqld!::handle_connection(void *) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/conn_handler/connection_handler_per_thread.cc:313)
mysqld!::pfs_spawn_thread(void *) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/perfschema/pfs.cc:2197)
libsystem_pthread.dylib!_pthread_start (未知源:0)
libsystem_pthread.dylib!thread_start (未知源:0)

預編譯sql執行

1 SET @s = 'SELECT * from products where code=?';
2 PREPARE stmt2 FROM @s;
3 SET @a = 200;
4 EXECUTE stmt2 USING @a;


執行1的時候是跟普通sql一樣的使用mem_root,釋放mem_root

在執行第二步的時候也會線上程上使用記憶體,進行語法解析,驗證語句,執行緒中儲存了stmt_map物件,這個字典用來存放預編譯的語句

mysqld!alloc_query(THD*, char const*, unsigned long) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:2158)
mysqld!Prepared_statement::prepare(char const*, unsigned long) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_prepare.cc:3247)
mysqld!mysql_sql_stmt_prepare(THD*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_prepare.cc:2323)
mysqld!mysql_execute_command(THD*, bool) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:2837)
mysqld!mysql_parse(THD*, Parser_state*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:5584)
mysqld!dispatch_command(THD*, COM_DATA const*, enum_server_command) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:1491)
mysqld!do_command(THD*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:1032)
mysqld!::handle_connection(void *) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/conn_handler/connection_handler_per_thread.cc:313)
mysqld!::pfs_spawn_thread(void *) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/perfschema/pfs.cc:2197)
libsystem_pthread.dylib!_pthread_start (未知源:0)
libsystem_pthread.dylib!thread_start (未知源:0)

在這裡呼叫lex_start進行了一次解析。


所以如果預編譯語句有很多的話,這個map會比較大,引數max_prepared_stmt_count是限制的全域性的數量

 if (thd->stmt_map.insert(thd, stmt))


執行第四步的時候,是真正的執行,堆疊

mysqld!mysql_sql_stmt_execute(THD*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_prepare.cc:2640)
mysqld!mysql_execute_command(THD*, bool) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:2842)
mysqld!mysql_parse(THD*, Parser_state*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:5584)
mysqld!dispatch_command(THD*, COM_DATA const*, enum_server_command) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:1491)
mysqld!do_command(THD*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:1032)
mysqld!::handle_connection(void *) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/conn_handler/connection_handler_per_thread.cc:313)
mysqld!::pfs_spawn_thread(void *) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/perfschema/pfs.cc:2197)
libsystem_pthread.dylib!_pthread_start (未知源:0)
libsystem_pthread.dylib!thread_start (未知源:0)

執行完後,也會呼叫 free_root進行釋放。


所以預編譯跟普通sql執行,多了儲存預編譯語句的map物件,預編譯多了一次賦值的解析與執行。


問題1: 一次編譯,多次執行,編譯的結果放到哪裡了?

mysql的跟oracle不太一樣,mysql的是每次輸入引數,會給sql設定下對應的值,然後走一遍普通的mysql_execute_command的流程。stmt_map針對效能提升這沒有什麼用途,就是用來儲存語句的。


所以這裡來看,mysql 使用預編譯語句對效能的影響不如oracle那麼大,只是在防止注入的安全上有用。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/25719946/viewspace-2947595/,如需轉載,請註明出處,否則將追究法律責任。

相關文章