【数据库设计与实现】第1章:空间管理与数据布局

简介: 空间管理与数据布局 设计原则数据库设计数据布局和空间管理应当考虑哪些因素呢?又有哪些设计原则?本章先提出问题,看有哪些因素要考虑,然后选取Oracle和MySQL这2款最流行的数据库,看看它们是如何进行空间管理的。我们先看看有哪些因素要考虑:数据最终要反映到持久设备上,空间管理首先要考虑持久设备(如磁盘)的物理特性,考虑如何组织数据才能最大化地发挥存储设备的效率;如何协调平衡多样化的数据,如超大的
空间管理与数据布局  
设计原则

数据库设计数据布局和空间管理应当考虑哪些因素呢?又有哪些设计原则?本章先提出问题,看有哪些因素要考虑,然后选取Oracle和MySQL这2款最流行的数据库,看看它们是如何进行空间管理的。我们先看看有哪些因素要考虑:

  • 数据最终要反映到持久设备上,空间管理首先要考虑持久设备(如磁盘)的物理特性,考虑如何组织数据才能最大化地发挥存储设备的效率;
  • 如何协调平衡多样化的数据,如超大的单表和数据庞大的小表,持久数据和临时数据,系统管理数据和用户数据,索引数据和非索引数据等等;
  • 如何平衡数据的逻辑相关性和逻辑独立性,逻辑相关性要求将逻辑上相关的数据聚集在一起,提高联合访问的效率,而逻辑独立性又要求将逻辑上独立的数据独立存放,提高数据失效、迁移、变更的效率;
  • 如何平衡并发度和空间利用率,并发度要求尽可能将所有相关的持久化设备都调动起来,将设备能力尽可能均衡地用满,而空间利用率则要求尽可能紧凑地利用空间,避免空间区域和碎片,提高扫描效率;
Oracle 设计原理
Datafile 空间管理

Oracle的tablespace由若干个datafile组成,datafile以extent为单位进行空间的申请和释放。可以这么说,datafile是空间的提供者,而segment是空间的使用者,两者之间的交互语言就是extent和block。

图1.2-1 Block结构

 

表1.2-1 Cache Layer结构

域

长度

含义

type

1

本Block的类型,可以取值:

  • 0x01 undo segment header
  • 0x02 undo block
  • 0x03 save undo header block
  • 0x04 save undo data block
  • 0x05 data segment header block(temp, index, data)
  • 0x06 KTB managed data block(with ITL)
  • 0x07 temp table data block(no ITL)
  • 0x08 sort key
  • 0x09 sort run
  • 0x0a segment free list block
  • 0x0b data file header
  • 0x0c data segment header with FLG blcks
  • 0x0e unlimited undo segment header
  • 0x0f unlimited save undo segment header
  • 0x10 unlimited data segment header
  • 0x11 unlimited data segment header with FLG blocks
  • 0x12 extent map block
  • 0x17 bitmapped segment header
  • 0x1d KTFB bitmapped file space header
  • 0x1e KTFB bitmapped file space bitmap
  • 0x20 first level bitmap block
  • 0x21 second level bitmap block
  • 0x22 third level bitmap block
  • 0x23 pagetable segment header block
  • 0x24 pagetable extent map block
  • 0x25 system managed undo extent map block

frmt

1

格式,标识不同的block版本

spare1/2

2

保留

rdba

4

相对block地址,前10个bit为相对文件id,后22个bit为block号,可见1个tablespace可以有1023个datafile,每个datafile可以有4M个block

scn

6

本block最近一次更新时的scn

seq

1

如果更新本block时scn相同,则seq加1

flg

1

组合标志位:

  • 0x01  新 block , data 部分为空;
  • 0x02 delayed logging change advance SCN/SEQ
  • 0x04  校验 CHECKSUM
  • 0x08  临时 block

chkval

2

flg的CHECKSUM标志为真时,写入前对block计算CHECKSUM,并填入本域,后继读出时会再次计算并检查,防止块损坏

spare3

2

保留

表1.2-2 footer结构

域

长度

含义

seq

1

等于cache layer中的seq

type

1

等于cache layer中的type

filter

2

数据文件:等于cache layer中scn的末尾2个byte

控制文件:control seq

block是数据管理的基本单元,在讨论datafile空间管理方式之前,我们先讨论一下block。如图1.2-1所示,所有的block都有3个部分组成:

  • Cache layer :定义 block 的总体信息,详情如表 1.2-1 所示;
  • Data : block 中存放的内容,具体内容随 cache layer 中的 type 不同而不同;
  • Footer :如果 block 发生了更改, scn 和 seq 至少有一个会变动,通过比较 footer 和 cache layer 中的对应部分就可以检测块损坏(如写入磁盘时,写入一半掉电的情况),当然 cache layer 中还有 chkval ,如果启用可以提供更强的校验能力;

图1.2-2 datafile文件结构

 

了解了block基本文件结构之后,我们开始讨论datafile是如何提供空间的。如图1.2-2所示,datafile由Data File Header、KTFB Bitmapped File Space Header、KTFB Bitmapped File Space Bitmap和Data组成,其中Data File Header、KTFB Bitmapped File Space Header、KTFB Bitmapped File Space Bitmap为文件保留区,保留区占用的block数随db_block_size的变化而变化(如2KB保留32个block,4KB保留16个block,8KB保留8个block,16KB和32KB保留4个block),下面详细看下各个部分的含义:

  • Data File Header :描述本 datafile 的总体信息, Cache Layer.type=0x0b , block size 为 8KB 时占用 2 个 block ,详情见表 1.2-3 ;
  • KTFB Bitmapped File Space Header :描述 bitmap 的总体信息, Cache Layer.type=0x1d , block size 为 8KB 时占用 1 个 block ,详情见表 1.2-4 ;
  • KTFB Bitmapped File Space Bitmap :完整的 bitmap ,用于描述本 datafile 中各个 extent 的详细情况, Cache Layer.type=0x1e , block size 为 8KB 时占用 5 个 block ,详情见表 1.2-5 ;
  • Data :用于存放具体数据;

表1.2-3 Data File Header部分关键信息

域

含义

dbid

本文件归属的db id

dbname

本文件归属的db name

tablespace

本文件归属的tablespace

file number

本文件的文件编号(dba、uba等地址中都会含文件编号)

prev file

归属于同1个tablepace的前1个datafile

file size

本文件的大小

file type

本文件的类型,3表示数据文件

blksize

block的大小

checkpoint

checkpoint系统信息

backup

backup系统信息

表1.2-4 KTFB Bitmapped File Space Header部分关键信息

域

含义

RelFno

相对文件编号

Unit

单元数,即bitmap中每个bit位代表的block数

Size

本datafile包含的block数

AutoExtent

对应于create tablespace语法

MaxSize

对应于create tablespace语法

tail

本文件最后1个block

first

本文件第一个处于free状态的unit地址

free

free状态的unit数量

表1.2-5 KTFB Bitmapped File Space Bitmap部分关键信息

域

含义

beginblock

存放data的block地址,前面的区域存放Data File   Header、KTFB Bitmapped File Space Header、KTFB Bitmapped File Space Bitmap

flag

0永久文件,1临时文件

first

本block中第1个可用的unit位置

free

本block中free的unit数量

map

1个bit代表1个unit,0表示free,1表示used

在KTFB Bitmapped File Space Bitmap Block中,对于统一尺寸的extent来说,unit等于extent,即1个bit位代表1个extent。对于自动管理来说,extent的大小是变化的,1个bit位代表1个最小尺寸的extent。由于大extent一定是小extent的整数倍,所以unit将最小exent作为基准,即1个bit位代表64K。当extent的实际大小是1M、8M、64M时就用多个bit位来表示,如1M的extent就用连续的16个bit来标识。

从空间利用率来看,由于某个extent只能属于某个特定的segment,所以小extent空间利用率高,大extent空间利用率低。例如当extent的大小为64M时,表哪怕仅使用了1个字节,也为它分配1个64M的extent,这64MB空间完全属于这个表,其它表无法使用。从性能的角度来看,大的extent空间比较连续,非常适合全表扫描类操作。Oracle的动态管理方式在空间利用率、性能之间做了平衡。

Segment 空间机制

图1.2-3 segment 3级bitmap管理机制

 

通过上节我们知道,datafile是通过bitmap管理表空间的extent分配情况。segment作为实际使用者,需要更加精确地知道本segment中各block的使用情况。如图1.2-3所示,segment是通过3级位图机制对block进行精细化管理的:

  • L1 BMB ( BitMap Block ):详细描述了其管理的各 data block 的空间使用情况,并有指向其归属的 L2 BMB 指针,详细情况见表 1.2-6 ;
  • L2 BMB :记录了其管理的所有 L1 BMB 地址,并有指向其归属的 L3 BMB 指针,详细情况见表 1.2-7 ;
  • L3 BMB :记录了其管理的所有 L2 BMB 地址,并有指向其归属的 segment header block 指针,实际上 segment header block 本身就是一个特殊的 L3 BMB ;

表1.2-6 L1 BMB部分关键信息

一级域

二级域

含义

parent dba

其归属的L2 BMB地址

unformatted

未格式化的block数量

total

MAP中block总数

first useful block

MAP中第1个可用的block

MAP(*n)

offset

extent的地址,即extent中首个block的地址

length

extent中block的数量

bits

标识extent中各个block的状态,每个block的状态占用4个bit位:

  • 0000 : unformatted ,尚未格式化;
  • 0001 : full ,已满;
  • 0010 : <25% ,空闲空间 <25% ;
  • 0011 : 25-50% ,空闲空间 25-50% ;
  • 0100 : 50-75% ,空闲空间 50-75% ;
  • 0101 : >75 ,空闲空间 >75% ;
  • 0110 : metadata ,元数据 block , segment header block 、 BMB 都是 metadata block ;

表1.2-7 L2 BMB部分关键信息

一级域

二级域

含义

parent dba

其归属的L3 BMB地址或segment   header block地址

number

MAP中L1 BMB的数量

nfree

MAP中尚有空闲空间的L1 BMB数量

ffree

MAP中已满的L1 BMB数量

MAP(*n)

dba

L1 BMB的地址

free

5:空闲;

0:FULL;

随着申请的data block越来越多,需要不断地申请L1 BMB、L2 BMB和L3 BMB进行管理。实际上,通过L1 BMB和L2 BMB已经能够管理非常庞大的data block数量,使用L3 BMB的情况非常少见(当然data segment header block本身就是一个特殊的L3 BMB)。在高并发时,存在多个L1 BMB可用,分散了争用,提高了并发性,可以获得非常高的性能:

  • 通过 data segment header block 找到第 1 个含空闲空间的 L2 BMB ( L2 Hint for inserts );
  • 对操作进程 PID 做 HASH 运算,得到随机数 N ,在 L2 BMB 中找到第 N 个 L1 BMB;
  • 对操作进程 PID 做 HASH 运算,得到随机数 M ,在 L1 BMB 中找到第 M 个有空闲空间的 data block;
  • 向找到的 data block 插入数据;

实际插入算法更加复杂,既要考虑一定的随机性和离散性,避免并发插入在特定的data block和BMB上竞争,又要考虑一定的聚集性,提高空间利用率和数据扫描效率(实际上会优先插入HWM以下的data block),最终算法在两者之间进行平衡。

图1.2-4 data segment header block结构

 

表1.2-8 Extent Control Header部分关键信息

一级域

二级域

含义

extents

本segment的extent数量

blocks

本segment的block数量(不含segment header block)

High HWM

HWM

High HWM的地址

ext

High HWM在哪个extent上

blk

High HWM在该extent的哪个bock上

ext size

High HWM所在的extent有多大

blocks below

本segment中处于High   HWM下的block数量

Low HWM

HWM

Low HWM的地址

ext

Low HWM在哪个extent上

blk

Low HWM在该extent的哪个bock上

ext size

Low HWM所在的extent有多大

blocks below

本segment中处于Low HWM下的block数量

L1 BMB for high HWM block

High HWM所在的block归属的L1 BMB

L1 BMB for low HWM block

Low HWM所在的block归属的L1 BMB

nl2

本segment中L2 BMB的数量

blksz

block的大小

L2 array start offset

L2 Bitmap Map在segment header block中的偏移

First level3 BMB

L3 BMB链表头

Last level3 BMB

L3 BMB链表尾

L2 hint for inserts

从本L2 BMB开始查找空闲空间供插入

Last level2 BMB

最新的L2 BMB

Last level1 BMB

最新的L1 BMB

表1.2-9 Extent Map和Auxiliary Map部分关键信息

一级域

二级域

含义

next

下一个map block的地址,随着extent数量的不断增长,data segment header block中就不够存放,这时会申请独立的map(type=0x12   extent map block)存放多出来的map

extents

extent的数量

Extent Map(*n)

dba

extent的地址

length

extent的block数量

Auxiliary Map(*n)

extent index

数组下标,虚拟列

L1 dba

管理该extent的L1 BMB地址,1个L1 BMB可以管理多个extent,1个extent只能归属于1个L1 BMB管理

data dba

该extent中首个data   block的地址,extent中可能有L1 BMB、L2 BMB、L3 BMB、Extent   Map Block、Segment Header Block等等,这些Metadata Block一般处于extent的头部

如图1.2-4所示,data segment header block包含如下几个关键部分:

  • 通过 extent control header 、 L3 BMB 、 L2 BMB 、 L1 BMB 就可以知道所有 data block 的空间使用情况、 HWM 情况,然后进行高并发的插入操作;
  • 通过 extent control header 、 extent map 、 auxiliary map 就可以知道所有数据的分布情况、 HWM 情况,进行高效的数据扫描,即从 extent map 起始 extent 开始扫描,扫描至 Low HWM ,对于 Low HWM 和 high HWM 之间的 block ,需要参考 L1 BMB ,以避开尚未格式化的 block ;

图1.2-5 extent内block分布示例

 

最后再看一下extent内的block分布情况,不管block是何种类型,最终都要落到extent内,接受以extent为单位的连续空间管理。图1.2-5给出了extent内block的分布示例,如果extent内存在metadata类型的block,一般都存放在extent的头部。extent在使用和分配上存在如下规律:

  • 延迟分配,建表时不分配 extent ,只有在实际插入数据时才会分配 extent ;
  • 非统一大小时, extent 的大小是动态变化的(以 block size 等于 8K 为例):
    • 0-15 extent ,每个 extent 包括 8 个 block ,此时 segment 总大小 <=1M ,每个 L1 BMB 可以管理 16 个 block ;
    • 16-79 extent ,每个 extent 包括 128 个 block ,此时 segment 总大小 <=32M ,每个 L1 BMB 可以管理 64 个 block ;
    • 80-199 extent ,每个 extent 包括 1024 个 block ,此时 segment 总大小 <=1G ,每个 L1 BMB 可以管理 256 个 block ;
    • >199 extent ,每个 extent 包括 8192 个 block ,此时 segment 总大小 >1G ,每个 L1 BMB 可以管理 1024 个 block ;
  • 有些情况下 1 个 L1 BMB 会管理多个 extent ,所以有的 extent 中没有 L1 BMB ,有些情况下 1 个 L1 BMB 无法将单个 extent 中的所有 block 都管理起来,此时 extent 中就会有多个 L1 BMB ;
  • 当 tablespace 由多个 datafile 组成时, extent 分片会考虑一定的均衡性, segment 会分布在多个 datafile 中,但单个 extent 不会跨 datafile ;
Data Block 空间机制

图1.2-6 data block结构

 

表1.2-10 Table Directory部分关键信息

域

长度

含义

flag

1

N=pctfree hit(clusters)

F=don’t put on free list

K=flushable cluster keys

ntab

1

本data block涉及的table数,只有clusters才可能大于1

nrow

2

本data block中的记录数

frre

2

空闲行的位置,初始为0,-1表示满了

fsbo

2

Free space的起始偏移

fseo

2

Free space的结尾偏移

avsp

2

Data block中空闲空间大小

tosp

2

所有事务提交后总可用空间大小

表1.2-11 Row Directory部分关键信息

域

长度

含义

offs

1

clusters表时有效

nrow

1

第1个表的行数

rowoff*n

2

Array,每行记录占1个位置,记录改行记录在Row Data中位置。记录在Row   Directory中的下标是RowID组成部分之一

RowId=Seg/Obj Number + File Number + Block Number + Row Number,其中Row Number就是Row   Directory中的下标

表1.2-10 Row Data部分关键信息

域

长度

含义

fb

1

K=cluster key

C=cluster table member

H=head piece of row

D=deleted row

F=first data piece

L=last data piece

P=first column continues from previous piece

N=last column continues in next piece

  • 删除行时并不立刻删除,而是打上 D 标签,延后删除;
  • 行链接、行迁移,所以需要 H/F/L/P/N 等标签;

lb

1

本行记录被ITL中的哪个事务给锁住了

cc

1

记录的列数,超过255列就需要行链接

data

var

数据,每列数据由length+data组成,length的情况:

  • 0xff 表示 number 类型取值为 null ,没有 data 部分;
  • Length<=250 字节, length 占用 1 个字节;
  • Length>250 字节, length 占用 3 个字节( block 本身的大小不会超过 64K ),再长就采用行链接;

如图1.2-6所示,data block和数据存储强相关的部分主要包括Row Directory和Row Data,Oracle的data block设计有如下特点:

  • row directory :行目录, array 型结构,每行记录占用 2 个字节,指向该行记录在 Row Data 中的位置。 array 的下标是 RowID 的组成部分,所以某行记录一旦落到 block 中,其在 row directory 中的位置就不能改变( row data 是可以被整理的),因为 index 、行迁移、行链接等都是通过 RowID 来定位的;
  • row data :每行记录的头部大小应尽可能地小,从而提高空间利用率。 fb 、 lb 、 cc 仅占用 3 个字节,每列的 length 部分也是变长的,且对于取值为 null 的 number 类型列,直接通过 length=0xff 来表示,不再占用 data 空间;
  • 随着行记录的增加, row directory 从上向下增长, row data 从下向上增长;
  • data block 如果写入过满,将来 update 操作很可能产生较多行迁移,影响查询性能,为此通过 PCTFree 参数定义插入时预留多少空间出来,用于将来的 update ( pctfree 对 data block ,以及 B 树的 leaf block 有效,但对 B 树的 root block 和 branch block 无效);
数据布局

Oracle中有system tablesapce、sysaux tablespace、temporary tablespace、undo tablespace和permanent tablespace,其存放的数据分别如下:

  • system tablespace :存放数据字典;
  • sysaux tablespace :存放工具相关对象信息;
  • temporary tablespace :存放临时数据,以及在内存不足时作为诸多活动的辅助空间(如排序);
  • undo tablespace :存放 undo 日志(前像);
  • permanent tablespace :存放用户数据;

应用可以根据实际情况创建多个temporary tablespace、undo tablespace和permanent tablespace。同时还可以设置bigfile属性,以容纳更大的数据量。每个tablespace都按照segment进行管理,规则如下:

  • 1 个表创建 1 个 segment ,如果是分区,每个分区创建 1 个 segment ;
  • 1 个索引创建 1 个 segment ,如果是分区索引,每个分区索引创建 1 个 segment ;
  • 表的每个 CLOB 、 BLOB 或者 NCLOB 列都会创建 1 个 segment ,如果这些列上有索引,还会创建对应的 LOB 索引 segment ;
MySQL 设计原理
Datafile 空间管理

MySQL同样也采用了tablespace、segment、extent、page(对应oracle中的block)这几个概念,tablespace由若干个datafile组成,datafile以extent为单位进行空间的申请和释放。不同的是MySQL将extent分成了2种:

  • extent :正常的 extent , 1 个 extent 大小为 1M ,由连续的 page 组成, segment 以 extent 为单位申请和释放空间;
  • frag extent :特殊的 extent , 1 个 frag extent 也是连续的 1M 空间,但是 frag extent 不属于任何 segment ,而是为大家共用。 MySQL 不支持变长 extent ,为了节约空间,每个 segment 的前 32 个 page 在 frag extent 中申请 page ,超过 32 个 page 再以 extent 为单位申请空间;

图1.3-1 page结构

 

表1.3-1 FIL HEADER结构

域

长度

含义

FIL_PAGE_SPACE_OR_CHKSUM

4

4.0版本前存space id,之后存checksum值

FIL_PAGE_OFFSET

4

page no

FIL_PAGE_PREV

4

B-Tree叶子page的前1个page,用于索引扫描

FIL_PAGE_NEXT

4

B-Tree叶子page的下1个page,用于索引扫描

FIL_PAGE_LSN

8

本page的LSN,标识本page的最近一次修改

FIL_PAGE_TYPE

2

page类型:

FIL_PAGE_INDEX

FIL_PAGE_UNDO_LOG

FIL_PAGE_INODE

FIL_PAGE_IBUF_FREE_LIST

FIL_PAGE_TYPE_SYS

FIL_PAGE_FIL_FLUSH_LSN

8

  • 系统表空间:刷脏 page ,最近 1 次的 LSN ,本域仅在数据文件的第 1 个 page 中有效,用于判断数据库是否正常关闭;
  • 用户表空间:未使用;

FIL_PAGE_SPACE_ID

4

本page的space id

表1.3-2 FIL TAILER结构

域

长度

含义

FIL_CHECKSUM

4

本page的checksum,用于校验异常page

FIL_PAGE_LSN

4

等于FIL HEADER中的FIL_PAGE_LSN,用于检测page写一半系统掉电的异常情况

page是MySQL datafile空间管理的基本单位,其结构如1.3-1所示,组成部分如下:

  • FIL HEADER : page 头,定义了 page 的总体信息,详细情况见表 1.3-1 ;
  • Data :数据部分,格式随着 FIL_PAGE_TYPE 不同而不同;
  • FIL TAILER : page 尾,用于校验异常 page ,详细情况见表 1.3-2 ;

图1.3-2 datafile总体空间布局

 

了解page的结构,下面看总体空间布局。如图1.3-2所示,MySQL数据文件空间布局相对比较固定,非常有规律可循:

  • FIL_PAGE_TYPE_XDES :该类 page 存放 extent 的管理信息,当 page size 确定后每个 FIL_PAGE_TYPE_XDES page 可以管理的 extent 数量是固定的,这样本 page 就可以按照管理的 extent 数有规律地存放。图 1.3-2 示例是 page size 为 16K 的情况,每个 FIL_PAGE_TYPE_XDES page 可以管理 256 个 extent ,每个 extent 包含 64 个 page ,所以 FIL_PAGE_TYPE_XDES page 每隔 16384 ( 256*64 )个 page 出现 1 个;
  • FIL_PAGE_TYPE_HDR : FIL_PAGE_TYPE_HDR 和 FIL_PAGE_TYPE_XDES 非常类似,除了存放 extent 管理信息之外,还多了一个 SPACE HEADER 结构,作为空间管理的总入口,可以这么说, FIL_PAGE_TYPE_HDR 是一个特殊的 FIL_PAGE_TYPE_XDES 。本类 page 只有 1 个,且固定在第 0 个位置上;
  • FIL_PAGE_IBUF_BITMAP :本类 page 存放每个 page 的空闲情况以及 IBUF 的使用情况,由于 FIL_PAGE_IBUF_BITMAP 和 FIL_PAGE_TYPE_XDES 管理的规模一样多,所以 FIL_PAGE_IBUF_BITMAP page 永远跟在 FIL_PAGE_TYPE_XDES page 后面,位置固定;
  • FIL_PAGE_INODE :本类 page 存放各 segment 的入口信息, page 的类型为 FIL_PAGE_INODE ,位置不固定;

图1.3-3 FIL_PAGE_TYPE_HDR vs FIL_PAGE_TYPE_XDES

 

表1.3-3 XDES Entry结构

域

长度

含义

XDES_ID

8

其归属的segment id

FLST_PREV

6

链表指针,指向前1个XDES   Entry,前4个字节为该Entry的page no,后2个字节为page内偏移

FLST_NEXT

6

链表指针,指向后1个XDES   Entry,前4个字节为该Entry的page no,后2个字节为page内偏移

XDES_XSTATE

4

表示本XDES Entry处在哪个链表上:

  • XDES_FREE : FREE 链表;
  • XDES_FREE_FRAG : FREE_FRAG 链表;
  • XDES_FULL_FRAG : FULL_FRAG 链表;
  • XDES_FSEG :归属于 XDES_ID 记录的 segment ;

XDES_BITMAP

16

16*8,共计128个bit位,每2个bit位对应1个page,1个bit标识该page是否为空(XDES_FREE_BIT),另1个bit预留;

表1.3-4 SPACE HEADER结构

域

长度

含义

FSP_SPACE_ID

4

space id

FSP_NOT_USED

4

保留

FSP_SIZE

4

本tablespace的page总数,自动扩展时会不断增加

FSP_FREE_LIMIT

4

小于本数的page都尚未初始化,该数以后的page都尚未加入到FREE LIST中

FSP_SPACE_FLAGS

4

32个bit位,各位的含义如下:

  • FSP_FLAGS_POS_ZIP_SSIZE :压缩 page 的 size , 0 表示非压缩;
  • FSP_FLAGS_POS_ATOMIC_BLOBS :行格式, compressed 或者 dynamic ;
  • FSP_FLAGS_POS_PAGE_SSIZE : page 的 size ;
  • FSP_FLAGS_POS_DATA_DIR :如果创建表空间显式指定了 data_dir ,设置本 flag ;
  • FSP_FLAGS_POS_SHARED :是否是共享的表空间;
  • FSP_FLAGS_POS_TEMPORARY :是否是临时表空间;
  • FSP_FLAGS_POS_ENCRYPTION :是否是加密表空间;
  • FSP_FLAGS_POS_UNUSED :未使用的位;

FSP_FRAG_N_USED

4

FSP_FREE_FRAG链表上已经被使用的page数

FSP_FREE

16

链表指针,指向空extent(所有page都为空)链表

FSP_FREE_FRAG

16

链表指针,指向含空闲空间的frag extent链表

FSP_FULL_FRAG

16

链表指针,指向已经用满的frag extent链表

FSP_SEG_ID

8

当前系统最大segment id+1,作为分配segment   id的计数器

FSP_SEG_INODES_FULL

16

链表指针,指向完全用满的inode page链表

FSP_SEG_INODES_FREE

16

链表指针,指向尚有空闲空间的inode page链表

下面详细对比一下FIL_PAGE_TYPE_HDR page和FIL_PAGE_TYPE_XDES page,如图1.3-3所示,两者的结构基本是一样的。FIL_PAGE_TYPE_HDR page的SPACE HEADER结构是有信息的,而FIL_PAGE_TYPE_XDES page的对应位置为空。SPACE HEADER结构存放的是空间管理的总入口信息,所以只需要存在于第0个page中。在讨论SPACE HEADER前,先看一下XDES Entry结构。如表1.3-3所示,XDES Entry通过XDES_BITMAP标识其管理的extent中各page的空间状态,FLST_PREV和FLST_NEXT双向指针将处于相同状态且归属于同1个segment的XDES Entry链接在一起。那么这些链表的头指针在哪里呢?在SPACE HEADER结构中,如表1.3-4所示,SPACE HEADER作为总入口,管理着如下5个重要的链表:

  • FSP_FREE 链表:管理所有空的 extent ,指向状态为 XDES_FREE 的 XDES Entry ;
  • FSP_FREE_FRAG 链表:管理所有含空闲空间的 frag extent ,指向状态为 XDES_FREE_FRAG 的 XDES Entry ;
  • FSP_FULL_FRAG 链表:管理所有已经写满的 frag extent ,指向状态为 XDES_FULL_FRAG 的 XDES Entry ,随着 frag extent 的满和不满,对应的 XDES Entry 会在 FSP_FREE_FRAG 链表和 FSP_FULL_FRAG 链表之间移动;
  • FSP_SEG_INODES_FULL 、 FSP_SEG_INODES_FREE 链表:分别管理所有已经写满和尚未写满的 INDOES ;
  • 链表头结构: FLST_LEN 4 个字节 +FLST_FIRST 6 个字节 +FLST_LAST 6 个字节,共计 16 个字节, FLST_LEN 表示链表长度, FLST_FIRST 指向第 1 个节点( 4 个字节的 page no+2 个字节的 page 内偏移), FLST_LAST 指向最后 1 个节点;

通过FSP_FREE_FRAG和FSP_FULL_FRAG链表将所有frag extent管理起来,通过FSP_FREE链表将所有尚未分配给segment的空闲extent管理起来,那么已经分配给segment的extent又是如何管理的呢?实际上,就是通过FSP_SEG_INODES_FULL和FSP_SEG_INODES_FREE链表进行间接管理的,我们将在segment机制章节进一步讨论。

图1.3-4 FIL_PAGE_IBUF_BITMAP结构

 

表1.3-5 Change Buffer Bitmap结构

域

长度

含义

IBUF_BITMAP_FREE

2bit

某个page的空闲情况,0(0bytes),1(512bytes),2(1024bytes),3(2048bytes)

IBUF_BITMAP_BUFFERED

1bit

某个page上是否存在ibuf缓存

IBUF_BITMAP_IBUF

1bit

某个page本身是否是ibuf   BTree的节点

最后再看一下FIL_PAGE_IBUF_BITMAP page,MySQL发明了Insert Buffer机制,并将其进一步通用化为Change Buffer机制。其核心思想是对非唯一的二级索引进行insert、delete时,如果对应的page不在缓存中,不必将该page调度进缓存进行实时操作,而是将insert、delete操作缓存到insert buffer page中,待将来读到该page或者后台purge线程再实施真正的insert、delete操作,从而提高效率。但insert buffer机制存在如下约束和要求:

  • 唯一索引上的 insert 操作不能缓存,因为要做唯一性校验,必须读到真正的 page 才能做唯一性校验;
  • insert buffer 是以 BTree 组织的,缓存的每条记录的主键为 spaceid 、 page no 、 counter ,通过 spaceid 、 page no 记录缓存的操作时针对哪个 page 的,但这就要求 page no 在缓存过程中不能发生变化,这就需要一个机制来及时识别 page no 是否可能发生变化;
  • 还需要一个机制来识别某个 page 上的操作是否有缓存,从而在读到该 page 时,先将缓存的操作 merge 进来,然后再实施真正的读处理;

FIL_PAGE_IBUF_BITMAP page就是为这些目的而设计的,FIL_PAGE_IBUF_BITMAP page固定在FIL_PAGE_TYPE_XDES page后面。如图1.3-4所示,change buffer bitmap中每4个bit对应后面的1个page,8192个字节就可以管理16384个page(8192*8/4)。如表1.3-5所示,4个bit的组成如下:

  • IBUF_BITMAP_FREE :给出某个特定 page 的空闲情况,如果空闲空间过小,会发生 page 分裂, page no 会发生变化,不能缓存,否则可以缓存;
  • IBUF_BITMAP_BUFFERED :标识某个特定 page 上是否做了操作缓存;
  • IBUF_BITMAP_IBUF :标识某个特定 page 本身缓存的一部分;
Segment 空间机制

图1.3-5 FIL_PAGE_INODE page结构

 

表1.3-6 INODE Entry结构

域

长度

含义

FSEG_ID

8

本INODE归属的segment,0表示未使用

FSEG_NOT_FULL_N_USED

8

FSEG_NOT_FULL链表上已被使用的page数

FSEG_FREE

16

链表头,指向已分配给本segment,但尚未使用的extent链表

FSEG_NOT_FULL

16

链表头,指向属于本segment,但尚未用满的extent链表

FSEG_FULL

16

链表头,指向属于本segment,且已完全用满的extent链表

FSEG_MAGIC_N

4

仅在调试模式下使用

FSEG_FRAG_ADDR

128

32*4,管理32个frag page(分布在frag   extent中的page),4个字节记录page no

MySQL是通过INODE Entry管理segment的,每个INODE Entry对应1个segment。下面结合存放INODE Entry的FIL_PAGE_INODE page一起来讨论。如图1.3-5所示,FIL_PAGE_INODE page包括如下2个部分:

  • FSEG_INODE_PAGE_NODE : 12 个字节的双向指针,将相关的 FSEG_INODE_PAGE_NODE page 链接在一起。实际上有 2 个 FSEG_INODE_PAGE_NODE 链表,就是 FIL_PAGE_TYPE_HDR page 中的 FSP_SEG_INODES_FULL 和 FSP_SEG_INODES_FREE 链表,前者将所有已经写满 INODE Entry 的 FSEG_INODE_PAGE_NODE page 链接在一起,后者将尚未写满 INODE Entry 的 FSEG_INODE_PAGE_NODE page 链接在一起;
  • INODE Entry : 1 个 FSEG_INODE_PAGE_NODE page 中可以存放 n 个 INODE Entry (随 page 的大小变化),每个 INODE Entry 管理 1 个具体的 segment ;

如表1.3-6所示,INODE Entry管理着1个具体的segment,其中的关键信息如下:

  • FSEG_FREE 、 FSEG_NOT_FULL 、 FSEG_FULL : 3 个 extent 链表的指针头,结合 XDES Entry 结构中的双向指针,把已经分配给本 segment 的 extent 按照尚未使用、已使用但尚未用满、已经用满 3 种情况管理起来;
  • FSEG_FRAG_ADDR :通过记录 page 号,将 frag extent 中分配给本 segment 的 32 个 page 管理起来;

图1.3-6 空间管理全貌

 

至此,如图1.3-6所示,我们得到MySQL空间管理的全景图(不含ibuf):

  • SPACE HEADER 中的 FSP_FREE 结合 XDES Entry 将所有尚未分配给 segment 的 extent 管理起来;
  • SPACE HEADER 中的 FSP_FREE_FRAG 和 FSP_FULL_FRAG 结合 XDES Entry 将所有尚未用满和已经用满的 frag extent 管理起来;
  • SPACE HEADER 中的 FSP_SEG_INODES_FREE 和 FSP_SEG_INODES_FULL 结合 FSEG_INODE_PAGE_NODE 将所有尚未用满和已经用满的 FIL_PAGE_INODE 管理起来;
  • 每个 segment 有 1 个 INODE Entry (存在于 FIL_PAGE_INODE 中),通过 FSEG_FULL 、 FSEG_NOT_FULL 、 FSEG_FREE 结合 XDES Entry 将所有分配给本 segment 的已经用满、尚未用满、尚未使用的 extent 管理起来;
  • frag extent 既被 SPACE HEADER 管理,其中的 page 同时又被 INODE Entry 中的 FSEG_FRAG_ADDR 管理;
Data Page 空间机制

图1.3-7 data page结构

 

表1.3-7 PAGE HEADER结构

域

长度

含义

PAGE_N_DIR_SLOTS

2

page directory区域的槽位数量

PAGE_HEAP_TOP

2

堆中空闲空间的偏移量

PAGE_N_HEAP

2

堆中记录的数量,包括用户记录、系统记录、标记为删除的记录,同时第1个bit位为1时表示记录格式为compact

PAGE_FREE

2

指向第1条标记为删除的记录,记录头有next,可以将所有标记为删除的记录链接在一起

PAGE_GARBAGE

2

标记为删除的记录总空间(行记录的delete flag为1),属于可回收的空间

PAGE_LAST_INSERT

2

指向最近1次插入的记录,用于优化顺序插入

PAGE_DIRECTION

2

最后1条记录的插入方向:

  • PAGE_LEFT ;
  • PAGE_RIGHT ;
  • PAGE_SAME_REC ;
  • PAGE_SAME_PAGE ;
  • PAGE_NO_DIRECTION ;

标识当前记录的插入顺序,用于插入优化

PAGE_N_RECS

2

以相同方向连续插入记录的数量

PAGE_MAX_TRX_ID

8

本page内有效用户记录数,不含系统记录和标记为删除的记录

PAGE_LEVEL

2

当前page在BTree中的层级,叶子节点取值为0

PAGE_INDEX_ID

8

索引id,本page属于哪个索引

表1.3-8 segment info结构

域

长度

含义

FSEG_HDR_SPACE

4

本segment对应的inode   entry所在page的space id

FSEG_HDR_PAGE_NO

4

本segment对应的inode   entry所在page的page no

FSEG_HDR_OFFSET

2

本segment对应的inode   entry在所在page内偏移

如图1.3-7所示,data page除了通用的FIL HEADER和FIL TAILER之外,其它关键组成部分如下:

  • PAGE HEADER :描述了本 page 的总体信息,详细见表 1.3-7 ,通过 PAGE_FREE 和每条记录中的 REC_NEXT ,将所有标志为删除的记录链接在一起;
  • SEGMENT INFO :仅在 BTree 的 root page 中设置, SEGMENT INFO1 记录叶子节点 segment 对应的 INODE 信息, SEGMENT INFO2 记录非叶子节点 segment 对应的 INODE 信息;
  • SYS RECORDS : 2 条特殊的系统记录, infimum 和 supremun ,占用 26 个字节,用于 MySQL 特有的边界定位和锁控制;
  • USER RECORDS :用户记录,从上向下增长;
  • PAGE DIRECTORY :目录,从下向上增长,每个 slot 占用 2 个字节,指向 USER RECORDS 中的具体某条记录。目录中的 slot 和记录不是一一对应的,每 4 到 8 条记录在目录中占用 1 个 slot ;

有效的用户记录(删除标志位不为真)和2条系统记录通过记录头的REC_NEXT链接在一起,链接的顺序即为索引的键值顺序,且infimum永远是第1条记录,而supremum永远是最后1条记录。为了加快记录匹配效率,MySQL在page的底部设计了DIRECTORY区域,每隔4-8条记录占用1个slot(slot对应多少条记录是动态变化的,slot之间也不相等,就某个slot来说,通过对应记录头的REC_NEW_N_OWNED记录相应的记录数)。由于DIRECTORY区域是有序的(按主键或索引键逆序存放),可以通过折半查找快速匹配到目标记录。

图1.3-8 compact记录格式

 

表1.3-9 RECORD HEADER结构

域

长度

含义

REC_NEW_INFO_BITS

4bits

仅使用了前2个bit,1个bit位标记本行记录是否已经被删除,另1个bit位标记本行记录是否为BTree当前Level最左边page的第1条用户记录

REC_NEW_N_OWNED

4bits

非0表示本行记录在page   directory中占1个slot,具体取值为本slot和下1个slot之间的记录数

REC_NEW_HEAP_NO

13bits

本行记录在堆中的序号,用于MySQL的锁机制

REC_NEW_STATUS

3bits

记录的类型:

  • REC_STATUS_ORDINARY :叶子节点记录;
  • REC_STATUS_NODE_PTR :非叶子节点记录;
  • REC_STATUS_INFIMUM : infimum 系统记录;
  • REC_STATUS_SUPREMUM : supremum 系统记录;

REC_NEXT

16btis

指向本page中的下1条记录,用于2中情况:

  • 有效记录:按键值顺序将本 page 内所有有效记录链接在一起;
  • 无效记录:将本 page 内所有标记为删除的记录链接在一起;

compact格式已经成为MySQL的主流格式。如图1.3-8所示,compact记录格式包括如下组成部分:

  • RECORD_HEADER :记录的头部信息,详情如表 1.3-9 所示,记录在 page 内的偏移一般都是指 RECORD HEADER 的位置,因为记录的其它几个部分都是变长的;
  • VAR_COL_LEN_LST :每个变长列的实际长度在此描述,最大长度小于 255 或者实际长度小于 128 占 1 个字节,否则占 2 个字节;
  • NULL_BITS :每个可为 NULL 的列在此占 1 个 bit ,如果列的实际值为 NULL ,此 bit 位为真, ROW_DATA 部分不会出现此列;
  • ROW_DATA :实际列数据,聚集索引会增加事务 id ( 6 个字节)和回滚指针( 7 个字节) 2 个隐藏列;
数据布局

MySQL的数据包括系统数据、临时数据、回滚数据、用户数据、重做数据和binlog数据,重做数据和binlog数据相对独立,和tablespace没有太大关系,下面重点看其它几个数据的布局:

  • 用户数据: InnoDB 的所有表都是索引组织表,每个索引创建 2 个 segment , 1 个 segment 存放叶子节点 page , 1 个 segment 存放非叶子节点 page 。每张表存放的 tablespace 随着历史的演进有 3 种方案:
    • 早期版本:所有数据都存放在 1 个公共的系统 tablespace 中;
    • 其后版本:增加支持每张表有 1 个独立的 tablespace 中;
    • 近期版本:增加支持通用的 tablespace ;
  • 系统类数据( InnoDB ):主要有数据字典数据和系统数据,存放在公共的系统 tablespace 中。字典数据表有 SYS_TABLES 、 SYS_INDEXS 、 SYS_COLUMNS 、 SYS_FIELDS ,分别存放表信息、索引信息、表列信息、索引列信息,也采用索引组织表方式存放。系统数据有 insert buf 、 double write 、事务系统数据等等;
  • 临时数据:存放用户创建的临时表,对应临时 tablespace ;
  • 回滚数据:存放回滚数据,包括 rollback segment 和 undo log segment ,早期版本存放在公共的系统 tablespace 中,近期版本可存放在独立的 undo tablespace 中;
总结与分析

Oracle和MySQL的空间布局总体上非常类似,采用tablespace、segment、extent、block(page) 4层管理机制。extent和block(page)最大化地发挥持久设备的物理特性,tablespace和segment用于在逻辑上平衡数据的独立性和相关性。不过在管理设计上,Oracle和MySQL还是存在非常大的差异性。

在block(page)层面,Oracle的block设计更加通用,MySQL的page设计与索引组织表深度耦合,具体表现在:

  • 对于单条记录, MySQL 设计了 NULL_BITS ,稀疏场景下可以有效节省空间。不过 MySQL 的记录头和隐藏列比较厚重,每条记录占用 18 个字节( RECORD HEADER 占 5 个字节,事务 id 占 6 个字节,回滚指针占 7 个字节), Oracle 的记录头仅占 3 个字节;
  • MySQL 的 page 也设计相对比较粗放,某些区域仅在特定场景下有效,如 SEGMENT INFO 仅在 BTree 的 root page 中有效, FIL_PAGE_PREV 和 FIL_PAGE_NEXT 仅对 BTree 的叶子节点 page 有效等等;
  • Oracle 采用了相对地址管理方式, RowID 中保存了 file no 、 block no 、 directory id ,优势是可以快速定位记录,缺点是 block 内的碎片整理可以比较高效,但 block 间的整理涉及记录在 block 间移动,代价非常高 (shrink table ,调整 rowid ,涉及面较广 ) 。 MySQL 采用的是主键管理方式,数据和索引解耦,优势是不仅可做 page 内的碎片整理,还可以做 page 间的碎片整理( optimize table ),缺点是定位记录需在 BTree 中检索,然后在 page 内通过 directory 进行折半查找,定位效率低;
  • Oracle 的 page 组织方式上,记录之间没有强的依赖关系,插入操作可以并发执行,并发间竞争小。 MySQL 是索引组织的,插入操作竞争 page 的概率大,且对于每个插入操作而言,需要在 directory 中进行这边查找,且有可能发生 directory 目录重组,单次插入操作的平均成本也相对较高;

在extent层面,Oracle通过变长extent应对多样化的表大小,MySQL则通过frag extent来达到类似的效果。不过extent对block的管理上,Oracle做得更加精细,也更加准确。采用4个bit描述1个block,区分<25% free、25-50% free、50-75% free、>75% free。MySQL只使用了1个bit(虽然预留了2个bit),只能描述1个page满还是空。当然MySQL还有1个change buffer机制,为了获得好的缓存效果,也统计了page的空间情况,但也只区分了0 free、512 free、1024 free、2048 free,完全针对change buffer机制定制,在两处对page进行了各自的统计,并没有拉通设计。

在segment层面,Oracle采用三级位图进行管理,实际上L1 BMB已经达到block级,和extent对block的管理是拉通的。MySQL则通过3个双向链表FSEG_FREE、FSEG_NOT_FULL、FSEG_FULL和1个FSEG_FRAG_ADDR位图进行管理。实际上,Oracle刚开始也采用的链表方式管理,然后演进到位图方式。链表管理方式需要从链表头开始进行遍历,高并发下竞争非常明显,影响并发效率。当然MySQL在并发插入时瓶颈在索引组织表上,此处的瓶颈还没有显现出来。

在segment的使用上,两者都区分undo segment、temporary segment。Oracle将大字段(BLOB)放在独立的segment中,表数据和索引逻辑独立,所以也分别放在各自的segment中。MySQL是索引组织表,并将索引的叶子节点和非叶子节点分别放在2个不同的segement中。

在tablesapce层面,都区分permanent table、temporary tablespace、undo tablespace,两者没有太大的本质区别。

PDF版下载地址:http://blog.itpub.net/69912723/viewspace-2713926/

相关文章
|
11天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
7847 14
|
9天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
1710 4
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
10天前
|
人工智能 并行计算 PyTorch
秋叶 ComfyUI 2026 整合包 v3.2 完整部署教程:Python 3.13 + Torch 2.13 全栈升级
秋叶aaaki ComfyUI 2026年8月整合包v3.2正式发布!全面升级Python 3.13.11、PyTorch 2.13.0+cu130及ComfyUI v0.30.2,原生支持MiniMax H3、Wan 2.2、Qwen-Image-2.1等2026主流音视频/图像模型,解压即用,无需环境配置。
1534 11
|
6天前
|
人工智能 JavaScript 芯片
DeepSeek 官方偷偷上传 Harness 桌面端安装包,我已经用上了。。附最新下载地址
DeepSeek Harness 官方的桌面端安装包被网友扒出来了,2 分钟讲明白如何使用,体验如何,适合作为 AI 编程工具么?附最新 Windows 和 Mac 双端的下载地址
1485 1
|
8天前
|
人工智能 编解码 并行计算
MiniMax-H3 一键整合包技术文档:8G 显存运行 AI 漫剧制作 —— 角色替换 / 动作迁移 / 文图生视频部署与调参指南
MiniMax H3 是 MiniMax 开源的全模态视频生成模型,支持文/图/音/视多条件输入,输出最高2K、15秒带双声道音频视频。本文档详述其Int8量化版在8GB显存下的本地一键部署、三段式工作流(EDIT/REPLACE/CONTINUE)、参数调优及常见问题排查。(239字)
|
23天前
|
人工智能 自然语言处理 安全
阿里云千问办公 QwenWork详细介绍:产品核心能力、典型场景、价格及常见问题解答
千问办公是阿里云推出的一站式AI办公平台,主打"不止于对话,更注重交付",依托通义千问旗舰大模型,用户一句话即可完成数据分析、PPT生成、视频剪辑等复杂任务,直接输出可用成果。产品深度打通钉钉生态与企业OA,覆盖桌面端、网页端,提供企业标准版198元/人/月等多档订阅方案,新用户注册即赠2000积分,适配工程师、HR、财务等多职业办公场景,成为能动手干活的"全能AI同事"。
3755 10
|
18天前
|
缓存 IDE Java
【保姆级】Android Studio下载、安装和汉化教程(2026最新)
Android Studio 是 Google 官方推出的免费 Android 应用开发集成环境,基于 IntelliJ IDEA,内置模拟器、调试器、性能分析及 Compose 界面工具,功能全面,文档丰富,是安卓开发首选工具。(239字)
1906 1