一、索引组织表
在InnoDB存储引擎中,表都是根据主键顺序组织存放的,这种存储方式称为索引组织表。如果创建表时没有显示地指定主键,InnoDB引擎会按照下述规则选择或创建主键:判读表中是否有非空的唯一索引,有即为主键如果有多个会选择创建表时第一个定义的非空唯一索引(Not Null+Unqiue Key);如果没有InnoDB引擎会自动创建一个6字节大小的指针
使用_rowid字段可以查看表的主键,但只适用于单个列为主键的情况
二、InnoDB逻辑存储结构
从InnoDB存储引擎的逻辑存储结构看,所有的数据都被存放在一个称为表空间的空间中,表空间又被称为段、页。
1、表空间
表空间可以看作是InnoDB存储引擎逻辑结构的最高层,所有的数据都存放在表空间中。上一章提到的innodb_file_per_table参数,开启后每张表内的数据都可以单独存放到一个表空间内,但是只包含数据、索引和插入缓冲页,其它类似回滚、插入缓冲索引页、系统事务信息、二次写缓冲等信息还是存放在共享表空间中。
2、段
表空间是由各个段组成的,常见的段由数据段、索引段、回滚段等。InnoDB存储引擎表是索引组织的,因此数据即索引、索引即数据。数据段即为B+树上的叶子节点,索引段即为B+树上的非索引节点。
3、区
区是连续页组成的空间,在任何情况下每个区的大小都是1MB,为了保证区中页的连续性,InnoDB引擎一次从磁盘申请4~5个区,在默认情况下,InnoDB存储引擎页的大小为16KB,即一个区中一共有64个连续的页。当然也可以使用参数KEY_BLOCK_SIZE设置页的大小为2K、4K或8K,与之对应的页的数量就是512、256和128
在每个段开始时,先用32个页大小的破碎页来存放数据,在使用完这些页后会进行64个连续页的申请,以节省磁盘容量的开销
4、页
页(也称块)是InnoDB磁盘管理的最小单位,其默认大小为16KB,可以通过参数innodb_page_size将页的大小设置为4K、8K、16K,但是设置完成后,所有表的大小都会变为设置的值,不可以再次进行修改,除非产生新的库。常见的页有:数据页、undo页、系统页、事务数据页、插入缓冲位图页、插入缓冲空闲列表页、未压缩的二进制大对象页、压缩的二进制大对象页
5、行
每个页存放的行记录也是有硬性规定的,最多允许存放16KB/2-200行记录,约7992行记录。
三、InnoDB行记录格式
InnoDB存储引擎提供了Compact和Redundant两种格式来存放行记录数
1、Compact行记录格式
Commpact行记录的设计目标是高效地存储数据,简单来说,一个页中存放的行数据越多,其性能就越高。其存储方式如下:
其首部是一个非空变长字段长度列表,其是按照列的顺序逆序放置的,若列的长度小于255字节,用1字节表示,若大于255字节,用2字节表示。变长字段的长度最大不可以超过2字节,这是因为Mysql数据库中VARCHAR类型的最大长度限制为65535。
第二个部分是NULL标识位,指示该行数据中是否有NULL值,有则用1表示,所占字节为1字节。头信息占用5个字节,共40个bit,记录如下信息:
值得注意的是NULL除了占有NULL标志位后,不占用任何空间。此外每行数据除了用户定义的例外,还有两个隐藏列,事务ID列和回滚指针列,分别占用6字节和7字节大小。若InnoDB表没有定义主键,每行还会增加一个6字节的rowid列
2、Redundant行记录格式
Redudant格式是5.0版本之前InnoDB的行记录方式,不同于Compant,Redundant行记录格式的首部是一个字段长度偏移列表,同样是按照列的顺序逆序放置的,若列的长度小于255字节,用1字节表示,若大于255字节,用2字节表示。第二部分为记录头信息,占用6字节,如下:
3、行溢出数据
InnoDB存储引擎可以将一条记录中的某些数据存储在真的的数据页之外。Mysql数据库中的VARCHAR类型可以存放65535字节,但这里指的是所有VARCHAR类型长度的总和。InnoDB存储引擎页的大小为16kb,即16638字节,是如何存放下65535个字节的呢?
一般情况下,InnoDB存储引擎的数据都是存放在页类型为B树的节点中,当行溢出发生时,数据存放在页类型为Uncompress BLOB的二进制大对象页中。
4、Compressed和Dynamic行记录格式
InnoDB1.0.x版本开始引入了新的文件按格式,Compact和Redundant格式被称为Antelope文件格式,新的文件格式被称为Barracuda文件格式,该格式下有两种新的行记录格式:Compressed和Dynamic。这两种新的格式对于存放在BLOB中的数据采用了完全的行溢出方式。
5、CHAR的行结构存储
通常理解VARCHAR是存储变长长度的字符类型,CHAR是存储固定长度的字符类型。从Mysql4.1版本开始,CHAR(N)中的N指的是字符的长度,而不是字节的长度,因此对于不同的字符编码,CHAR类型不再代表固定长度的字符串,因此CHAR在InnoDB中可以试为变长字符类型。
四、InnoDB数据页结构
InnoDB数据页可以由以下7个部分组成:①文件头②页头③Infimun和Supremum Records④用户记录即行记录⑤空闲空间⑥页目录⑦文件结尾信息。其中文件头、页头和文件结尾信息大小是固定的,分别为38、56、8字节,用来标记该页的一些信息;用户记录、空闲空间和页目录为实际的行记录存储空间,它们的大小是动态的
1、File Header
用来记录页的一些头信息,由8个部分组成,共占38字节
2、Page Header
用来记录数据页的状态信息,由14个部分组成,共占用56字节
3、Infimum和Supremum Record
InnoDB存储引擎中,每个数据页有两个虚拟的行记录,用来限定记录的边界。Infimum记录是比该页中任何主键值都要小的值,Supremum指任何可能达的值还要大的值,两个值在也被创建时建立,并且在任何情况下都不会被删除
4、User Recore和Free Space
User Record是实际存储记录的内容,Free Space指空闲空间,同样也是整个链表数据结构,在一条记录被删除后该空间会被加入到空闲链表中。
5、Page Directory
页目录存放了记录的相对位置,有时这些记录指针称为槽或目录槽,并不是每个记录都有一个槽,InnoDB存储引擎的槽是一个稀疏槽,即一个槽中可能包含多个记录。槽中按照索引键值顺序存放,可以利用二叉查找找到一个粗略的结果,再通过recorder header中的next_record来继续查找相关记录。
6、File Trailer
为了检测页是否已经完整的写入磁盘,InnoDB存储引擎设置了File Trailer部分。默认配置下,InnoDB存储引擎每次从磁盘读取一个页就检测该页的完整性。
五、约束
1、数据完整性
一般来说,数据完整性由以下三种形式:①实体完整性保证表中有一个主键;②用户通过主键或唯一约束来保证实体的完整性;③用户编写一个触发器来保证数据完整性
2、约束的创建和查找
约束的创建可以采用以下两种方式:①表建立时就进行约束定义;②利用ALTER TABLE命令来进行创建约束
- 对于唯一索引约束而言,可以通过Create Unique Index来创建,默认约束名和列名一样,也可以认为指定
- 对于主键约束而言,其默认约束名为Primary
- 对于外键约束而言,其默认约束名会包含依赖的表以及列名信息
3、约束和索引的区别
约束是逻辑的概念,用来保证数据的完整性,索引是一个数据结构,即有逻辑上的概念,在数据库中还代表着物理存储的方式
4、对错误数据的约束
通过设置参数sql_mode的值为Strict_trans_tables,数据库会对输入值的合法性进行约束,针对不同的错误,提示的错误内容也不同
5、Enum和Set约束
Mysql不支持传统的Check约束,但是可以通过Enum和Set类型解决部分这样的约束需求。但是对于传统约束支持的连续值的范围约束或更为复杂的约束,Enum和Set约束无法进行有效的约束,只能通过触发器来实现值域的约束
6、触发器与约束
触发器的作用是在执行Insert、Delete和Update之前或之后自动调用SQL或存储过程,创建触发器的命令是Create Trigger,只有具备Super权限的Mysql数据库用户才可以执行这条命令。一个表最多可以建立6个触发器,即新增、删除、更新操作之前或之后,目前Mysql只支持For Each Row的触发方式,即按每行记录进行触发
7、外键约束
InnoDB存储引擎完整支持外键约束,一般来说被引用的为父表,引用的表称为子表,外键定义时的On Delete和On Update表示在对父表进行Delete和Update操作时,对子表做的操作,可定义的子表操作如下:
- Cascade:父表发生delete或update操作时,相应子表中的数据也进行delete或update操作
- Set Null:父表发生delete或update操作时,相应子表中的数据被更新为NULL值,但是该列需要被设定为允许Null值
- No Action:父表发生delete或update操作时,抛出错误,不允许这类操作发生
- Restrict:父表发生delete或update操作时,抛出错误,不允许这类操作发生,如果定义外键时没有指定On Delete或On Update,则使用该设定
No Action和Restrict的功能是相同的,这时因为Mysql数据库的外键约束都是即时检查的,而非Oracle可以设定延时检查。另外,InnoDB存储引擎在外键建立时会自动地对该列加一个索引,可以很好的避免外键无索引导致的死锁问题。
六、视图
View是一个命名的虚表,视图中的数据没有实际的物理存储
1、视图的作用
视图的主要用途就是作为一个抽象层,程序本身只需要按照视图定义来获取或更新数据。一般称可以进行更新操作的视图为可更新视图,视图定义中的With Check Option就是针对可更新视图的,即可更新的值是否需要检查,对于不满足定义条件的插入数据,会抛出一个异常,不允许数据更新。
2、物化视图
物化视图表示根据基表实际存在的实表,其数据存储在非易失的存储设备上,它可以用于预先计算并保存多表的Join或聚集(Group By)等耗时较多的SQL操作结果,在MSSQL中,称为索引视图。
在Oracle中物化视图的创建方式有两种:Build Immediate和Build Defreeed,默认为前置,在创建物化视图时生成数据,后者则根据需要生成数据。物化视图的刷新是指在DML操作后,采用何种模式进行刷新:On Demand表示在用户需要时进行刷新,On Commit表示在对基表进行DML操作提交时进行刷新。刷新的方式有四种:①FAST为增量刷新,即只刷新上次刷新后的修改;②Complete指对整个物化视图进行刷新;③Force指在刷新时判断是否可以进行FAST刷新,不行则采用Complete刷新;④Nerver指不进行任何刷新
Mysql不支持物化视图,但是可以通过触发器实现类似的功能
七、分区表
1、分区概述
分区的过程是将一个表或索引分解为更小、更可管理的部分,每个分区都是独立的对象,可以独立处理,也可以作为一个更大的对象的一部分进行处理。Mysql数据库支持水平分区,且分区是局部分区索引,即一个分区中即存放了数据又存放了索引。Mysql数据库支持集中类型的分区:
- Range分区:行数据基于属于一个给定连续区间的列值被放入分区;
- List分区:和Range分区类似,只是List分区面向的是离散的值;
- Hash分区:根据用户自定义的表达式的返回值来进行区分,返回值不能为负数;
- Key分区:根据Mysql数据库提供的哈希函数进行分区
不论创建任何分区,如果表中存在主键或唯一索引时,分区列必须是唯一索引的一个组成部分;如果没有指定主键或唯一索引,可以指定任何一个列为分区列
2、分区类型
1、Range分区
Range分区主要用于日期列的分区,例如销售记录表可以按照年来分区存放对应年份的记录。这样做的好处是方便管理,并且可以加快部分查询操作,启用分区后,需要注意边界值来编写最优的SQL语句
2、List分区
List分区和Range分区非常相似,但是List分区的值是离散的,非连续的。插入多行数据遇到分区未定义的值时,MyISAM引擎会存储之前的行数据,而InnoDB会将其视为一个事务,不进行任何数据插入操作。
3、Hash分区
Hash分区的目的是将数据均匀地分布到预先定义的各个分区中,保证各分区的数据数量大概一样,Range和List分区需要明确给定一个列值或列值集合,而Hash分区中,Mysql自动完成这些工作,用户只需要将要进行哈希分区的列值指定一个列值或表达式,以及指定被分区的表将要被分割成的分区数量
4、Key分区
Key分区和Hash分区相似,不同之处在于Hash分区使用用户定义的函数进行分区,Key分区使用Mysql数据库提供的函数进行分区
5、Columns分区
Range、List、Hash和Key分区的条件是数据必须是整型,如果不是整型需要通过函数将其转换为整型。从Mysql5.5版本开始支持Columns分区,它可以直接使用非整型的数据进行分区,分区根据类型直接比较而得,不需要转换为整型,它支持所有整型类型、日期类型、字符串类型。此外Range Column分区可以对多个列的值进行分区,对于之前的Range和List分区可以使用Range Columns和List Columns分区进行代替
3、子分区
子分区(subpartitioning)是在分区的基础上在进行分区,有时也称为复合分区,Mysql允许在Range和List的分区上再进行Hash或Key子分区。子分区的建立需要注意:
- 每个子分区的数量必须相同
- 要在一个分区表的任何分区上使用SubPartition来明确定义任何子分区,就必须定义所有的子分区
- 每个Subpartition子句必须包括子分区的一个名字
- 子分区的名字必须是唯一的
4、分区中的Null值
Mysql数据库允许对Null值进行分区,但是与其它数据库不同。Mysql数据库分区总是视NULL值小于任何一个非NULL值,因此对于不同的分区类型,其处理Null值的方法也不同
- 对于Range分区,如果向分区列插入了Null值,Mysql数据库会将该值放入最左边的分区;
- 对于List分区,使用Null值必须显示地指出哪个分区中放入Null值,否则会报错
- 对于Hash和Key分区,任何分区函数都会将含有Null值的记录返回为0
5、分区的性能
数据库应用分为两类,一类是OLTP(在线事务处理),如电子商务和网络游戏,一类是OLAP(在线分析处理),如数据仓库和数据集市。对于OLAP应用,分区可以很好的提高查询的性能,对于OLTP应用,分区需要非常小心,设计不好的表会带来严重的性能问题。
6、在表和分区间交换数据
Mysql5.6开始支持Alter Table ... Exchange Partition语法,该语法允许分区或子分区中的数据与另一个非分区表中的数据进行交换。在使用时需要满足以下条件:
- 要交换的表需要和分区表有着相同的表结构,但是表不能含有分区;
- 在非分区表中数据必须在交换的分区定义内;
- 被交换的表中不能含有外键,或者其它表含有对该表的外键引用;
- 用户除了Alter、Insert和Create权限外还需要Drop权限;
- 使用时不会触发交换表和被交换表上的触发器;
- Auto_Increment列将被重置