【书 : InnoDB 存储引擎】第4章 表

4.1 索引组织表

在 innodb 引擎中, 表都是根据主键顺序组织存放的。 每张表都有个主键(primary key), 如果在创建表时没有显示地定义主键, 则 innodb 会按一下方式:

1 首先判断表中是否有非空的唯一索引(unique not null), 如果有, 则该列为主键。多个时, 选择第一个, 顺序为定义索引顺序。

2 如果不符合上述条件, innodb 会自动创建一个 6 字节大小的指针。

mysql>select a,b,c,d,_rowid from z; 

_rowid 可以显示表的主键,但只能是单列为主键情况, 对于多列组成组件就显得无能为力了。

4.2 Innodb 逻辑存储结构


4.2.1 表空间

启用了 innodb_file_per_table 参数, 需要注意的是每张表空间内存放的只是数据, 索引和插入缓冲 bitmap 页, 其他类的数据,如 回滚(undo)信息, 插入缓冲索引页,系统事务信息,二次写缓冲(double write buffer)等还是存放在原来的共享表空间内。

innodb 不会再执行 rollback 时去回收共享表空间。 虽然 innodb 不会回收这些空间, 但是会自动判断这些 undo 信息是否还需要, 如果不需要, 则将这些空间标记为可用空间, 供下次 undo 使用。 master thread 每 10 秒会执行一次 full purge 操作, 有可能用户执行update 语句后, 会发现 ibdata1 不会再增大了, 就是这个原因。

4.2.2 段

数据段为 B+ 数的叶子节点(leaf  node segment), 索引段为 B+ 数的非索引节点(non-leaf node segment).

4.2.3 区

区是由连续页组成的空间, 在任何情况下每个区的大小都是 1MB。 为了保证区中页的连续性, Innodb 一次从磁盘申请 4~5 个区。 默认情况下, Innodb 页的大小为 16KB, 即一个区中一共有 64 个连续的页。在用户启用了 innodb_file_per_table 后, 创建的表默认大小为 96KB。 区中是 64 个连续的页, 创建的表的大小至少是 1MB 才对啊? 其实 这是因为在每个段开始时, 先用 32 个页大小的碎片页(fragment page)来存放数据,在使用完这些页之后才是 64 个连续页的申请。 这样做的目的是, 对于一些小表, 或者是 undo 这类的段, 可以在开始申请较少的空间, 节省磁盘容量的开销。

举例:工具 py_innodb_page_info 

创建 t1 

mysql>insert t1 select null, repeat('a', 7000);

mysql>insert t1 select null, repeat('a', 7000);

插入 2 条后


再插入 60 条后


再 插入一条后,开始申请新页

4.2.4 页

页是 innodb 磁盘管理的最小单位。 默认大小 16Kb。 从 innodb 1.2.x 开始, 可通过 innodb_page_size 将页的大小设置为 4K, 8K, 16K。 设置完成, 所有野大小都为 innodb_page_size, 不可以对其再次进行修改。除非通过 mysqldump 导入和导出操作来产生新的库。

页类型

4.2.5 行

Innodb 是面向列的(row-oriented), 也就是数据是按行进行存放的。 

4.3 Innodb 行记录格式

在 mysql 5.1 版本中, 默认设置 compact 行格式。 用户可以通过 show table status like 'table' 来查看当前使用的行格式, 其中 row_format 属性表示当前使用的行记录结构类型。 数据库实例的作用之一就是读取页中存放的行记录。


4.3.1  Compact 行记录格式


hexdump -C -v xxx.ibd > xxx.txt

4.3.2 Redundant 行记录格式



4.3.3 行溢出数据

在字符类型为 latin1 下,理论值为 65535, 实测 varchar 类型的最大长度为 65532, 因为有其他开销。 创建时,大于这个值时, 若设置 SQL_MODE 为严格模式,则会报错, 若 不是严格模式, 则允许创建表。 数据库抛出一个 warning。 varchar 类型转换成了 text 类型。

若字符类型为 gbk (max=32767), utf8 (max=21845), 可知 varchar(N) 中的 N 指的是字符的长度,而文档中 varchar 最大支持 65535 ,字节。注:该长度是指所有的 varchar 列的长度总和, 如果列的长度总和超过这个长度, 依然无法创建。

Innodb 的页为 16KB, 即 16384 字节, 却能存放 65532 字节。 原因是 一般情况下, 数据都是存放在页类型为 B-tree node 中。 当发生行溢出时, 数据存放在页类型为 uncompress blob 页中。

数据节点 B-tree Node 保存了varchar(65532) 的前 768 字节的前缀(prefix)数据, 之后是偏移量,指向行溢出页, 也就是 uncompressed blob page.

如果页中只能存放下一条记录,那么 innodb 存储会自动将行数据存放到溢出页中。因为 innodb 是索引组织的, 即 B+ tree 结构, 这样每个页中至少应该有两条记录,否则失去了 B+ tree 的意义,变成链表了。

经过多次测试, 发现阈值长度为 8098. 即能保证插入 2 条记录。

对于 text 或 blob 数据类型, 用户不总是存放在 uncompressed blob page 中。是放在数据页中还是 blob 页中, 取决于一个页中是否至少存两条记录。

用户既然使用了 blob 类型, 一般不可能存放长度太小的数据。 因此大多数情况下, blob 的行数还是会发生行溢出, 实际数据保存在 blob 页中, 数据页只保存数据的 768 字节。

4.3.4 Compressed 和 Dynamic 行记录格式

Antelope: compact 和 Redundant

Barracuda: Compressed 和 Dynamic

Barracuda 采用完全的行溢出方式。数据页中只存放 20 个字节的指针, 实际的数据都存放在 off page  中。


compressed 行记录的另一个功能就是, 存储在其中的行记录会以 zlib 的算法进行压缩, 因此对于 blob,text,varchar 这类大长度类型的数据能够进行非常有效的存储。

4.3.5 Char 的行结构存储

InnoDB 内部 char 类型在多字节字符集类型存储。 char 类型被明确视为了变长字符类型, 对于未能占满长度的字符还是填充 0x20. Innodb 存储内部对字符的存储和我们用 hex 函数看到的一致。 因此认为在多字节字符集的情况下, char 和 varchar 的实际行存储基本没有区别。

4.4 Innodb 数据页结构

4.4.1File Header

4.4.2 Page Header

4.4.3 Infimum 和 Supermum Record

4.4.4 User Record 和 Free Space

4.4.5 Page Directory

4.4.6 File Trailer

4.4.7 Innodb 数据页结构示例分析

4.5 Named File Formats 机制


参数 innodb_file_format 用来指定文件格式。

参数 innodb_file_format_check 用来检测当前 Innodb 引擎文件格式的支持度。

4.6 约束

4.6.1 数据完整性

关系型数据库系统和文件系统的一个不同点事, 关系数据库本身能保证存储数据的完整性, 不需要应用程序控制,而文件系统一般需要再程序端进行控制。

innodb 的约束:primary key, unique key, foreign key, default, not null

4.6.2 约束的创建和查找

表建立时就进行约束定义

利用 alter table 命令来进行创建约束

4.6.3 约束和索引的区别

约束更是一个逻辑的概念, 用来保证数据的完整性; 而索引是一个数据结构, 既有逻辑上的概念, 在数据库中还代表着物理存储的方式。

4.6.4 对错误数据的约束

默认情况下, 向 not null 的列插入一个 null 值, 向 date 列 出入一个非法日期, 如‘’2009-02-30,mysql 不会报错,而是显示警告,查询时 会分别显示 0 和 0000-00-00 。 通过 show warnings 来显示警告信息。

通过设置 sql_mode 来严格审核输入的参数, 如 

mysql>set sql_mode='STRICT_TRANS_TABLES';


4.6.5 ENUM 和 SET 约束

规定域范围只能是 male 或 female , 时, 可以用 enum 类型来进行约束

mysql->create table a(id int, sex enum('male', 'female'));

4.6.6 触发期与约束

最多可以为一个表建立 6 个触发器, 分别为 insert , update , delete 的 before 和 after 各定义一个。 当前 mysql 只支持  for each row  的触发方式, 即按每行记录进行触发, 不支持像 DB2 的 for each statement 的触发方式。

例如, 创建 before 触发, 判断新旧值差异, 新值总小于旧值。 非法数据则还原, 并记录,新,旧值到 uercash_err_log 表。



4.6.7 外键约束

创建外键及其他操作
创建父子表

外键定义时的 on delete 和 on update 表示在对父表进行 delete 和 update 操作时, 对字表所做的操作, 可定义的字表操作有:

cascade :对 父表进行 delete 或 update 操作时, 对相应字表页惊醒delete 或 update 操作。

set null:对父表 delete 或 update 时, 对相应子表中的数据更新为 null 值, 单字表相应列必须运行为 null 值。

no action 和 restrict:当父表 delete 或 update 操作时, 抛出错误,不允许这类操作发生。

目前 mysql 外键约束都是 及时检查,从定义看出, no action 和 restrict 的功能使相同的。

如果没有指定 on delete 或 on update , restrict 是默认的外键设置

Innodb 在外键建立时会自动地对该列加一个索引。 如 给 parent_id 加索引。


4.7 视图

与持久表不同的是, 视图中的数据没有实际的物理存储。

4.7.1 视图的作用

一 抽象封装

程序本身不需要关心基表(base table)的结构

二 安全层

按照视图定义来取数据或更新数据虽然视图是基于基表的一个虚拟表, 但是用户可以对某些视图进行更新操作, 其本质是通过视图的定义来更新基本表。


视图 1 
视图2

不加 with check option, 则可以更新不符合的数据到基表,但视图中不显示, 且不报错。 加上后则,报错。

show tables, 该命令会显示出当前数据库下的所有的表, 但因为视图是虚表, 同样被作为表显示出来。 基表可通过 information_schema 下的 table 表来查询, 并搜索表类型为 base table 的表。要查看视图的元数据(meta data), 可以访问 information_schema 下的 views 表。

4.7.2 物化视图

物化视图的好处是对于一些复杂的统计类查询能直接查处结构。

mysql 数据库本身并不支持物化视图, 换句话说, mysql 数据库中的视图总是虚拟的。 但是用户可以通过一些机制来实现物化视图的功能。

4.8 分区表

4.8.1 分区概述

几种分区类型:

range 分区,list 分区,hash 分区,key 分区

无论创建何种类型的分区, 如果表中存在主键或唯一索引时, 分区列必须是唯一索引的一个组成部分。如果建表时没有指定主键, 唯一索引, 可以指定任何一个列为分区列。

4.8.2 分区类型

1 range 分区:行数据基于属于一个给定连续区间的列值被放入分区。 mysql 5.5 开始支持 range columns 的分区


查询 information_schema 库中的 PARTITIONS 表来查看每个分区的具体信息。PARTITION_METHOD 表示分区的类型, 这里显示 range.

maxvalue 表示正无穷。 values less than maxvalue

分区后的好处:

1 便于管理。 如删除某一年份的数据, 只是删除该年的分区即可。

alter table sales drop partition p2008;

2 加快某些查询操作。 如查询 2008 年的销售额。

explain partitions select * from sales where date >='2008-01-01' and date <= '2008-12-31'\G;

可看到 partitions:p2008 ,只搜索了 p2008 这个分区,而不会所有的分区。

优化器只能对 year(), to_days(), to_seconds(), unix_timestamp(), 这类函数进行优化选择。

2 list 分区: 和 range 分区类似, 只是 list 分区面向的是离散的值。 mysql 5.5 开始支持list columns 的分区。

在 insert 插入多个行数据的过程中遇到分区未定义的值时, myisam 和 innodb 存储引擎的处理完全不同。 myisam 会将之前的行数据都插入 ,但之后的数据不会被插入。 而 innodb 会视其为一个事务, 因此没有任何数据插入。

3 hash 分区: 根据用户自定义的表达式的返回值来进行分区, 返回值不能为负值。

hash 分区的目的是将数据均匀地分布到预定义的各个分区中, 保证各分区的数据数量大致都是一样的。

mysql 还支持一种称为 linear hash 的分区, 它使用一个更加复杂的算法来确定新行插入到已经分区的表中的位置。 linear hash 分区的优点 在于, 增加、删除、合并和拆分

4 key 分区: 根据 mysql 数据库提供的哈希函数来进行分区。key 分区和 hash 分区相似, 不同之处在于 hash 分区使用用户定义的函数进行分区, key 分区使用 mysql 数据库提供的函数进行分区。

5 COLUMNS 分区: columns 分区可以直接使用非整形的数据进行分区, 分区根据类型直接比较而得, 不需要转化为整形。

mysql 5.5 开始支持 columns 分区, 对于之前的 range 和 list 分区, 用户可以用 range columns 和list columns 分区很好的代替。

4.8.3 子分区


4.8.4 分区中的 Null 值

range 分区总是视 Null 值视为 小于任何一个非 null 值, 这和 mysql 数据库中处理 null 值的 order by 操作是一样的。

在 list 分区下要使用 null 值, 则必须显示地指出那个分区中放入 null 值, 否则报错。

hash 和 key 分区对于 null 值的处理方式和 range ,list 分区不一样。 任何分区函数都将含有 null 值的记录返回为 0.

4.8.5 分区和性能

对于 olap (在线分析处理)的应用,分区的确是可以很好地提高查询的性能,因为 olap 应用大多数查询需要频繁地扫描一张很大的表。

对于 oltp (在线事务处理), 分区应该非常小心。通常不会获取一张达标的 10% 的数据,大部分都是通过索引返回几条记录即可。这时,分区反而会使性能下降。

4.8.6 在表和分区间交换数据

mysql 5.6 开始支持 alter table .... exchange partition 语法。 该语句允许分区或子分区中的数据与另一个非分区的表中的数据进行交换。

创建一个同样的表:

create table e2 like e;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,193评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,306评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,130评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,110评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,118评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,085评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,007评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,844评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,283评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,508评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,395评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,985评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,630评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,797评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,653评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,553评论 2 352

推荐阅读更多精彩内容