此时 。你可能会想 。我讲了客户态的内存分配器:ptmalloc和tcmalloc 。无论是哪个分配器 。它的作用就是避免客户进程频繁向Linux内核申请内存空间 。造成cpu在客户态和内核态之间频繁更换 。从而影响内存存取的效率 。用它们就可以解决内存利用率的问题 。为什么MySQL还要自己搞一套?
或许MySQL的作者觉得无论哪个内存分配器 。它的实现都过于复杂 。这些复杂性会影响MySQL对于内存处理的性能 。因此 。MySQL自身又实现了一套内存分配机制:MEM_ROOT 。它的内存处理机制相对比较简单 。内存临时表的分配就是选用这样一种方式 。
下面 。我就以《导读》中的SQL为例 。仔细精准讲解一下分组统计是怎么才能利用MEM_ROOT内存分配和释放机制的?Spring Boot 学习笔记 。这个分享给你 。太全了 。
MEM_ROOT咱们先看看MEM_ROOT的结构 。MEM_ROOT设计比较简单 。主要包含这几部分 。如下图:
文章插图
free:一个单向链表 。链表中每一个单元叫block 。block中存放的是空闲的内存区 。每个block包含3个元素:
left:block中剩余的内存大小size:block对应内存的大小next:指向下一个block的指针如上图 。free所在的行就是一个free链表 。链表中每个箭头相连的部分就是block 。block中有left和 size 。每个block之间的箭头就是next指针
used:一个单向链表 。链表中每一个单元叫block 。block中存放已使用的内存区 。一样 。每个block包含上面3 个元素
min_malloc:控制一个 block 剩余空间还有多少的时候从free链表移除 。加入到used链表中
block_size:block对应内存的大小
block_num:MEM_ROOT 管理的block数量
first_block_usage:free链表中第一个block不满足申请空间大小的次数
pre_alloc:当释放整个MEM_ROOT的时候可以通过参数控制 。选择保留pre_alloc指向的block
下面我就以《导读》中的分组统计SQL为例 。看一下MEM_ROOT是如何分配内存的?分配
文章插图
初始化MEM_ROOT 。见上图:min_malloc = 32block_num = 4first_block_usage = 0pre_alloc = 0block_size = 1000err_handler = 0free = 0used = 0申请内存 。见上图:由于初始化MEM_ROOT时 。free = 0 。说明free链表不存在 。故向Linux内核申请4个大小为1000/4=250的block 。构造一个free链表 。如上图 。链表中包含4个block。结合前面free链表结构的说明 。每个block中size为250 。left也为250分配内存 。见上图:(1) 遍历free链表 。从free链表头部取出第一个block 。如上图向下的箭头(2) 从取出的block中划分220大小的内存区 。如上图向右的箭头上面-220 。block中的left从250变成30(3) 将划分的220大小的内存区分配给SQL中的groupby字段viewed_user_age和统计字段count(*) 。用于后面的统计分组数据收集到该内存区(4) 由于第(2)步中 。分配后的block中的left变成30 。30 < 32 。即小于第(1)步中初始化的min_malloc 。所以 。结合上面min_malloc的含义的教学 。该block将插入used链表最底部 。如上图底部 。由于used链表在第(1)步初始化时为0 。所以 。该block插入used链表的最底部 。即插入头部释放下面还是以《导读》中的分组统计为例 。咱们接下来看一下MEM_ROOT是如何释放内存的?
文章插图
image-20210323233158459.png
如上图 。MEM_ROOT释放内存的过程如下:遍历used链表中 。找到需要释放的block 。如上图 。block(30,250)为之前已分配给分组统计用的block将block(30,250)中的left + 220 。即30 + 220 = 250 。释放该block已使用的220大小的内存区 。得到释放后的block(250,250)将block(250,250)插入free链表最底部 。如上图曲线箭头部分通过MEM_ROOT内存分配和释放的教学 。咱们发现MEM_ROOT的内存管理方式是在每个Block上连续分配 。内部碎片基本在每个Block的最底部 。由min_malloc成员变量控制 。但是min_malloc的值是在代码中写死的 。有点不够灵活 。所以 。对一个block来探讨 。当left小于min_malloc 。从其申请的内存越大 。那么block中的left值越小 。那么 。该block的内存利用率越高 。碎片越少 。反之 。碎片越多 。这个写死是MySQL的内存分配的一个缺陷 。
磁盘临时表当分组及统计字段对应的所有值大小超过tmp_table_size决定的值 。那么 。MySQL将使用磁盘来存放这些值 。这个存放值的磁盘位置 。MySQL叫它磁盘临时表 。
咱们都知道磁盘存取的性能一定比内存存取的性能差很多 。因为会产生磁盘IO 。所以 。一旦分组及统计字段不得不写入磁盘 。那性能相对是很差的 。所以 。咱们尽量调大参数tmp_table_size 。使得组及统计字段可以在内存临时表中处理 。
推荐阅读
- 台湾青枣种植技术 台湾青枣的种植情况简介
- 地板瓷砖如何清理又亮又干净不留痕迹 地板瓷砖如何清理又亮又干净
- 你觉得最好的电视直播软件是什么?
- 装修136平米房子大概多少钱 装修房子136平的要多少钱
- 你有没有因为喜欢一个人然后去追了他所有的剧呢?
- 十大哺乳动物排行榜 最大陆生哺乳类动物介绍
- 最好用的电视直播软件?
- 怎样和异性开始第一次聊天?
- 新生儿飞机抱能缓解打嗝吗 新生儿喜欢飞机抱是什么原因