MySQL中sp运行check表版本更新流程解析

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
RDS MySQL DuckDB 分析主实例,集群系列 8核16GB
简介: MySQL中sp运行check表版本更新流程解析
  • 一、MySQL的sp运行SQL语句两个步骤介绍
  • 二、代码跟踪
  • 三、知识应用
  • 四、总结

一、MySQL的sp运行sql语句两个步骤介绍

MySQL的sp运行SQL语句需要执行2个步骤:prepare和execute。第一次执行的时候先执行prepare,进行相关语句parse、itemize、fix_fields等操作,然后才开始进行execute操作。等第二次再执行该sp的时候就直接运行execute而不需要再次进行重复的prepare操作,这样可以节省sp运行时候重复prepare的开销。但是,对于表操作就有一个问题产生,那就是如果执行第二遍的时候表的结构发生改变了,那么不进行reprepare而直接execute是会发生错误的。因此,本文章的目的在于寻找sp多次运行时候如何确认表版本更新并进行正确的操作。

先看下面的例子:
CREATE TABLE t1 (a INT, b VARCHAR(10));
INSERT INTO t1 VALUES (1,'11');
INSERT INTO t1 VALUES (2,'21');
MySQL> select * from t1;
+------+------+
| a    | b    |
+------+------+
|    1 | 11   |
|    2 | 21   |
+------+------+
2 rows in set (0.01 sec)
DELIMITER $$
CREATE PROCEDURE p1()
BEGIN
update t1 set b='aa' where a=1;
END $$
DELIMITER ;
MySQL> call p1; #这里第一次操作,因此会先执行update这句SQL的prepare再进行execute。
Query OK, 1 row affected (0.05 sec)
MySQL> select * from t1;
+------+------+
| a    | b    |
+------+------+
|    1 | aa   |
|    2 | 21   |
+------+------+
2 rows in set (0.01 sec)
MySQL> call p1; #这里第二次操作,直接执行update这句SQL的execute。
Query OK, 0 rows affected (13.78 sec)
#接着我们执行表结构的更新。
MySQL> alter table t1 add i int;
Query OK, 0 rows affected (0.41 sec)
Records: 0  Duplicates: 0  Warnings: 0
#然后再次执行sp,就会发现这次执行了这句SQL的prepare再进行execute。
MySQL> call p1;
Query OK, 0 rows affected (34.24 sec)

二、代码跟踪

现在跟踪一下这个sp看看上面在哪里check表版本并且能正确执行reprepare的。

#首先看一下这个sp涉及的instr。
MySQL> show procedure code p1;
+-----+---------------------------------------+
| Pos | Instruction                           |
+-----+---------------------------------------+
|   0 | stmt "update t1 set b='aa' where a=1" |
+-----+---------------------------------------+
1 row in set (0.01 sec)
可以看到这个sp只涉及了sp_instr_stmt::execute的运行,因此跟踪一下代码找到sp_lex_instr::validate_lex_and_execute_core,可以发现这个函数里面有一个无限while循环,如果发现is_invalid()的话就重新执行parse动作,如果!is_invalid()就直接执行exec_core,这个跟上面的运行步骤就对的上了。但是表结构变更后在哪里被判定为rc=true的呢,那就从reset_lex_and_exec_core这个函数继续跟踪下去。
bool sp_lex_instr::validate_lex_and_execute_core(THD *thd, uint *nextp,
                                                 bool open_tables) {
  while (true) {
    if (is_invalid() || (m_lex->has_udf() && !m_first_execution)) {
      LEX *lex = parse_expr(thd, thd->sp_runtime_ctx->sp);
    }
    bool rc = reset_lex_and_exec_core(thd, nextp, open_tables);
    if (!rc) return false;
    thd->clear_error();
    invalidate();
  }
}
#跟踪代码发现有一个check_and_update_table_version函数是用来check表版本是否一致的
#打印堆栈看一下代码调用过程:
Thread 51 "mysqld" hit Breakpoint 6, check_and_update_table_version (thd=0x7fff70001060, tables=0x7fff702c4e20, 
    table_share=0x7fff70297640) at /mysql/sql/sql_base.cc:3722
3722   if (!tables->is_table_ref_id_equal(table_share)) {
(gdb) bt
#0  check_and_update_table_version (thd=0x7fff70001060, tables=0x7fff702c4e20, table_share=0x7fff70297640)
    at /mysql/sql/sql_base.cc:3722
#1  0x0000000003340f71 in open_and_process_table (thd=0x7fff70001060, lex=0x7fff702c2650, tables=0x7fff702c4e20, 
    counter=0x7fff702c26a8, prelocking_strategy=0x7fffec2e7478, has_prelocking_list=false, ot_ctx=0x7fffec2e7368)
    at /MySQL/sql/sql_base.cc:5223
#2  0x000000000333f788 in open_tables (thd=0x7fff70001060, start=0x7fffec2e7488, counter=0x7fff702c26a8, flags=0, 
    prelocking_strategy=0x7fffec2e7478) at /mysql/sql/sql_base.cc:5968
#3  0x0000000003343c96 in open_tables_for_query (thd=0x7fff70001060, tables=0x7fff702c4e20, flags=0)
    at /MySQL/sql/sql_base.cc:6958
#4  0x0000000003514334 in Sql_cmd_dml::execute (this=0x7fff702c6138, thd=0x7fff70001060)
    at /MySQL/sql/sql_select.cc:543
#5  0x0000000003475097 in mysql_execute_command (thd=0x7fff70001060, first_level=false)
    at /MySQL/sql/sql_parse.cc:3832
#6  0x00000000033075c6 in sp_instr_stmt::exec_core (this=0x7fff70249a80, thd=0x7fff70001060, nextp=0x7fffec2eac38)
    at /MySQL/sql/sp_instr.cc:1008
#7  0x00000000033052ed in sp_lex_instr::reset_lex_and_exec_core (this=0x7fff70249a80, thd=0x7fff70001060, 
    nextp=0x7fffec2eac38, open_tables=false) at /mysql/sql/sp_instr.cc:457
#8  0x00000000033060a4 in sp_lex_instr::validate_lex_and_execute_core (this=0x7fff70249a80, thd=0x7fff70001060, 
    nextp=0x7fffec2eac38, open_tables=false) at /mysql/sql/sp_instr.cc:741
#9  0x0000000003306748 in sp_instr_stmt::execute (this=0x7fff70249a80, thd=0x7fff70001060, nextp=0x7fffec2eac38)
    at /MySQL/sql/sp_instr.cc:925
#10 0x00000000032f4d74 in sp_head::execute (this=0x7fff7018e7a0, thd=0x7fff70001060, merge_da_on_success=true)
    at /MySQL/sql/sp_head.cc:2272
#11 0x00000000032f80e1 in sp_head::execute_procedure (this=0x7fff7018e7a0, thd=0x7fff70001060, args=0x0)
    at /MySQL/sql/sp_head.cc:2977
#可以发现open_tables函数调用了这个函数,这个函数调用了ask_to_reprepare,
#在sp运行中这个ask_to_reprepare返回的是true。因此这里就解开了之前的问题,
#为何表版本更新了会return true然后重新进行parse操作。
static bool check_and_update_table_version(THD *thd, TABLE_LIST *tables,
                                           TABLE_SHARE *table_share) {
  if (!tables->is_table_ref_id_equal(table_share)) {
    /*
      Version of the table share is different from the
      previous execution of the prepared statement, and it is
      unacceptable for this SQLCOM.
    */
    if (ask_to_reprepare(thd)) return true;
    /* Always maintain the latest version and type */
    tables->set_table_ref_id(table_share);
  }
  return false;
}

三、知识应用

如果想开发一种动态表类型,需要每次执行sp都重新parse这个表,那就可以借用这个ask_to_reprepare函数来保证多次执行sp的时候都会进行重新reprepare。

四、总结

在MySQL的sp操作中涉及表操作的sql语句一定会执行check_and_update_table_version这个函数,每次会根据这个函数的结果来确定要不要重新parse该sql语句,如果没有版本改变就直接进行execute操作,如果有改变就重新parse,先prepare再execute,这样可以保证每次执行sp的SQL语句的时候表结构一定是最新的。

EnjoyGreatSQL:)

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
3月前
|
存储 域名解析 弹性计算
阿里云上云流程参考:云服务器+域名+备案+域名解析绑定,全流程图文详解
对于初次通过阿里云完成上云的企业和个人用户来说,很多用户不仅是需要选购云服务器,同时还需要注册域名以及完成备案和域名的解析相关流程,从而实现网站的上线。本文将以上云操作流程为核心,结合阿里云的活动政策与用户系统梳理云服务器选购、域名注册、备案申请及域名绑定四大关键环节,以供用户完成线上业务部署做出参考。
|
4月前
|
存储 SQL 关系型数据库
MySQL中binlog、redolog与undolog的不同之处解析
每个都扮演回答回溯与错误修正机构角色: BinLog像历史记载员详细记载每件大大小小事件; RedoLog则像紧急救援队伍遇见突發情況追踪最后活动轨迹尽力补救; UndoLog就类似时间机器可倒带历史让一切归位原始样貌同时兼具平行宇宙观察能让多人同时看见各自期望看见历程而互不干扰.
234 9
|
5月前
|
存储 SQL 关系型数据库
MySQL 核心知识与索引优化全解析
本文系统梳理了 MySQL 的核心知识与索引优化策略。在基础概念部分,阐述了 char 与 varchar 在存储方式和性能上的差异,以及事务的 ACID 特性、并发事务问题及对应的隔离级别(MySQL 默认 REPEATABLE READ)。 索引基础部分,详解了 InnoDB 默认的 B+tree 索引结构(多路平衡树、叶子节点存数据、双向链表支持区间查询),区分了聚簇索引(数据与索引共存,唯一)和二级索引(数据与索引分离,多个),解释了回表查询的概念及优化方法,并分析了 B+tree 作为索引结构的优势(树高低、效率稳、支持区间查询)。 索引优化部分,列出了索引创建的六大原则
140 2
|
5月前
|
存储 SQL 关系型数据库
MySQL 核心知识与性能优化全解析
我整理的这份内容涵盖了 MySQL 诸多核心知识。包括查询语句的书写与执行顺序,多表查询的连接方式及内、外连接的区别。还讲了 CHAR 和 VARCHAR 的差异,索引的类型、底层结构、聚簇与非聚簇之分,以及回表查询、覆盖索引、左前缀原则和索引失效情形,还有建索引的取舍。对比了 MyISAM 和 InnoDB 存储引擎的不同,提及性能优化的多方面方法,以及超大分页处理、慢查询定位与分析等,最后提到了锁和分库分表可参考相关资料。
135 0
|
6月前
|
关系型数据库 MySQL
MySQL字符串拼接方法全解析
本文介绍了四种常用的字符串处理函数及其用法。方法一:CONCAT,用于基础拼接,参数含NULL时返回NULL;方法二:CONCAT_WS,带分隔符拼接,自动忽略NULL值;方法三:GROUP_CONCAT,适用于分组拼接,支持去重、排序和自定义分隔符;方法四:算术运算符拼接,仅适用于数值类型,字符串会尝试转为数值处理。通过示例展示了各函数的特点与应用场景。
|
9月前
|
SQL 算法 搜索推荐
mysql 之order by工作流程
本文深入解析了MySQL中`ORDER BY`的排序机制,通过具体示例展示了排序过程及性能优化方法。文章首先分析了基于内存和磁盘的排序方式,包括`sort_buffer_size`的影响以及临时文件的使用场景。接着介绍了`rowid`排序算法,该算法通过减少参与排序的数据量来提升性能,并对比了其与传统排序的区别。此外,还探讨了随机查询`ORDER BY RAND()`的执行流程及其优化策略。最后提到了MySQL 5.6引入的优先队列排序算法,适用于仅需部分有序结果的场景。文章结合`optimizer_trace`工具详细说明了各配置参数对排序行为的影响,为优化查询提供了实用指导。
140 1
mysql 之order by工作流程
|
8月前
|
SQL 运维 关系型数据库
MySQL Binlog 日志查看方法及查看内容解析
本文介绍了 MySQL 的 Binlog(二进制日志)功能及其使用方法。Binlog 记录了数据库的所有数据变更操作,如 INSERT、UPDATE 和 DELETE,对数据恢复、主从复制和审计至关重要。文章详细说明了如何开启 Binlog 功能、查看当前日志文件及内容,并解析了常见的事件类型,包括 Format_desc、Query、Table_map、Write_rows、Update_rows 和 Delete_rows 等,帮助用户掌握数据库变化历史,提升维护和排障能力。
|
9月前
|
监控 Shell Linux
Android调试终极指南:ADB安装+多设备连接+ANR日志抓取全流程解析,覆盖环境变量配置/多设备调试/ANR日志分析全流程,附Win/Mac/Linux三平台解决方案
ADB(Android Debug Bridge)是安卓开发中的重要工具,用于连接电脑与安卓设备,实现文件传输、应用管理、日志抓取等功能。本文介绍了 ADB 的基本概念、安装配置及常用命令。包括:1) 基本命令如 `adb version` 和 `adb devices`;2) 权限操作如 `adb root` 和 `adb shell`;3) APK 操作如安装、卸载应用;4) 文件传输如 `adb push` 和 `adb pull`;5) 日志记录如 `adb logcat`;6) 系统信息获取如屏幕截图和录屏。通过这些功能,用户可高效调试和管理安卓设备。
|
10月前
|
Java 数据库 开发者
详细介绍SpringBoot启动流程及配置类解析原理
通过对 Spring Boot 启动流程及配置类解析原理的深入分析,我们可以看到 Spring Boot 在启动时的灵活性和可扩展性。理解这些机制不仅有助于开发者更好地使用 Spring Boot 进行应用开发,还能够在面对问题时,迅速定位和解决问题。希望本文能为您在 Spring Boot 开发过程中提供有效的指导和帮助。
1223 12

推荐镜像

更多