innoDB表空间以及数据页结构

innoDB表空间以及数据页结构(带图详解)

MySQL在启动时,如果存储引擎为innodb,则innodb会初始化其所有的子系统,其中就包括了页面缓冲区子系统,通过函数buf_pool_init来实现。Buffer缓冲池可以有多个实例,可以通过配置文件中的参数innodb_buffer_pool_instances来设置,默认值为1,实现多实例的缓冲池主要是为了提高在数据页放问时的并发度。每个实例的空间大小都是相同的,也就是说系统会将整个配置的缓冲区大小按实例个数平分,然后每个实例各自进行初始化操作。

一个缓冲区实例在代码中用buf_pool_t结构体来描述,这个结构体是用来管理一个缓冲区实例的核心工具,它里面包括了很多信息,主要包括上面提到的LRU链表,用来管理这个实例中所有页面的访问情况;FREE链表,用来存储这个实例中所有空闲的页面;flush_list链表,用来存储所有被修改过且需要刷到文件中的页面;mutex,这个主要用来保护这个缓冲池实例的,因为一个实例只能由一个线程访问;chunks,这个是一个指向这个实例第一个真正内存页面的首地址,所以页面都是连续存储,所以通过这个指针直接就可以访问所有的其它页面。

初始化一个缓冲池实例的内存空间的函数是buf_chunk_init,一个缓冲池实例的内存分布是一块连续的内存空间,这块内存空间中存储了两部分内容,前面是这些数据缓存页面的控制头结构信息(buf_block_t结构),每一个页面对应一个控制头信息,所以控制头信息连续存储在一起,所以控制信息存储完成之后才是真正的缓冲页面,所以一个缓冲池实例实际所用的空间是比配置中指定的要大,因为还需要存储控制头信息的空间,下面表示的是一个缓冲池实例的内存分布情况:

       对于缓冲池中的所有页面,都有一个控制头信息与它对应,从上图可以看出,每一个ctl都控表示了一个属于自己的page的使用情况。初始化实例时当然还需要对每一个控制头信息进行初始化,也就是每一个buf_block_t结构,初始化一个页面控制信息是通过buf_block_init函数实现的,buf_block_t结构中包含了很多信息,主要包括:其对应的页面地址frame;页信息结构buf_page_t,这个结构用来描述一个页面的信息,包括所属表空间的ID号、页面号、被修改所使用的LSNnewest_modificationoldest_modification)、使用状态(现在共有9种状态)等;用来保护这个页面的互斥量mutex;访问页面时对这个页面上的锁lockread/write)等。在初始化完每一个页面之后,需要将每一个页面加入到上面提到的空闲页链表中,因为这些页面现在的状态都是未使用(BUF_BLOCK_NOT_USED)。这些在下面的文章中将会讲到。

Buffer Pool内部组成

Buffer Pool中默认的缓存页大小和在磁盘上默认的页大小是一样的,都是16KB。

为了更好的管理这些在Buffer Pool中的缓存页,mysql 为每一个缓存页都创建了一些所谓的控制信息, 包括该页所属的表空间编号、页号、缓存页在Buffer Pool中的地址、链表节点信息、一些锁信息以及LSN信息

控制块和缓存页是一一对应的,它们都被存放到 Buffer Pool 中,其中控制块被存放到 Buffer Pool 的前边,缓存页被存放到 Buffer Pool 后边,所以整个Buffer Pool对应的内存空间看起来就是这样的:

Buffer pool flush list的工作原理
dirty page如何存在flush链表中?
在flush list中存在的page只能是dirty page,flush list中存在的dirty page是按着oldest_modification时间排序的,当页面访问/修改都被封装为一个mini-transaction,mini-transactin提交的时候,则mini-transaction涉及到的页面就进入了flush链表中,oldest_modification的值越大,说明page越晚被修改过,就排在flush链表的头部,oldest_modification的值越小,说明page越早被修改过,就排在flush链表的尾部,这样当flush链表做flush动作时,从flush链表的尾部开始scan,写出一定数量的dirty page到磁盘,推荐checkpoint点,使恢复的时间尽可能的短。除了flush链表本身的flush操作可以把dirty page从flush链表删除外,lru链表的flush操作也会让dirty page从flush链表删除。

Buffer pool lru list的工作原理
总的来说每当一个新页面被读取buffer pool之后,MySQL数据库InnoDB存储引擎都会判断当前buffer pool的free page是否足够,若不足,则尝试flush LRU链表。
在MySQL 5.6.2之前,用户线程在读入一个page (buf_read_page)、新建一个page(buf_page_create)、预读page(buf_read_ahead_linear) 等等操作时,都会在操作成功之后,调用buf_flush_free_margin函数,判断当前buffer pool是否有足够的free pages,若free pages不足,则进行LRU list flush,释放出足够的free pages,保证系统的可用性。

通过判断当前buf pool中需要flush多少dirty pages,才能够预留出足够的可被替换的页面(free pages or clean pages in LRU list tail)。

Buffer Pool LRU/Flush List flush对比
1).LRU list flush,由用户线程触发(MySQL 5.6.2之前);而Flush list flush由MySQL数据库InnoDB存储引擎后台srv_master线程处理。(在MySQL 5.6.2之后,都被迁移到page cleaner线程中)
2).LRU list flush,其目的是为了写出LRU 链表尾部的dirty page,释放足够的free pages,当buf pool满的时候,用户可以立即获得空闲页面,而不需要长时间等待;Flush list flush,其目的是推进Checkpoint LSN,使得InnoDB系统崩溃之后能够快速的恢复。
3).LRU list flush,其写出的dirty page,需要移动到LRU链表的尾部(MySQL 5.6.2之前版本);或者是直接从LRU链表中删除,移动到free list(MySQL 5.6.2之后版本)。Flush list flush,不需要移动page在LRU链表中的位置。
4).LRU list flush,由于可能是用户线程发起,已经持有其他的page latch,因此在LRU list flush中,不允许等待持有新的page latch,导致latch死锁;而Flush list flush由后台线程发起,未持有任何其他page latch,因此可以在flush时等待page latch。
5).LRU list flush,每次flush的dirty pages数量较少,基本固定,只要释放一定的free pages即可;Flush list flush,根据当前系统的更新繁忙程度,动态调整一次flush的dirty pages数量,量很大。

 

表空间

(1)系统表空间

系统表空间由一个或多个数据文件(ibdata文件)组成。其中包含与InnoDB相关对象有关的元数据(InnoDB 数据字典 data dictionary),以及更改缓冲区change buffer), 双写缓冲区doublewrite buffer)和撤消日志undo logs)的存储区 。

InnoDB 如果表是在系统表空间中创建的,则系统表空间中也包含表的表数据和索引数据。

系统表空间的问题:

在MySQL 5.6.7之前,默认设置是将所有InnoDB表和索引保留 在系统表空间内,这通常会导致该文件变得非常大。因为系统表空间永远不会缩小,所以如果先加载然后删除大量临时数据,则可能会出现存储问题。

在MySQL 5.7中,默认设置为 独立表空间模式,其中每个表及其相关索引存储在单独的 .ibd文件中。此默认设置使使用Barracuda文件格式的InnoDB功能更容易使用,例如表压缩页外列的有效存储以及大索引键前缀(innodb_large_prefix)。

将所有表数据保留在系统表空间或单独的 .ibd文件中通常会对存储管理产生影响。InnoDB在MySQL 5.7.6中引入了通用表空间[11],这些表空间也由.ibd文件表示 。通用表空间是使用CREATE TABLESPACE语法创建的共享表空间。它们可以在MySQL数据目录之外创建,能够容纳多个表,并支持所有行格式的表。

(2)独立表空间

MySQL 5.7中,配置参数:innodb_file_per_table,默认处于启用状态,这是一个重要的配置选项,会影响InnoDB文件存储,功能的可用性和I/O特性等。

启用之后,每个表的数据和索引是存放在单独的.ibd文件中的,而不是在系统表空间的共享ibdata文件中。

优点

  • 您可以更加灵活的选择数据压缩[12]的行格式,如:
    • 默认情况下(innodb_page_size=16K),前缀索引[13]最多包含768个字节。如果开启innodb_large_prefix,且Innodb表的存储行格式为 DYNAMIC 或 COMPRESSED,则前缀索引最多可包含3072个字节,前缀索引也同样适用;
  • TRUNCATE TABLE执行的更快,并且回收的空间不会继续保留,而是让操作系统使用;
  • 可以在单独的存储设备上创建每表文件表空间数据文件,以进行I / O优化,空间管理或备份。请参见 14.6.1.2 Creating Tables Externally

缺点

  • 独立表空间中的未使用空间只能由同一个表使用,如果管理不当,会造成空间浪费;
  • 多个表需要刷盘,只能执行多次fsync,无法合并多个表的写操作,这可能会导致更多的fsync操作总数;
  • mysqld必须为每个表文件空间保留一个打开的文件句柄,如果表数量多,可能会影响性能;
  • 每个表都需要自己的数据文件,需要更多的文件描述符;

即使启用了innodb_file_per_table参数,每张表空间存放的只是数据、索引和插入缓存Bitmap页,其他数据如回滚信息、插入缓冲索引页、系统事务信息、二次写缓冲等还是存放在原来的共享表空间中。

(3)通用表空间

通用表空间使用CREATE TABLESPACE语法创建。

类似于系统表空间,通用表空间是共享表空间,可以存储多个表的数据。

通用表空间比独立表空间具有潜在的内存优势,服务器在表空间的生存期内将表空间元数据保留在内存中。一个通用表空间通常可以存放多个表数据,消耗更少的表空间元数据内存。

数据文件可以放置在MySQL数据目录或独立于MySQL数据目录。

(4)undo表空间

undo表空间包含undo log。

innodb_rollback_segments变量定义分配给每个撤消表空间的回滚段的数量。

undo log可以存储在一个或多个undo表空间中,而不是系统表空间中

在默认配置中,撤消日志位于系统表空间中。SSD存储更适合undo log的I/O模式,为此,可以把undo log存放在有别于系统表空间的ssd硬盘中。

innodb_undo_tablespaces 配置选项控制undo表空间的数量。

(5)临时表空间

由用户创建的非压缩临时表和磁盘内部临时表是在共享临时表空间中创建的。

innodb_temp_data_file_path 配置选项指定零时表空间文件的路径,如果未指定,则默认在 innodb_data_home_dir目录中创建一个略大于12MB 的自动扩展数据文件ibtmp1 。

使用ROW_FORMAT=COMPRESSED属性创建的压缩临时表,是在独立表空间中的临时文件目录中创建的 。

服务启动的时候创建临时表空间,关闭的时候销毁临时表空间。如果临时表空间创建失败,则意味着服务启动失败

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值