8036中参数temptable_max_mmap/temptable_max_ram/tmp_table_size的含义(Allocator分配器)

为了说明清楚这3个参数的作用,我们有必要先从temptable引擎的内存分配器说起,这样能够更自然的带出这3个参数的确切含义,同时能够理解其内存分配的基本方式。

一、综述

在temptable引擎中包含了一个内存分配器Allocator,这个分配器主要目的是为了高效的管理内存,并且为temptable中的各个容器提供所需的内存,譬如临时表的索引插入数据就需要分配内存,每个线程都会初始化一个内存分配器。
总的来看这个内存分配器主要是以Block为单位进行分配的,并且每次需要分配的内存以Chunk为单位存放在Block中,这就减少了内存分配的次数,并且Block的分配是以1M,2M,4M,最大512M 成指数上升的,也就意味着如果需要大量的内存,分配的方式也会改变,因此比较灵活。而我们常见的 temptable_max_mmap/temptable_max_ram/tmp_table_size 参数就是在这一层生效的。

二、Block和Chunk

容器在分配内存的时候都是以Chunk为单位的,但是实际物理内存的分配是以Block为单位的,也就是说内存实际上实在Block中分配,如果不够才会到OS层面去获取,而一个Block到底多大下面在分析。每个Block有自己的metadata(在Block header中)如下,主要包含4个元素:

  • 物理内存类型 8字节,类型为分配的来自MMAP还是RAM
  • 当前block大小 8字节
  • chunk数量 8字节
  • first_pristine_offset 8字节,当前Block使用的总量包含了元数据,用m_offset(block 起点offset)+first_pristine_offset(Block使用总量) 就可以得到Block中下一个可用的位置(也叫做slot,但和上面的含义不同)。

而每个Chunk也有自己的metadata,主要存放的是本次分配chunk前first_pristine_offset的位置。我们大概将一个Block的表示如下,假定这个Block已经分配了2个Chunk,

block(block header)

       block metadata  chunk1 offset          chunk2 offset       
  |----   ----  ---- ---- | chunk1 header---data- |chunk2 header---data-|--------------------------------------------------------------------
  |                       |                                             |\
  |                       |                                             |
                                                                 next_available_slot
Block offset     pristine_offset =                           offset + pristine_offset
                    metadata  4*8                                定位新的内存点
                    +chunk 1  size
                    +chunk 2  size
                   保存的是偏移量

     
chunk1 header(8字节) = metadata  4*8 大小
chunk2 header(8字节) = metadata  4*8+chunk 1  size

并且通过Chunk的起点地址很容易的就能反推到Block起点地址,只需要使用Chunk内存的起点位置减去其元数据中存储的pristine_offset就能够快速反推Block的起点位置了如下,

inline uint8_t *Chunk::block() const { return m_offset - offset(); }

三、关于物理内存分配的策略

只要需要向OS申请内存,总是一次申请一个Block,temptable的内存分配主要包含2种策略,在8036中用到的是

  • Exponential_policy:size策略,主要是按照指数的方式增长内存,避免过多的物理内存分配影响性能,比如前面说的每个Block 1M,2M,4M 最大512M就是这个size 策略进行判断的。
  • Prefer_RAM_over_MMAP_policy_obeying_per_table_limit:source策略,首先会判断参数tmp_table_size是否超过,超过则直接报Result::RECORD_FILE_FULL,然后根据参数的设置temptable_max_mmap/temptable_max_ram,先考虑使用ram分配,不够在进行mmap分配。如果都满了则报Result::RECORD_FILE_FULL, 报错后转为Innodb物理临时表。

如果涉及到物理内存分配和释放的时候总是是调用两个函数如下

  • static temptable::allocate_from :从MMAP或者RAM中分配内存,并且返回实际的地址,返回的地址会存储在Block的m_offset中。
  • static temptable::deallocate_from:释放内存,根据Block的m_offset就可以释放这一片内存。

这里面包含了实际的分配方法,可以自行参考,需要注意的是MMAP分配内存的时候可能会出现一些包含以开头mysql_temptable的临时文件如下,

inline void *Memory<Source::MMAP_FILE>::fetch(size_t bytes) {
  File f = create_temp_file(file_path, mysql_tmpdir, "mysql_temptable.", mode,
                            UNLINK_FILE, MYF(MY_WME));

下面我们来看看分配的过程。

四、从全局block_pool中获取一个slot

这个block_pool中主要包含了各个线程的第一个temptable block所在的位置,本质是一个容器数组其中每条数据只包含一个block属性,并且为全局共享,lock free的静态变量如下

  • static Lock_free_shared_block_pool<SHARED_BLOCK_POOL_SIZE> shared_block_pool;

其中每一个元素位置叫做slot,当线程需要建立temptable 临时表的时候都会通过类方法去获取一个有效的slot,如下,

temptable::Handler::Handler
m_shared_block(shared_block_pool.try_acquire(thd_thread_id(ha_thd()))),
调用方法
*try_acquire(size_t thd_id)

在线程退出的时候释放最后一个Block(也就是建立的第一个Block),但是其他Block会在临时表使用中或者使用后释放,如下

try_release(size_t thd_id)

获取到虽然获取了Block,但是并没有实际的分配内存。

五、Allocator的初始化和内存分配

当某个线程建立临时表的时候,就需要对Allocator进行初始化,分配器中包含3个重要的元素,

  • m_shared_block:这个就来自前面说的block_pool中获取的某个slot对应的block,这代表的是某个线程第一个block
  • m_state:这里面包含了一个当前分配的block的一个计数器和当前指向的block(current_block)
  • m_table_resource_monitor:当前表的内存统计,这是为了实现参数tmp_table_size所做的。

Allocator实现了2个必须要实现的功能就是内存分配和释放,分别叫做,当分配内存的时候需要调用,

  • temptable::Allocator::allocate:从Block中分配Chunk需要的内存,首先需要查看的m_shared_block是否为空,如果为空则需要分配第一个Block,这个肯定是从OS内存中获取一个1M的空间,如果不是第一分配,可能在第一个Block中存在剩余的空间则不需要再次从OS中获取内存直接分配即可,如果第一个Block分配完了就需要新分配一个Block,并且从OS中获取2M的空间了,并且由current_block指向,接下来可能Chunk需要的内存就在current_block分配了,如果current_block也满了就再分配4M的Block,并且由current_block指向,依次类推。
  • temptable::Allocator::deallocate:从Block中删除Chunk的内存,这部分基本和上面是相反的,先从Block中删除这个Chunk,然后判断Block Chunk的数量,如果为0了则这个Block整体从OS中释放。但是需要注意的是第一个Block的内存不会释放会持续到线程退出如下,
if (m_shared_block && (block == *m_shared_block)) { //如果m_shared_block存在 则不做任何事情,保留最后一个block
      // Do nothing. Keep the last block alive.
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352

推荐阅读更多精彩内容

  • 咱们肯定知道标准库中著名allocator,许多存储器都需要这个东西。我们现在就模仿着写一个,当然肯定没有原来的厉...
    最爱DJ喜羊羊阅读 265评论 0 0
  • 操作系统的物理内存: —— 常说的 “内存条”数据从磁盘中加载到内存后,才能被CPU访问。【操作系统的代码和数据...
    helinyu阅读 935评论 0 1
  • 内存分配器Allocator 容器都依赖一个分配器,扩容和回收内存都通过分配器实现 分配器是负责封装内存管理的对象...
    游戏开发程序员阅读 265评论 0 0
  • 相信大家在学习C语言的时候,malloc是最早遇到的几个方法之一,这里就来深入的了解下,macOS/iOS中用户空...
    码农苍耳阅读 8,012评论 2 14
  • 1 标准接口 张老师在课上讲述了C++内存分配器的标准接口,接口规格如下: 一组typedef:-allocato...
    clamxyz阅读 723评论 0 0