下面这条SQL语句执行的时候指定了ENGINE = InnoDB存储引擎为InnoDB:
CREATE TABLE `tb_album` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
`title` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '相册名称',
`image` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '相册封面',
`image_items` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '图片列表',
`is_delete` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '0' COMMENT '是否可删除(1:可删除;0:不可删除)',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 100002 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
我们去数据文件看一张表存储了那些东西:
#可以查看到数据存储的路径
show variables like '%datadir%';
可以看到我们创建一张表,在数据目录会创建.frm和.ibd两个文件。
#查看innodb存储引擎状态,包含缓冲池、修改缓冲、自适应哈希状态信息、日志缓冲等信息
show engine innodb status;
#查看buffer_pool_size的大小 换算成M 发现是128M
show variables like 'innodb_buffer_pool_size';
BufferPool 缓存了表和索引数据,加速了数据的访问和修改。它不只是简单的做缓存,引入了LRU算法来缓存热点数据。有下面几个特点:
它的大小默认为128M,数据以页的方式进行组织,数据结构是单链表。
页(Pages)是 InnoDB 中管理数据的最小单元,默认大小为16k, 行数据会组织到页里面
对于 Buffer Pool 中数据的查询,InnoDB 直接读取返回。对于 Buffer Pool 中数据的修改,InnoDB 直接在 Buffer Pool 中修改,并将修改写入redo log。
innodb中的LRU算法和普通的LRU算法存在一定的区别
传统的LRU算法存在问题,举个例子,比如我们现在访问了某些数据,数据比较多,触发了预加载机制,这个时候这些数据页就会被写到队列头部,但是这些数据我们之后不再访问,那我们之前频繁访问的数据已经被挤到队尾了,这个时候之前频繁访问的数据就会进行刷盘操作,而这些只访问过一次的数据留在了缓冲区,那岂不是我们再访问频繁的数据又会产生IO重新将它加载进来,所以为了解决这个问题MySQL基于传统的LRU做了冷热分离的LUR。
New Sublist存储的是热点数据,Old Sublist存储的是冷数据。
#我们可以看到两个配置
show variables like "innodb_old%"
#innodb_old_blocks_pct 配置冷数据区的大小 也就是下面的 3/8
那么现在访问数据是怎么样工作的呢?
1 数据页第一次加载进来,会放到冷数据区的队头。
2 那么再访问这个数据怎么加入到热点区域里面去呢?它默认不会立即被放到热点区域去,默认情况下如果我们1秒之内再对这个数据进行了访问,那么它才会被移动到热数据的队头。这个时间可以通过innodb_old_blocks_time来进行配置, 可以看到默认就是1s。
show variables innodb_old_blocks_time;
#查看ChangeBuffer的状态
SHOW ENGINE INNODB STATUS\G
1 介绍
这个区域属于BufferPool 的一部分,缓存的是辅助索引数据,如果修改辅助索引的数据并且这些数据不存在于BufferPool中,ChangeBuffer就会缓存它们,所以它的出现加速了辅助索引的写入操作。
由于辅助索引数据的不连续性,导致修改辅助索引时需要进行频繁的磁盘 IO 消耗大量性能,Change Buffer 缓冲对二级索引的修改操作,同时将写操作录入 redo log 中,它不会立即刷盘,而会在合适的时机合并BufferPool对辅助索引的修改,然后达到一定的量或者系统空闲的时候进行刷盘(这是一个周期性的操作),从而减少了IO操作。
它的数据结构是一个B树。
2 配置
#这个是配置缓存那些操作类型
innodb_change_buffering
有这样几个值:
#这个配置了Change BufferPool占 BufferPool 的百分比
innodb_change_buffer_max_size
# 查看是否开启自适应哈希配置,默认是开启的
mysql> show variables like 'innodb_adaptive_hash_index';
自适应哈希索引是建立在索引上的索引,它优化的目标是频繁查询的数据页和索引页,如果使用聚簇索引进行数据页定位的时候需要根据索引树的高度从根节点走到叶子节点,通常需要 3 到 4 次查询才能定位到数据,有了哈希索引我们就可以直接获取到这个数据。InnoDB 根据对索引使用情况的分析和索引字段的分析,通过自调优Self-tuning的方式为索引页建立或者删除哈希索引。
对于二级索引,若命中 AHI,则将直接从 AHI 获取二级索引页的记录指针,再根据主键沿着聚簇索引查找数据;若聚簇索引查询同样命中 AHI,则直接返回目标数据页的记录指针,此时
就可以根据记录指针直接定位数据页。
AHI 的大小为 Buffer Pool 的 1/64,在 MySQL 5.7 之后支持分区,以减少对于全局 AHI 锁的竞争,默认分区数为 8。
#可以通过这个参数取指定 默认为8 最大值为512
innodb_adaptive_hash_index_parts
引入缓存其实本质都是解决磁盘IO的问题,先写缓冲再写磁盘可以大大减少磁盘IO
日志缓冲区是存储要写入磁盘上的日志文件的数据的内存区域。日志缓冲区大小由innodb_log_buffer_size变量定义。默认大小为16MB。日志缓冲区的内容将定期刷新到磁盘。大型的日志缓冲区使大型事务能够运行,而无需在事务提交之前将重做日志数据写入磁盘。因此,如果您有的事务需要更新、插入或删除许多行,则增加日志缓冲区的大小将保存磁盘I/O。innodb_flush_log_at_trx_commit变量控制写入日志缓冲区内容到磁盘的方式(根据事务执行来控制什么时候刷盘)。innodb_flush_log_at_timeout变量控制日志刷新频率,多少秒进行一次刷盘。
innodb_flush_log_at_trx_commit有下面这个几个取值:
0 :当设置为0时,日志每秒会被写入系统缓存,过一秒刷新到磁盘一次。未刷新日志的事务可能会在崩溃中丢失。
1:完全符合ACID,需要默认设置1。在每个事务提交时,日志都会被写入并刷新到磁盘。
2:设置为2时,每个事务提交后写入日志,每秒刷新到磁盘一次。未刷新日志的事务可能会在崩溃中丢失。与0的区别是每个事务提交的时候就会写到系统缓存。
需要注意的是,每一秒更新并不是绝对的,也就是说不一定到了1s就一点会刷日志
在磁盘中,InnoDB 将所有数据都逻辑地存放在一个空间中,称为表空间(Tablespace)。表空间由段(Segment)、区(extent)、页(Page)组成。
表空间有下面这几种:
系统表空间(System Tablespace)
独立表空间(File-per-table Tablespace)
通用表空间(General Tablespace)
回滚表空间(Undo Tablespace)
临时表空间(The Temporary Tablespace)
1 系统表空间
系统表空间是 InnoDB 数据字典、双写缓冲、修改缓冲和回滚日志的存储位置,如果关闭独立表空间,它将存储所有表数据和索引。
它默认下是一个初始大小 12MB、名为 ibdata1 的文件,系统表空间所对应的文件由innodb_data_file_path 定义。
指定系统表空间文件自动增长后,其增长大小由 innodb_autoextend_increment 设置(默认为 64MB)且不可缩减,即使删除系统表空间中存储的表和索引,此过程释放的空间仅仅是在表空间文件中标记为已释放而已,并不会缩减其在磁盘中的大小。
数据字典
(Data Dictionary): 数据字典是由各种表对象的元数据信息(表结构,索引,列信息等)组成的内部表
双写缓冲
(Doublewrite Buffer):双写缓冲用于保证写入磁盘时页数据的完整性,防止发生部分写失效问题。非常重要,
修改缓冲
(Change Buffer): 内存中 Change Buffer 对应的持久化区域
回滚日志
(Undo Log):实现事务进行 回滚 操作时对数据的恢复。是实现多版本并发控制(MVCC)重要组成。
2 独立表空间
独立表空间用于存放每个表的数据和索引。其他类型的信息,如:回滚日志、双写缓冲区、系统事务信息、修改缓冲等仍存放于系统表空间内。因此即使用了独立表空间,系统表空间也会不断增长。在5.7版本中默认开启开启独立表空间(File-per-table TableSpace)(innodb_file_per_table=ON )之后,InnoDB 会为每个数据库单独创建子文件夹,数据库文件夹内为每个数据表单独建立一个表空间文件 同时创建一个 table.frm 文件用于保存表结构信息。每个独立表空间的初始大小是 96KB。
系统表空间与独立表空间之前的关系:1 开启独立表空间innodb_file_per_table=1,每张表的数据都会存储到一个独立表空间,即名.ibd文件表 2关闭独占表空间innodb_file_per_table=0,则所有基于InnoDB存储引擎的表数据都会记录到系统表空间,即ibdata1 文件
3 通用表空间
通用表空间(General Tablespace)是一个由 CREATE TABLESPACE 命令创建的共享表空间,创建时必须指定该表空间名称和 ibd 文件位置,ibd 文件可以放置于任何 MySQL 有权限的地方。该表空间内可以容纳多张数据表,同时在创建时可以指定该表空间所使用的默认引擎。
通用表空间存在的目的是为了在系统表空间与独立表空间之间作出平衡。系统表空间与独立表空间中的表可以向通用表空间移动,反之亦可,但系统表空间中的表无法直接与独立表空间中的表相互转化。
4 Undo 表空间
Undo TableSpace 用于存放一个或多个 undo log 文件。默认 undo log 存储在系统表空间中,MySql 5.7中支持自定义 Undo log 表空间并存储所有 undo log。一旦用户定义了 Undo Tablespace,则系统表空间中的 Undo log 区域将失效。对于 Undo Tablespace 的启用必须在 MySQL 初始化前设置,Undo Tablespace 默认大小为 10MB。Undo Tablespace 中的 Undo log 表可以进行 truncate 操作。
5 临时表空间
MySQL 5.7 之前临时表存储在系统表空间中,这样会导致ibdata 在使用临时表的场景下疯狂增长。5.7 版本之后 InnoDB 引擎从系统表空间中抽离出临时表空间(Temporary Tablespace),用于独立保存临时表数据及其回滚信息。该表空间文件路径由 innodb_temp_data_file_path 指定,但必须继承 innodb_data_home_dir 。
1 段 Segment
表空间由各个段(Segment)组成,创建的段类型分为数据段、索引段、回滚段等。由于 InnoDB 采用聚簇索引与 B+ 树的结构存储数据,所以事实上数据页和二级索引页仅仅只是 B+ 树的叶子节点,因此数据段称为 Leaf node segment,索引段其实指的是 B+ 树的非叶子节点,称为 Non-Leaf node segment。一个段会包含多个区,至少会有一个区,段扩展的最小单位是区。
2 区 Extend是由连续的页组成的空间,大小固定为 1MB,由于默认页大小为 16K,因此一个区默认存储 64 个连续的页。如果页大小调整为 4K,则 256 个连续页组成一个区。为了保证页的连续性,InnoDB 存储引擎会一次从磁盘申请 4 ~ 5 个区。
3 页 Page
页(Page)是 InnoDB 的基本存储单位,每个页大小默认为 16K,从 InnoDB1.2.x 版本开始,可通过设置 innodb_page_size 修改为 4K、8K、16K。InnoDB 首次加载后便无法更改。
# 查看MySQL页大小
show variables like 'innodb_page_size';
但是我们一般不推荐修改,因为这个与操作系统有关,再和操作系统交互的时候比如操作系统写磁盘的页是4k,我们的页是16k这是整数倍就比较好操作,这是和操作系统底层IO操作的基本单位息息相关的
4 行 Row
InnoDB的数据是以行为单位存储的,1个页中包含多个行。在MySQL5.7中,InnoDB提供了4种行格式:Compact、Redundant、Dynamic和Compressed行格式,Dynamic为MySQL5.7默认的行格式。创建表时可以指定行格式:
CREATE TABLE t1 (c1 INT) ROW_FORMAT=DYNAMIC;
#修改表的行格式
ALTER TABLE tablename ROW_FORMAT=行格式名称;
#修改默认行格式
SET GLOBAL innodb_default_row_format=DYNAMIC;
#查看表行格式
SHOW TABLE STATUS LIKE 't1';
关于4中行格式