MySQL 存储过程运行的内存管理

2023年 8月 15日 72.4k 0

  • 一、讲解例子
  • 二、function内存管理过程讲解
  • 三、function内存管理过程图例
  • 四、总结

一、讲解例子

MySQL的存储过程在运行过程中的内存管理跟table等运行时候是不一样的,它涉及多层内存管理,在开发时候如果不注意内存管理很容易造成内存泄露。接下来我用以下function的例子来说明,procedure的也是类似的,只是少了return result的过程。

function语句示例:

set global log_bin_trust_function_creators=1;CREATE FUNCTION f1 (a VARCHAR(32)) RETURN VARCHAR(32) no sqlis result VARCHAR(32);BEGIN  result := CONCAT(a,'y');  RETURN(result);END;

二、function内存管理过程讲解

1、在debug模式下查看function的code。可以看到一共分了3个步骤实现。

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内存管理

mysql> select f1('mysql');

gdb跟踪return result的代码:

#0  sp_head::create_result_field (this=0x7fff400fd258, thd=0x7fffe83a9ca0, field_max_length=93825047739703,     field_name_or_null=0x7fffe83a9c90 "\300\234:\350\377\177", 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(m_return_field_def.pack_length() + 1);

3、执行function的内存管理

执行function的内存管理相关代码,sp_head::execute_function函数:

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_arenaerr_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释放的代码

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相关的处理过程。

+------------------------------------------+| thd(return result)                       |+           -------------------------------+|           |call_arena(create)            |+           |    --------------------------+|           |    |execute_arena(sp_instr)  ||           |    |                         |+------------------------------------------+

四、总结

MySQL存储过程的内存管理过程很精妙,代码中会出现多次的thd->swap_query_arena来进行内存切换,必须严格区分哪些数据应该放在对应的那个arena,才能正确管理sp数据。

Enjoy GreatSQL 🙂

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论