zoukankan      html  css  js  c++  java
  • 内存表

    什么是内存表

    内存表,就是放在内存中的表,所使用内存的大小可通过My.cnf中的max_heap_table_size指定,如max_heap_table_size=1024M 内存表满后,会提示数据满错误。 ERROR 1114 (HY000): The table ‘abc’ is full

    内存表的特性

    • 内存表的表定义是存放在磁盘上的,扩展名为.frm, 所以重启不会丢失。
    • 内存表的数据是存放在内存中的,所以重启会丢失数据
    • 内存表支持AUTO_INCREMENT列 (ps, 一些网站资源说不支持)
    mysql> show create table heap_test;
    | heap_test | CREATE TABLE `heap_test` (
      `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
      `a1` char(8) DEFAULT NULL,
      `a2` char(8) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `a1` (`a1`(2))
    ) ENGINE=MEMORY AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
    
    • 内存表表在所有客户端之间共享
    • 在数据库复制时,如果主机当掉,则会在binLog中自动加入delete from [内存表],将slave的数据也删除掉,以保证两边的数据一致性
    • 内存表不支持事务
    • 内存表是表锁,当修改频繁时,性能可能会下降。

    内存表使用场景

    因为内存表有两个主要的特性

    1. 多线程共享,对所有的用户连接是可见的,这一点和临时表完全不同。
    2. 数据存储在内存中,有很快的访问速度。

    所以内存表很适合作为缓存,存储中间结果,和需要频繁访问的数据。

    缺点也很明显,数据存在内存中, 服务器重启后数据会丢失。

    内存表源码说明

    创建内存表的相关参数 internal_tmp_mem_storage_engine

    • set internal_tmp_mem_storage_engine = memory, create_tmp_table 创建出的临时表引擎是 memory (heap 表)
    • set internal_tmp_mem_storage_engine=default , create_tmp_table 创建出的临时表引擎是 TempTable (temporary 表)

    以下代码调研 临时表引擎为 memory

    创建 heap 表

    代码调用栈

    Sql_cmd_create_table::execute -> mysql_create_table -> mysql_create_table_no_lock -> create_table_impl -> rea_create_base_table -> ha_create_table -> handler::ha_create -> ha_heap::create
    

    代码接口:ha_heap::create 这个接口做的事情主要是

    1. Prepare HP_CREATE_INFO (HP_CREATE_INFO 是存储了 heap 表的一些表结构定义信息)
    2. 创建 heap 表,把之前 准备好的 表定义信息, 并且赋值给 HP_SHARE 类的指针 (HP_SHARE 是一个描述每一个内存中的存储文件的类) 初始化 HP_KEYDEF, 和 HP_BLOCK,这两个类作为 HP_SHARE 的类成员变量 2.1 HP_KEYDEF 定义了 其索引描述符 2.2 在create_heap 表内,会调用一个 static 的方法 init_block, HP_BLOCK 是 memory 引擎树型存储结构的描述类

    PS: MEMORY 引擎会将数据记录在一些定长的内存块中,每个内存块中记录数目存储在 HP_BLOCK 类中的

    uint records_in_block{0}; /* Records in one heap-block */

    每条记录的长度存储在 HP_BLOCK 中的 uint recbuffer{0}; / * Length of one saved record * / 给每条记录分配空间的内存长度为 recbuffer + 1, 最后一位是标记位,value = 1 为未删除,value = 0 为删除。

    查询 heap 表

    Memory 有两个全局变量,heap_open_list and heap_share_list.

    • 每一个 HP_SHARE 对应一个物理表
    • 每个表会有一个或者多个表描述类,所以每一个表描述类对应着一个 handler 实例和HP_INFO 实例 (这也是多个线程可以共享 heap 表的原因,每一个线程会有一个自己的表描述类)
    • 每一个handler 实例中有一个 HP_SHARE 的引用
    • hp_share_list 保存所有的 hp_share , hp_open_list 保存所有的 hp_info

    查询时内存表主要会做三个步骤 开表 -> 预算有多少记录->读记录 代码接口: ha_heap::open -> ha_heap::info ->ha_heap::rnd_init

    开表代码调用栈

    open_tables_for_query -> open_tables -> open_and_process_table -> open_table -> open_table_from_share -> handler::ha_open -> ha_heap::open
    

    在 ha_heap::open 这个接口里主要做的事情是

    1. 开表,调用 HP_INFO* heap_open(const char* name, int mode) 根据表名打开对应的 heap 表 这里主要是检查 hp_share_list 中是否有要打开表的 hp_share 信息,如果找到了,则根据找到的 hp_share 信息初始化 新的 hp_info 信息,并将其加入到 hp_open_list 中
    2. 如果没有找到heap 表,create one 通过调用 heap_create

    释放 heap 表

    代码接口: ha_heap::close

    这是一个释放临时表的接口,主要做的事情是从hp_open_list 中删除相应的HP_INFO, 然后–info->s->open_count, 将于 hp_info 关联的 hp_share 中的技术变量 open_count 减1 并且调用 my_free 释放 hp_info 当这个值减为0时候并且 hp_share 的 delete_on_close 为 true, 则调用 hp_free 释放 hp_share

    内存表多实例共享

    我们已知内存表的一个特性就是多实例共享,以下从三个方面描述多实例共享。 1.MySQL 建立连接 connect_a,连接的数据库中有 heap 表, MySQL 建立连接 connect_b, 选择与 connect_a 同一个库

    这种场景下,当connect_a 建立时,系统会调用

      open_table_from_share -> handler::ha_open -> ha_heap::open 
    

    打开内存表,同时给当前的线程创建一个属于自己的表描述类,这个表描述类,对应着一个handler 和 HP_INFO 实例。 当 connect_b 建立时,系统的做法同 connect_a 建立时一样,会为当前的线程创建一个属于自己的表描述类,用于操做内存表

    2.MySQL 建立连接 connect_a,连接的数据库中没有heap表, MySQL 建立连接 connect_b,选择与 connect_a 同一个库,创建内存表, connect_a, 查询该内存表

    这种场景下,当connect_a 建立,系统调用open_table_from_share, 因为库中没有内存表,所以不会调用开启内存表接口的方法,connect_b 建立,也不会调用开启内存表接口的方法,当 connect_b 创建内存表后,系统会调用 创建内存表的接口 ha_heap::create。 表创建好后,connect_a 查询内存表,这个时候系统会为connect_a 的线程创建一个属于自己的表描述类,代码路径是 open_table_from_share -> handler::ha_open -> ha_heap::open

    3.MYSQL 建立连接 connect_a, 连接的数据库中有heap表,MySQL 建立连接 connect_b, 选择与 connect_a 同一个表,connect_b 断开连接,MySQL 建立连接 connect_c, 选择与 connect_a 同一个库,并drop 内存表。connect_a 查询内存表 这种场景下,connect_a 和 connect_b,connect_c 建立后,系统都会为他们创建属于自己线程的表描述类,用于操作内存表。 connect_b 断开连接后,不会对内存表有任何影响。connect_c 调用 drop table 后,系统会调用 ha_heap::close 方法,这个方法具体的描述在上文已经阐述,connect_a 再去查内存表会抛出 ERROR 1146, table doesn’t exist 错误。

    内存表设计思考

    MySQL 数据库内部的多线程机制,提高了系统的吞吐量,并且提供的是插件式的存储引擎结构,这样就使得每一个表都可以设置自己的存储引擎。内存表也作为一种存储引擎可以供系统的表做选择,每一个表都有自己的一个表描述类(TABLE),多个线程中每个线程在处理请求的时候都会有自己的表描述类,每个表描述类都会分配一个自己的handler类实例,这不仅仅适用与内存表,也适用了其他的存储引擎。这样设计内存表,就是提供一种快速访问的存储引擎。大大提供数据的访问速率。

  • 相关阅读:
    PAT顶级 1015 Letter-moving Game (35分)
    PAT顶级 1008 Airline Routes (35分)(有向图的强连通分量)
    PAT顶级 1025 Keep at Most 100 Characters (35分)
    PAT顶级 1027 Larry and Inversions (35分)(树状数组)
    PAT 顶级 1026 String of Colorful Beads (35分)(尺取法)
    PAT顶级 1009 Triple Inversions (35分)(树状数组)
    Codeforces 1283F DIY Garland
    Codeforces Round #438 A. Bark to Unlock
    Codeforces Round #437 E. Buy Low Sell High
    Codeforces Round #437 C. Ordering Pizza
  • 原文地址:https://www.cnblogs.com/igoodful/p/12811095.html
Copyright © 2011-2022 走看看