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

Posted 老叶茶馆_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL 存储过程运行的内存管理相关的知识,希望对你有一定的参考价值。

* GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。

  • 一、讲解例子

  • 二、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 sql
is 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<uchar>(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_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释放的代码

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 :)


《深入浅出MGR》视频课程

戳此小程序即可直达B站

https://www.bilibili.com/medialist/play/1363850082?business=space_collection&business_id=343928&desc=0



文章推荐:


想看更多技术好文,点个“在看”吧!

以上是关于MySQL 存储过程运行的内存管理的主要内容,如果未能解决你的问题,请参考以下文章

初识mysql(待补充)

18MySQL日志管理(上)

Mysql第十三天,Mysql内存管理及优化

PHP MySQL Cursor 实现以及它们如何管理内存

V8垃圾回收?看这篇就够了!

Java前提下, MySQL数据库,一次性存储大量数据导致内存溢出