我的编程空间,编程开发者的网络收藏夹
学习永远不晚

如何理解InnoDB引擎

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

如何理解InnoDB引擎

这期内容当中小编将会给大家带来有关如何理解InnoDB引擎,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

一、综述

innodb的物理文件包括系统表空间文件ibdata,用户表空间文件ibd,日志文件ib_logfile,临时表空间文件ibtmp,undo独立表空间等。

  • 系统表空间是innodb最重要的文件,它记录包括元数据信息,事务系统信息,ibuf信息,double write等关键信息。

  • 用户表空间文件通常分为两类,一类是当innodb_file_per_table打开时,一个用户表空间对应一个文件,另外一种则是5.7版本引入的所谓General Tablespace,在满足一定约束条件下,可以将多个表创建到同一个文件中。

  • 日志文件主要用于记录redo log。innodb在所有数据变更前,先写redo日志。为保证redo日志原子写入,日志通常以512字节的block单位写入。但由于现代文件系统升级,block_size通常设置到了4k,因此innodb也提供了一个选项支持redo日志以4k为单位写入。

  • 临时表空间文件用于存储所有非压缩的临时表,第1~32个临时表专用的回滚段也存放在该文件中。由于临时表的本身属性,该文件在重启时会重新创建。

  • undo独立表空间是innodb的一个可选项,由innodb_undo_tablespaces配置。默认情况下,该值为0,即undo数据是存储在ibdata中。innodb_undo_tablespaces 设置为非0,可使得undo 回滚段分配到不同的文件中,目前开启undo tablespace 只能在install阶段进行。

上述文件除日志文件外,都具有较为统一的物理结构。所有物理文件由页(page 或 block)构成,在未被压缩情况下,一个页的大小为UNIV_PAGE_SIZE(16384,16K)。不同用途的页具有相同格式的页头(38)和页尾(8),其中记录了页面校验值,页面编号,表空间编号,LSN等通用信息,详见下表。所有page通过一定方式组织起来,下面我们分别从物理结构,逻辑结构,文件管理过程来具体了解innodb的文件结构。

如何理解InnoDB引擎

二、文件物理结构

2.1 基本物理结构

innodb 的每个数据文件都归属于一个表空间(tablespace),不同的表空间使用一个唯一标识的space id来标记。值得注意的是,系统表空间ibdata虽然包括不同文件ibdata1, ibdata2…,但这些文件逻辑上是相连的,这些文件同属于space_id为0的表空间。

表空间内部,所有页按照区(extent)为物理单元进行划分和管理。extent内所有页面物理相邻。对于不同的page size,对应的extent大小也不同,对应为:

如何理解InnoDB引擎

通常情况下,extent由64个物理连续的页组成,表空间可以理解为由一个个物理相邻的extent组成。为了组织起这些extent,每个extent都有一个占40字节的XDES entry。利用XDES entry,我们可以方便地了解到该extent每页空闲与否,以及其当前状态。其格式如下:

如何理解InnoDB引擎

所有XDES entry都统一放在extent描述页中,一个extent描述页至多存放256个XDES entry,用于管理其随后物理相邻的256个extent(256*64 = 16384 page),如下图所示所示:

如何理解InnoDB引擎由图可见,每个XDES entry有严格对应的页面,其对应页面上下界可以描述为:

min_scope = extent 描述页 page_no + xdes 编号 * 64max_scope =( extent 描述页 page_no + xdes 编号 * 64 )+63

值得注意的是,其中 page 0的extent描述页还记录了与该table space相关的信息(FSP HEADER),其类型为FIL_PAGE_TYPE_FSP_HDR。其他extent描述页的类型相同,为FIL_PAGE_TYPE_XDES。

2.2 系统数据页

系统表空间(ibdata)不仅存放了SYS_TABLE / SYS_INDEX 等系统表的数据,还存放了回滚信息(undo),插入缓冲索引页(IBUF bitmap),系统事务信息(trx_sys),二次写缓冲(double write)等信息。
innodb中核心的数据都存放在ibdata中的系统数据页中。系统数据页主要包括:FIL_PAGE_TYPE_FSP_HDR, FIL_PAGE_IBUF_BITMAP, FIL_PAGE_TYPE_SYS, IBUF_ROOT_PAGE, FIL_PAGE_TYPE_TRX_SYS, FIL_PAGE_TYPE_SYS, DICT_HDR_PAGE等。

  • FIL_PAGE_TYPE_FSP_HDR/FIL_PAGE_TYPE_XDES
    extent描述页(page 0/16384/32768/… ),上文已述及,故不再展开。

  • FIL_PAGE_IBUF_BITMAP
    ibdata第2个page类型为FIL_PAGE_IBUF_BITMAP,主要用于跟踪随后的每个page的change buffer信息。由于bitmap page的空间有限,同样每隔256个extent Page之后,也会在XDES PAGE之后创建一个ibuf bitmap page。

  • FIL_PAGE_INODE
    ibdata的第3个page的类型为FIL_PAGE_INODE,用于管理数据文件中的segment,每个inode页可以存储FSP_SEG_INODES_PER_PAGE(默认为85)个记录。segment是表空间管理的逻辑单位,每个索引占用2个segment,分别用于管理叶子节点和非叶子节点。关于segment的详细介绍,将在第三节展开。

  • FSP_IBUF_HEADER_PAGE_NO 和 FSP_IBUF_TREE_ROOT_PAGE_NO
    上述两个页分别是Ibdata的第4个page和第5个page。change buffer本质上也是btree结构,其root页固定在第5个page FSP_IBUF_TREE_ROOT_PAGE_NO。由于FSP_IBUF_TREE_ROOT_PAGE_NO中原先用于记录leaf inode entry的字段被用于维护空闲page链表了,因此ibdata需要使用第4页FSP_IBUF_TREE_ROOT_PAGE_NO 来对ibuf进行空间管理。

  • FSP_TRX_SYS_PAGE_NO
    ibdata第6个page的类型为FSP_TRX_SYS_PAGE_NO,记录了innodb重要的事务系统信息,包括持久化的最大事务ID,以及128个rseg(rollback segment)的地址,double write位置等。这128个rseg中,rseg0固定在ibdata中,rseg1-rseg32用于管理临时表,rseg33-rseg128 当未开启undo独立表空间 (innodb undo tablespace = 0)时,仍放在ibdata中,否则放在undo独立表空间中。每个rseg中记录了1024个slot,每个slot也都可对应一个事务,用于管理该事务的undo记录。由于每个slot也需要申请和释放page,因此每个slot也对应一个segment(空间管理逻辑单位)。

  • FSP_DICT_HDR_PAGE_NO
    ibdata第8个page的类型为FSP_DICT_HDR_PAGE_NO,用来存储数据词典表的信息 。该页存储了SYS_TABLES,SYS_TABLE_IDS,SYS_COLUMNS,SYS_INDEXES和SYS_FIELDS的root page,以及当前最大的TABLE_ID/ROW_ID/INDEX_ID/SPACE_ID。当对用户表操作时,需要先从数据字典表中获取到用户表对应的表空间,以及其索引root页的page_no,才能定位到具体数据的位置,对其进行增删改查。(只有拿到数据词典表,才能根据其中存储的表信息,进一步找到其对应的表空间,以及表的聚集索引所在的page no)

  • double write buffer
    innodb使用double write buffer来防止数据页的部分写问题,在写一个数据页之前,总是先写double write buffer,再写数据文件。当崩溃恢复时,如果数据文件中page损坏,会尝试从dblwr中恢复。double write buffer总共128个page,划分为两个block。由于dblwr在安装实例时已经初始化好了,这两个block在Ibdata中具有固定的位置,page64 ~127 划属第一个block,page 128 ~191划属第二个block。

当innodb_file_per_table为off状态时,所有用户表也将和SYS_TABLE / SYS_INDEX 等系统表一样,存储在ibdata中。当开启innodb_file_per_table时,innodb会为每一个用户表建立一个独立的ibd文件。该ibd文件存放了对应用户表的索引数据和插入缓冲bitmap。而该表的回滚数据(undo)仍记录在ibdata中。

三、文件逻辑结构

3.1 基本逻辑结构

innodb为了组织各extent,在表空间的第一个page还维护了三个extent的链表:FSP_FREE、FSP_FREE_FRAG、FSP_FULL_FRAG。分别将extent完全未被使用,部分被使用,完全被使用的Xdes entry串联起来。如下图所示:

如何理解InnoDB引擎

段(segment 或称 inode)是用来管理物理文件的逻辑单位,可以向表空间申请分配和释放page或extent,是构成索引,回滚段的基本元素。为节省空间,每个segment都先从表空间FREE_FRAG中分配32个页(FSEG_FRAG_ARR),当这些32个页面不够使用时。按照以下原则进行扩展:如果当前小于1个extent,则扩展到1个extent满;当表空间小于32MB时,每次扩展一个extent;大于32MB时,每次扩展4个extent。
在为segment分配空闲的extent时,如果表空间FSP_FREE上没有空闲的extent,则会为FSP_FREE重新初始化一些空闲extent。extent的分配类似于实现了一套借还机制。segment向表空间租借extent,只有segment退还该空间时,该extent才能重新出现在FSP_FREE/FSP_FULL_FRAG/FSP_FULL中。
segment内部为了管理起这些分配来的extent。也有三个extent链表:FSEG_FREE、FSEG_NOT_FULL、FSEG_FULL,也分别对应extent完全未被使用,部分被使用,完全被使用的Xdes entry。segment的结构如下图所示

如何理解InnoDB引擎inode entry是用于管理segment的结构,一个inode entry对应一个segment。segment的32个页(FSEG_FRAG_ARR),FSEG_FREE、FSEG_NOT_FULL、FSEG_FULL等信息都记录在inode entry中。inode entry的具体结构如下表所示:

如何理解InnoDB引擎inode entry所在的inode page有可能存放满,因此又通过头page(FIL_PAGE_TYPE_FSP_HDR)中维护了两个inode Page链表FSP_SEG_INODES_FULL和FSP_SEG_INODES_FREE。前者对应没有空闲inode entry的inode page链表,后者对应的至少有一个空闲inode entry的inode page链表,如下图所示:

如何理解InnoDB引擎

3.2 索引

ibd文件中真正构建起用户数据的结构是btree。表中的每一个索引对应一个btree。主键(cluster index)对应btree的叶子节点上记录了行的全部列数据(加上transaction id列及rollback ptr)。当表中无主键时,innodb会为该表每一行分配一个唯一的rowID,并基于它构造btree。如果表中存在二级索引(secondary index),那么其btree叶子节点存储了键值加上cluster index索引键值。
每个btree使用两个Segment来管理数据页,一个管理叶子节点(leaf segment),一个管理非叶子节点(non-leaf segment)。这两个segment的inode entry地址记录在btree的root page中。root page分配在non-leaf segment第一个碎片页上(FSEG_FRAG_ARR)。
当对一个表进行增删改查的操作时,我们首先需要从ibdata的第8页FSP_DICT_HDR_PAGE_NO中load改表的元数据信息,从SYS_INDEXES表中获取该表各索引对应的root page no,进而通过root page对这个表的用户数据btree进行操作。表空间的逻辑结构如下图所示:

如何理解InnoDB引擎

3.3 索引页数据

索引最基本的页类型为FIL_PAGE_INDEX,其结构如下表所示。Index Header中记录了page所在btree层次,所属index ID,page directory槽数等与页面相关的信息。Fseg Header中记录了该index的leaf-segment和non-leaf segment的inode entry,system records包括infimum和supremum,分别代表该页最小、最大记录虚拟记录。page directory是页内记录的索引。btree只能检索到记录所在的page,page内的检索需要使用到通过page directory构建起的二分查找。

如何理解InnoDB引擎

innodb按行存放数据。当前MySQL支持等行格式包括antelope(compact和redundant),和barracuda(dynamic和compressed)。barracuda与antelope主要区别在于其处理行外数据等方式,barracuda只存储行外数据等地址指针,不像antelope一样存放768字节的行前缀内容。以compact行格式为例介绍行格式的具体内容,如下图所示,行由变长字段长度列表、NULL标志位、记录头信息、系统列、用户列组成。记录头信息中存放删除标志、列总数、下行相对偏移等信息、系统列包括rowID、transactionID、rollback pointer等组成。

如何理解InnoDB引擎

四、文件管理过程

下面用精简后的源码来简单介绍innodb文件的管理过程。

4.1 btree的创建过程

btree的创建过程可以概括为:先创建non_leaf segment,利用non_leaf segment的首页(即32个碎片页中第一页)作为root page;然后创建leaf_segment;最后对root page进行必要的初始化。详细过程请参考以下代码:

btr_create(    ulint type,    ulint space,    const page_size_t& page_size,    index_id_t index_id,    dict_index_t* index,    const btr_create_t* btr_redo_create_info,    mtr_t* mtr){            ...        block = fseg_create(space, 0,    PAGE_HEADER + PAGE_BTR_SEG_TOP, mtr);    if (block == NULL) {        return(FIL_NULL);    }        page_no = block->page.id.page_no();        frame = buf_block_get_frame(block);                if (!fseg_create(space, page_no,            PAGE_HEADER + PAGE_BTR_SEG_LEAF, mtr)) {                        btr_free_root(block, mtr);            return(FIL_NULL);        }                page_zip = buf_block_get_page_zip(block);        if (page_zip) {                        page = page_create_zip(block, index, 0, 0, NULL, mtr);        } else {                        page = page_create(block, mtr,            dict_table_is_comp(index->table),            dict_index_is_spatial(index));        }                btr_page_set_index_id(page, page_zip, index_id, mtr);                btr_page_set_next(page, page_zip, FIL_NULL, mtr);        btr_page_set_prev(page, page_zip, FIL_NULL, mtr);                        return(page_no);}

4.2 segment的创建过程

segment的创建过程比较简单:先在inode page中为segment分配一个inode entry,然后再inode entry上进行初始化,更新space header里的最大segment id,即可。需要注意的是:当传入的page 为0 时,意味着要创建一个独立的segment,需要将当前的inode entry地址记录在段首page中,并返回;当传入的page非0时,segment需要在指定的page的指定位置记录下当前的inode entry地址。详细过程请参考代码:

buf_block_t*fseg_create_general(    ulint    space_id,    ulint    page,        ulint    byte_offset,     ibool    has_done_reservation,     mtr_t*    mtr)    {        ...        if (page != 0) {                block = buf_page_get(page_id_t(space_id, page), page_size,                     RW_SX_LATCH, mtr);        header = byte_offset + buf_block_get_frame(block);    }            space_header = fsp_get_space_header(space_id, page_size, mtr);    inode = fsp_alloc_seg_inode(space_header, mtr);    if (inode == NULL) {        goto funct_exit;    }        seg_id = mach_read_from_8(space_header + FSP_SEG_ID);    mlog_write_ull(space_header + FSP_SEG_ID, seg_id + 1, mtr);        mlog_write_ull(inode + FSEG_ID, seg_id, mtr);    mlog_write_ulint(inode + FSEG_NOT_FULL_N_USED, 0, MLOG_4BYTES, mtr);        flst_init(inode + FSEG_FREE, mtr);    flst_init(inode + FSEG_NOT_FULL, mtr);    flst_init(inode + FSEG_FULL, mtr);        mlog_write_ulint(inode + FSEG_MAGIC_N, FSEG_MAGIC_N_VALUE,             MLOG_4BYTES, mtr);    for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {        fseg_set_nth_frag_page_no(inode, i, FIL_NULL, mtr);    }        if (page == 0) {        block = fseg_alloc_free_page_low(space, page_size,                         inode, 0, FSP_UP, RW_SX_LATCH,                         mtr, mtr#ifdef UNIV_DEBUG                         , has_done_reservation#endif                          );        header = byte_offset + buf_block_get_frame(block);        mlog_write_ulint(buf_block_get_frame(block) + FIL_PAGE_TYPE,                 FIL_PAGE_TYPE_SYS, MLOG_2BYTES, mtr);    }        mlog_write_ulint(header + FSEG_HDR_OFFSET,             page_offset(inode), MLOG_2BYTES, mtr);    mlog_write_ulint(header + FSEG_HDR_PAGE_NO,             page_get_page_no(page_align(inode)),             MLOG_4BYTES, mtr);    mlog_write_ulint(header + FSEG_HDR_SPACE, space_id, MLOG_4BYTES, mtr);funct_exit:    DBUG_RETURN(block);}

4.3 extent的分配过程

表空间分配extent的逻辑比较简单,直接查询FSP_FREE上有没有剩余的extent即可,没有的话就为FSP_FREE重新初始化一些extent。详细逻辑如下:

staticxdes_t*fsp_alloc_free_extent(    ulint            space_id,    const page_size_t&    page_size,    ulint            hint,    mtr_t*            mtr){        ...        header = fsp_get_space_header(space_id, page_size, mtr);        descr = xdes_get_descriptor_with_space_hdr(        header, space_id, hint, mtr, false, &desc_block);    fil_space_t*    space = fil_space_get(space_id);        if (descr && (xdes_get_state(descr, mtr) == XDES_FREE)) {            } else {                first = flst_get_first(header + FSP_FREE, mtr);        if (fil_addr_is_null(first)) {            fsp_fill_free_list(false, space, header, mtr);            first = flst_get_first(header + FSP_FREE, mtr);        }                if (fil_addr_is_null(first)) {            return(NULL);            }        descr = xdes_lst_get_descriptor(            space_id, page_size, first, mtr);    }        flst_remove(header + FSP_FREE, descr + XDES_FLST_NODE, mtr);    space->free_len--;    return(descr);}

当为segment分配extent时稍微复杂一些:先检查FSEG_FREE中是否有剩余的extent,如果没有再用fsp_alloc_free_extent从表空间中申请extent。在第二种情况下,FSEG_FREE中的extent不足,因此还会进一步尝试为FSEG_FREE分配更多extent。详细过程如下:

staticxdes_t*fseg_alloc_free_extent(    fseg_inode_t*        inode,    ulint            space,    const page_size_t&    page_size,    mtr_t*            mtr){        ...        if (flst_get_len(inode + FSEG_FREE) > 0) {        first = flst_get_first(inode + FSEG_FREE, mtr);        descr = xdes_lst_get_descriptor(space, page_size, first, mtr);    } else {        descr = fsp_alloc_free_extent(space, page_size, 0, mtr);        if (descr == NULL) {            return(NULL);        }                seg_id = mach_read_from_8(inode + FSEG_ID);        xdes_set_state(descr, XDES_FSEG, mtr);        mlog_write_ull(descr + XDES_ID, seg_id, mtr);        flst_add_last(inode + FSEG_FREE, descr + XDES_FLST_NODE, mtr);                fseg_fill_free_list(inode, space, page_size,                    xdes_get_offset(descr) + FSP_EXTENT_SIZE,                    mtr);    }    return(descr);}

4.4 page的分配过程

表空间page的分配过程如下:先查看hint_page所在的extent是否适合分配空闲页面,不适合的话,则尝试从FSP_FREE_FRAG链表中寻找空闲页面。如果FSP_FREE_FRAG为空,则新分配一个extent,将其添加到FSP_FREE_FRAG中,并在其中分配空闲页面。

static MY_ATTRIBUTE((warn_unused_result))buf_block_t*fsp_alloc_free_page(    ulint            space,    const page_size_t&    page_size,    ulint            hint,    rw_lock_type_t        rw_latch,    mtr_t*            mtr,    mtr_t*            init_mtr){        ...        header = fsp_get_space_header(space, page_size, mtr);    descr = xdes_get_descriptor_with_space_hdr(header, space, hint, mtr);        if (descr && (xdes_get_state(descr, mtr) == XDES_FREE_FRAG)) {            } else {                first = flst_get_first(header + FSP_FREE_FRAG, mtr);                if (fil_addr_is_null(first)) {            descr = fsp_alloc_free_extent(space, page_size,                              hint, mtr);            if (descr == NULL) {                                return(NULL);            }            xdes_set_state(descr, XDES_FREE_FRAG, mtr);            flst_add_last(header + FSP_FREE_FRAG,                      descr + XDES_FLST_NODE, mtr);        } else {            descr = xdes_lst_get_descriptor(space, page_size,                            first, mtr);        }                hint = 0;    }        free = xdes_find_bit(descr, XDES_FREE_BIT, TRUE,                 hint % FSP_EXTENT_SIZE, mtr);    if (free == ULINT_UNDEFINED) {        ut_print_buf(stderr, ((byte*) descr) - 500, 1000);        putc('', stderr);        ut_error;    }    page_no = xdes_get_offset(descr) + free;            fsp_alloc_from_free_frag(header, descr, free, mtr);        return(fsp_page_create(page_id_t(space, page_no), page_size,                   rw_latch, mtr, init_mtr));}

为了能够使得segment内逻辑上相邻的节点在物理上也尽量相邻,尽量提高表空间的利用率,在segment中分配page的逻辑较为复杂。详细过程如下所述:

staticbuf_block_t*fseg_alloc_free_page_low(    fil_space_t*        space,    const page_size_t&    page_size,    fseg_inode_t*        seg_inode,    ulint            hint,    byte            direction,    rw_lock_type_t        rw_latch,    mtr_t*            mtr,    mtr_t*            init_mtr#ifdef UNIV_DEBUG    , ibool            has_done_reservation#endif ){        ...        reserved = fseg_n_reserved_pages_low(seg_inode, &used, mtr);        space_header = fsp_get_space_header(space_id, page_size, mtr);    descr = xdes_get_descriptor_with_space_hdr(space_header, space_id,                           hint, mtr);    if (descr == NULL) {                hint = 0;        descr = xdes_get_descriptor(space_id, hint, page_size, mtr);    }            if ((xdes_get_state(descr, mtr) == XDES_FSEG)        && mach_read_from_8(descr + XDES_ID) == seg_id        && (xdes_mtr_get_bit(descr, XDES_FREE_BIT,                 hint % FSP_EXTENT_SIZE, mtr) == TRUE)) {take_hinted_page:                ret_descr = descr;        ret_page = hint;        goto got_hinted_page;            } else if (xdes_get_state(descr, mtr) == XDES_FREE           && reserved - used < reserved / FSEG_FILLFACTOR           && used >= FSEG_FRAG_LIMIT) {                ret_descr = fsp_alloc_free_extent(            space_id, page_size, hint, mtr);        xdes_set_state(ret_descr, XDES_FSEG, mtr);        mlog_write_ull(ret_descr + XDES_ID, seg_id, mtr);        flst_add_last(seg_inode + FSEG_FREE,                  ret_descr + XDES_FLST_NODE, mtr);                fseg_fill_free_list(seg_inode, space_id, page_size,                    hint + FSP_EXTENT_SIZE, mtr);        goto take_hinted_page;            } else if ((direction != FSP_NO_DIR)           && ((reserved - used) < reserved / FSEG_FILLFACTOR)           && (used >= FSEG_FRAG_LIMIT)           && (!!(ret_descr              = fseg_alloc_free_extent(                  seg_inode, space_id, page_size, mtr)))) {                ret_page = xdes_get_offset(ret_descr);        if (direction == FSP_DOWN) {            ret_page += FSP_EXTENT_SIZE - 1;        }    } else if ((xdes_get_state(descr, mtr) == XDES_FSEG)           && mach_read_from_8(descr + XDES_ID) == seg_id           && (!xdes_is_full(descr, mtr))) {                ret_descr = descr;        ret_page = xdes_get_offset(ret_descr)            + xdes_find_bit(ret_descr, XDES_FREE_BIT, TRUE,                    hint % FSP_EXTENT_SIZE, mtr);    } else if (reserved - used > 0) {                fil_addr_t    first;        if (flst_get_len(seg_inode + FSEG_NOT_FULL) > 0) {            first = flst_get_first(seg_inode + FSEG_NOT_FULL,                           mtr);        } else if (flst_get_len(seg_inode + FSEG_FREE) > 0) {            first = flst_get_first(seg_inode + FSEG_FREE, mtr);        } else {            return(NULL);        }        ret_descr = xdes_lst_get_descriptor(space_id, page_size,                            first, mtr);        ret_page = xdes_get_offset(ret_descr)            + xdes_find_bit(ret_descr, XDES_FREE_BIT, TRUE,                    0, mtr);    } else if (used < FSEG_FRAG_LIMIT) {                buf_block_t* block = fsp_alloc_free_page(            space_id, page_size, hint, rw_latch, mtr, init_mtr);        if (block != NULL) {                        n = fseg_find_free_frag_page_slot(seg_inode, mtr);            fseg_set_nth_frag_page_no(                seg_inode, n, block->page.id.page_no(),                mtr);        }        return(block);    } else {                ret_descr = fseg_alloc_free_extent(seg_inode,                           space_id, page_size, mtr);        if (ret_descr == NULL) {            ret_page = FIL_NULL;            ut_ad(!has_done_reservation);        } else {            ret_page = xdes_get_offset(ret_descr);        }    }        if (ret_page == FIL_NULL) {        return(NULL);    }got_hinted_page:        if (ret_descr != NULL) {        fseg_mark_page_used(seg_inode, ret_page, ret_descr, mtr);    }        return(fsp_page_create(page_id_t(space_id, ret_page), page_size,                   rw_latch, mtr, init_mtr));}

innodb的文件结构由自下而上包括page(页),extent(区),segment(段),tablespace(表空间)等几个层次。page是最基本的物理单位,所有page具有相同的页首和页尾;extent由通常由连续的64个page组成,tablespace由一个个连续的extent组成;段是用来管理物理文件的逻辑单位,可以向表空间申请分配和释放page 或 extent,是构成索引,回滚段的基本元素;表空间是一个宏观概念,当innodb_file_per_table为ON时一个用户表对应一个表空间。

上述就是小编为大家分享的如何理解InnoDB引擎了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

如何理解InnoDB引擎

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何理解InnoDB引擎

这期内容当中小编将会给大家带来有关如何理解InnoDB引擎,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。一、综述innodb的物理文件包括系统表空间文件ibdata,用户表空间文件ibd,日志文件ib_l
2023-05-31

详解MySQL InnoDB存储引擎的内存管理

存储引擎之内存管理 在InnoDB存储引擎中,数据库中的缓冲池是通过LRU(Latest Recent Used,最近最少使用)算法来进行管理的,即最频繁使用的页在LRU列表的最前段,而最少使用的页在LRU列表的尾端,当缓冲池不能存放新读取
2022-05-13

如何将MySQL中的MyISAM存储引擎转换为InnoDB存储引擎?

要将MyISAM引擎转换为InnoDB,我们可以使用ALTER命令。现在让我们借助引擎MyISAM 创建一个表。mysql> create table MyISAMToInnoDBDemo-> (-> id int,-> Name varc
2023-10-22

如何理解docker日志引擎

这篇文章将为大家详细讲解有关如何理解docker日志引擎,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。docker原生支持众多的日志引擎,适用于各种不同的应用场景,小编对其作一个简单的说明。
2023-06-04

MySql InnoDB存储引擎之Buffer Pool运行原理讲解

目录1. 前言2. Buffer Pool2.1 Buffer Pool结构2.2 Free链表2.3 缓冲页哈希表2.4 Flush链表2.5 LRU链表2.6 多个实例2.7 Buffer Pool状态信息3. 总结1. 前言我们已经
2023-01-04

MySql InnoDB存储引擎之Buffer Pool运行原理讲解

缓冲池是用于存储InnoDB表,索引和其他辅助缓冲区的缓存数据的内存区域。缓冲池的大小对于系统性能很重要。更大的缓冲池可以减少磁盘I/O来多次访问同一表数据。在专用数据库服务器上,可以将缓冲池大小设置为计算机物理内存大小的百分之80
2023-01-04

MySql数据库InnoDB引擎ACID的实现原理

那么数据库是如何实现这四大特性的呢? 本文以MySQL的InnoDB引擎为例,详细分析ACID四大事务特性的实现原理; 一、原子性(Atomicity),原子性的特点是一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部
MySql数据库InnoDB引擎ACID的实现原理
2015-09-05

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录