* GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。
- 一、讲解例子
- 二、function内存管理过程讲解
- 三、function内存管理过程图例
- 四、总结
一、讲解例子
MySQL的存储过程在运行过程中的内存管理跟table等运行时候是不一样的,它涉及多层内存管理,在开发时候如果不注意内存管理很容易造成内存泄露。接下来我用以下function的例子来说明,procedure的也是类似的,只是少了return result的过程。
function语句示例:
代码语言:javascript复制set global log_bin_trust_function_creators=1;
CREATE FUNCTION f1 (a VARCHAR(32)) RETURN VARCHAR(32) no sql
is result VARCHAR(32);
BEGIN
result := CONCAT(a,'y');
RETURN(result);
END;
二、function内存管理过程讲解
1、在debug模式下查看function的code。可以看到一共分了3个步骤实现。
代码语言:javascript复制mysql> show function code f1;
----- ------------------------------
| Pos | Instruction |
----- ------------------------------
| 0 | set result@1 NULL |
| 1 | set result@1 concat(a@0,'y') |
| 2 | freturn 15 result@1 |
----- ------------------------------
3 rows in set (0.01 sec)
2、return result内存管理
代码语言:javascript复制mysql> select f1('mysql');
gdb跟踪return result的代码:
代码语言:javascript复制#0 sp_head::create_result_field (this=0x7fff400fd258, thd=0x7fffe83a9ca0, field_max_length=93825047739703,
field_name_or_null=0x7fffe83a9c90 "300234:350377177", table=0x7fff400fd258)
at /home/wuyy/greatdb/gitmerge/percona-server/sql/sp_head.cc:1972
#1 0x0000555558b52165 in Item_func_sp::init_result_field (this=0x7fff400fbe50, thd=0x7fff40001060)
at /home/wuyy/greatdb/gitmerge/percona-server/sql/item_func.cc:8768
#2 0x0000555558b53209 in Item_func_sp::fix_fields (this=0x7fff400fbe50, thd=0x7fff40001060, ref=0x7fff400fc2d0)
at /home/wuyy/greatdb/gitmerge/percona-server/sql/item_func.cc:9021
#3 0x0000555558e37345 in setup_fields (thd=0x7fff40001060, want_privilege=1, allow_sum_func=true, split_sum_funcs=true,
column_update=false, typed_items=0x0, fields=0x7fff400fadd0, ref_item_array=..., is_ora_update_set=false)
at /home/wuyy/greatdb/gitmerge/percona-server/sql/sql_base.cc:9244
#4 0x0000555558f9d023 in Query_block::prepare (this=0x7fff400fadc8, thd=0x7fff40001060, insert_field_list=0x0)
at /home/wuyy/greatdb/gitmerge/percona-server/sql/sql_resolver.cc:275
#5 0x0000555558fcba8b in Sql_cmd_select::prepare_inner (this=0x7fff400fc4d0, thd=0x7fff40001060)
at /home/wuyy/greatdb/gitmerge/percona-server/sql/sql_select.cc:470
#6 0x0000555558fcb52c in Sql_cmd_dml::prepare (this=0x7fff400fc4d0, thd=0x7fff40001060)
at /home/wuyy/greatdb/gitmerge/percona-server/sql/sql_select.cc:392
#7 0x0000555558fcbd43 in Sql_cmd_dml::execute (this=0x7fff400fc4d0, thd=0x7fff40001060)
at /home/wuyy/greatdb/gitmerge/percona-server/sql/sql_select.cc:525
#8 0x0000555558f42e77 in mysql_execute_command (thd=0x7fff40001060, first_level=true)
at /home/wuyy/greatdb/gitmerge/percona-server/sql/sql_parse.cc:4784
在sp_head::create_result_field看到该result field是建立在thd->mem_root的,也就是一开始thd的内存里面。
table->record[0] = thd->mem_root->ArrayAlloc<uchar>(m_return_field_def.pack_length() 1);
3、执行function的内存管理
执行function的内存管理相关代码,sp_head::execute_function函数:
代码语言:javascript复制1、在sp_head::execute_function有如下代码用来创建运行内存:
thd->swap_query_arena(call_arena, &backup_arena); 建立新的内存块call_arena用来存放funciton运行产生的数据。
sp_rcontext *func_runtime_ctx =
sp_rcontext::create(thd, m_root_parsing_ctx, return_value_fld); sp_rcontext::create运行的内存在call_arena
thd->swap_query_arena(backup_arena, &call_arena); 内存切换回一开始thd的内存。
2、接着是每个instr步骤的内存管理:
thd->swap_query_arena(call_arena, &backup_arena); 内存切换到call_arena
err_status = execute(thd, true);
thd->swap_query_arena(backup_arena, &call_arena); 内存切换回一开始thd的内存。
3、以上第2步的execute(thd, true)内存管理
thd->swap_query_arena(execute_arena, &backup_arena);建立新的内存块execute_arena用来存放funciton运行每一个步骤产生的数据
do{
i = get_instr(ip);
err_status = i->execute(thd, &ip);
free_root(&execute_mem_root, MYF(0));
}while 每个步骤的内存块都在execute_arena上,每个sp_instr都是单独管理内存,该sp_instr执行完毕立即释放内存。因此这个内存块是临时的,所有希望 永久存放的数据都不应该存放在这个内存上。
thd->swap_query_arena(backup_arena, &execute_arena); 内存切换回call_arena
4、内存释放
以上产生的内存块call_arena释放的代码
代码语言:javascript复制sp_head::execute_function代码结束时候释放call_arena:
err_with_cleanup:
::destroy(func_runtime_ctx);
call_arena.free_items();
free_root(&call_mem_root, MYF(0)); 这里释放call_arena内存块
三、function内存管理过程图例
上面的过程总结如图所示,每个阶段内存产生的数据包括item和field都应该使用对应的arena,即thd->swap_query_arena来管理内存,这样才不会造成数据管理错乱,数据丢失等问题。procedure的内存管理也是一样的,只是少了return result相关的处理过程。
代码语言:javascript复制 ------------------------------------------
| thd(return result) |
-------------------------------
| |call_arena(create) |
| --------------------------
| | |execute_arena(sp_instr) |
| | | |
------------------------------------------
四、总结
MySQL存储过程的内存管理过程很精妙,代码中会出现多次的thd->swap_query_arena来进行内存切换,必须严格区分哪些数据应该放在对应的那个arena,才能正确管理sp数据。
Enjoy GreatSQL :)
《深入浅出MGR》视频课程
戳此小程序即可直达B站
https://www.bilibili.com/medialist/play/1363850082?business=space_collection&business_id=343928&desc=0
文章推荐:
- MySQL 8.0有趣的新特性:CHECK约束
- MySQL 启停过程了解一二
- 技术分享 | 微服务架构的数据库为什么喜欢分库分表?
- MySQL内存管理机制浅析
- 技术分析 | 浅析MySQL与ElasticSearch的组合使用
关于 GreatSQL
GreatSQL是由万里数据库维护的MySQL分支,专注于提升MGR可靠性及性能,支持InnoDB并行查询特性,是适用于金融级应用的MySQL分支版本。
Gitee: https://gitee.com/GreatSQL/GreatSQL
GitHub: https://github.com/GreatSQL/GreatSQL
Bilibili:
https://space.bilibili.com/1363850082/video