MySQL中普通sql与预编译sql 区别

来源:这里教程网 时间:2026-03-01 17:09:37 作者:

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那么大,只是在防止注入的安全上有用。

相关推荐