1. InnoDB存储引擎介绍

  MySQL从5.5版本开始将InnoDB作为默认存储引擎,该存储引擎是第一个完整支持事务ACID特性的存储引擎,且支持数据行锁,多版本并发控制(MVCC),外键,以及一致性非锁定读。
ENGINE= 参数指定创建其他存储引擎的表。

InnoDB的关键属性包括:

• ACID事务特性支持,包括commit,rollback以及crash恢复的能力
• 行级别锁以及多版本并发控制MVCC
• 利用主键的聚簇索引(clustered index)在底层存储数据,以提升对主键查询的IO性能
• 支持外键功能,管理数据的完整性

ACID模型是关系型数据库普遍支持的事务模型,用来保证数据的一致性,其中的ACID分别代表:

• A:atomicity    原子性:事务是一个不可再分割的工作单位,事务中的操作要么都发生,要么都不发生
• C:consistency  一致性:事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。这是说数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性
• I:isolation    独立性:多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果
• D:durability   持续性:在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚
举例来说,比如银行的汇款1000元的操作,简单可以拆分成A账户的余额-1000,B账户的余额+1000,还要分别在A和B的账户流水上记录余额变更日志,这四个操作必须放在一个事务中完成,否则丢失其中的任何一条记录对整个系统来说都是不完整的。

对上述例子来说,
1. 原子性体现在要么四条操作每个都成功,意味着汇款成功,要么其中某一个操作失败,则整个事务中的四条操作都回滚,汇款失败;
2. 一致性表示当汇款结束时,A账户和B账户里的余额变化和操作日志记录是可以对应起来的;
3. 独立性表示当汇款操作过程中如果有C账户也在往B账户里汇款的话,两个事务相互不影响,即A->B有四个独立操作,C->B有四个独立操作;
4. 持久性表示当汇款成功时,A和B的余额就变更了,不管是数据库重启还是什么原因,该数据已经写入到磁盘中作为永久存储,不会再变化,除非有新的事务
其中事务的独立性(隔离性)是通过MySQL锁机制实现
原子性,一致性,持久性则通过MySQL的redo和undo日志记录来完成

2. InnoDB 和 ACID 模型

mysql> select * from score where sid=1;
+-----+-----------+-------+
| sid | course_id | score |
+-----+-----------+-------+
|   1 |         1 |    76 |
|   1 |         2 |    90 |
|   1 |         3 |    82 |
|   1 |         5 |    56 |
+-----+-----------+-------+

mysql> update score set score=90 where sid=1;

mysql> select * from score where sid=1;
+-----+-----------+-------+
| sid | course_id | score |
+-----+-----------+-------+
|   1 |         1 |    90 |
|   1 |         2 |    90 |
|   1 |         3 |    90 |
|   1 |         5 |    90 |
+-----+-----------+-------+

mysql> rollback;

mysql> select * from score where sid=1;     #----> rollbck不起作用
+-----+-----------+-------+
| sid | course_id | score |
+-----+-----------+-------+
|   1 |         1 |    90 |
|   1 |         2 |    90 |
|   1 |         3 |    90 |
|   1 |         5 |    90 |
+-----+-----------+-------+

# 原因:
mysql> show variables like '%autocommit%';  # 默认自动提交,回滚失效
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
实现事务回滚,方法一:
mysql> set autocommit=0;   # 重新登录,设置失效

mysql> update score set score=85 where sid=1;

mysql> select * from score where sid=1;
+-----+-----------+-------+
| sid | course_id | score |
+-----+-----------+-------+
|   1 |         1 |    85 |
|   1 |         2 |    85 |
|   1 |         3 |    85 |
|   1 |         5 |    85 |
+-----+-----------+-------+

mysql> rollback;

mysql> select * from score where sid=1;      # 关闭自动提交,实现回滚
+-----+-----------+-------+
| sid | course_id | score |
+-----+-----------+-------+
|   1 |         1 |    90 |
|   1 |         2 |    90 |
|   1 |         3 |    90 |
|   1 |         5 |    90 |
+-----+-----------+-------+
实现事务回滚,方法二:
mysql> begin;                 # 开启一个事务

mysql> update score set score=85 where sid=1;
Query OK, 4 rows affected (0.00 sec)
Rows matched: 4  Changed: 4  Warnings: 0

mysql> select * from score where sid=1;
+-----+-----------+-------+
| sid | course_id | score |
+-----+-----------+-------+
|   1 |         1 |    85 |
|   1 |         2 |    85 |
|   1 |         3 |    85 |
|   1 |         5 |    85 |
+-----+-----------+-------+
4 rows in set (0.01 sec)

mysql> rollback;
Query OK, 0 rows affected (0.07 sec)

mysql> select * from score where sid=1;
+-----+-----------+-------+
| sid | course_id | score |
+-----+-----------+-------+
|   1 |         1 |    90 |
|   1 |         2 |    90 |
|   1 |         3 |    90 |
|   1 |         5 |    90 |
+-----+-----------+-------+
实现事务回滚,方法三:
mysql> start transaction;      # 开启一个事务

mysql> update score set score=85 where sid=1;

mysql> select * from score where sid=1;
+-----+-----------+-------+
| sid | course_id | score |
+-----+-----------+-------+
|   1 |         1 |    85 |
|   1 |         2 |    85 |
|   1 |         3 |    85 |
|   1 |         5 |    85 |
+-----+-----------+-------+

mysql> rollback;

mysql> select * from score where sid=1;      # 事务执行,最后rollback,回滚数据
+-----+-----------+-------+
| sid | course_id | score |
+-----+-----------+-------+
|   1 |         1 |    90 |
|   1 |         2 |    90 |
|   1 |         3 |    90 |
|   1 |         5 |    90 |
+-----+-----------+-------+
实现事务回滚,方法四:
mysql> start transaction;      # 开启一个事务

mysql> update score set score=85 where sid=1;

mysql> select * from score where sid=1;
+-----+-----------+-------+
| sid | course_id | score |
+-----+-----------+-------+
|   1 |         1 |    85 |
|   1 |         2 |    85 |
|   1 |         3 |    85 |
|   1 |         5 |    85 |
+-----+-----------+-------+
[root@master ~]# mysql

mysql> select * from score where sid=1;    # 重新登录或者重开窗口、数据库异常重启登录,都可实现回滚
+-----+-----------+-------+
| sid | course_id | score |
+-----+-----------+-------+
|   1 |         1 |    90 |
|   1 |         2 |    90 |
|   1 |         3 |    90 |
|   1 |         5 |    90 |
+-----+-----------+-------+

3. InnoDB 多版本控制

为保证并发操作和回滚操作,InnoDB会将修改前的数据存放在回滚段中。

InnoDB会在数据库的每一行上额外增加三个字段以实现多版本控制,

第一个字段是DB_TRX_ID用来存放针对该行最后一次执行insert、update操作的事务ID,而delete操作也会被认为是update,只是会有额外的一位来代表事务为删除操作;
第二个字段是DB_ROLL_PTR指针指向回滚段里对应的undo日志记录;
第三个字段是DB_ROW_ID代表每一行的行ID。

回滚段中的undo日志记录只有在事务commit提交之后才会被丢弃,为避免回滚段越来越大,要注意及时执行commit命令

epub mysql内核innodb mysql内核:innodb存储引擎_mysql

(1)在两个数据库链接下实验多版本控制:
链接1:mysql> start transaction;
链接2:mysql> start transaction;
链接1:mysql> update score set score=88 where sid=1;
链接2:mysql> select * from score where sid=1; ###链接1锁数据未释放,链接2也能访问相同数据
+-----+-----------+-------+
| sid | course_id | score |
+-----+-----------+-------+
|   1 |         1 |    90 |
|   1 |         2 |    90 |
|   1 |         3 |    90 |
|   1 |         5 |    90 |
+-----+-----------+-------+
链接1: mysql>commit;
链接2:mysql> select * from score where sid=1; ###链接1锁释放,但链接2访问到的数据依然是之前的数据
+-----+-----------+-------+
| sid | course_id | score |
+-----+-----------+-------+
|   1 |         1 |    90 |
|   1 |         2 |    90 |
|   1 |         3 |    90 |
|   1 |         5 |    90 |
+-----+-----------+-------+

链接2:mysql> commit;
链接2:mysql> select * from score where sid=1; ###链接2提交之后,再访问到的数据是修改后的数据
+-----+-----------+-------+
| sid | course_id | score |
+-----+-----------+-------+
|   1 |         1 |    88 |
|   1 |         2 |    88 |
|   1 |         3 |    88 |
|   1 |         5 |    88 |
+-----+-----------+-------+
(2)在两个数据库链接下实验锁等待:
链接1:mysql> begin;
链接1:mysql> select * from score where sid=1;
+-----+-----------+-------+
| sid | course_id | score |
+-----+-----------+-------+
|   1 |         1 |    90 |
|   1 |         2 |    90 |
|   1 |         3 |    90 |
|   1 |         5 |    90 |
+-----+-----------+-------+
链接1:mysql> update score set score=88 where sid=1;

链接2:mysql> update score set score=85 where sid=1; ###链接1锁未释放,链接2等待锁释放

链接1:mysql>commit;
链接2:mysql> update score set score=85 where sid=1; ###链接1提交锁释放,链接2执行成功

链接1:mysql> select * from score where sid=1;
+-----+-----------+-------+
| sid | course_id | score |
+-----+-----------+-------+
|   1 |         1 |    85 |
|   1 |         2 |    85 |
|   1 |         3 |    85 |
|   1 |         5 |    85 |
+-----+-----------+-------+

链接2:mysql> select * from score where sid=1;
+-----+-----------+-------+
| sid | course_id | score |
+-----+-----------+-------+
|   1 |         1 |    85 |
|   1 |         2 |    85 |
|   1 |         3 |    85 |
|   1 |         5 |    85 |
+-----+-----------+-------+

4.InnoDB 体系结构

epub mysql内核innodb mysql内核:innodb存储引擎_epub mysql内核innodb_02

epub mysql内核innodb mysql内核:innodb存储引擎_回滚_03

特点:
根据主键寻址速度很快
主键值递增的insert插入效率较好
主键值随机insert插入操作效率差

epub mysql内核innodb mysql内核:innodb存储引擎_epub mysql内核innodb_04

epub mysql内核innodb mysql内核:innodb存储引擎_epub mysql内核innodb_05

• 缓存池
buffer pool缓存池是InnoDB在内存中开辟的用来缓存表数据和索引数据的区域,一般可以设置为50%~80%的物理内存大小,通过对经常访问的数据放置到内存当中来加快访问速度。
buffer pool以page页的格式组成,页之间组成list列表,并通过LRU算法(最近最少使用算法)对长久不使用的页进行置换。
数据的读写需要经过缓存(缓存在buffer pool
数据以整页(16K)位单位读取到缓存中
缓存中的数据以LRU策略换出(最少使用策略)
IO效率高,性能好

• Adaptive Hash Index(自适应哈希索引)
adaptive Hash index属性使得InnoDB更像是内存数据库。
该属性通过innodb_adapitve_hash_index开启,也可以通过skip-innodb_adaptive_hash_index参数关闭。
InnoDB存储引擎会监控对表上索引的查找,如果观察到建立哈希索引可以带来速度的提升,则建立哈希索引,所以称之为自适应(adaptive)的。
自适应哈希索引通过缓冲池的B+树构造而来,因此建立的速度很快。而且不需要将整个表都建哈希索引,InnoDB存储引擎会自动根据访问的频率和模式来为某些页建立哈希索引。

• 自适应哈希索引
哈希(hash)是一种非常快的等值查找方法,在一般情况下这种查找的时间复杂度为O(1),即一般仅需要一次查找就能定位数据。
而B+树的查找次数,取决于B+树的高度,在生产环境中,B+树的高度一般3-4层,故需要3-4次的查询。
InnoDB会监控对表上个索引页的查询。如果观察到建立哈希索引可以带来速度提升,则自动建立哈希索引,称之为自适应哈希索引(Adaptive Hash Index,AHI)。
AHI有一个要求,就是对这个页的连续访问模式必须是一样的。
例如对于(a,b)访问模式情况:
where a = xxx
where a = xxx and b = xxx
AHI启动后,读写速度提高了2倍,辅助索引的连接操作性能可以提高5倍。
AHI是数据库自动优化的,DBA只需要指导开发人员去尽量使用符合AHI条件的查询,以提高效率。

• Redo log buffer
redo log buffer是一块用来存放写入redo log文件内容的内存区域,内存的大小由innodb_log_buffer_size参数确定。该buffer的内容会定期刷新到磁盘的redo log文件中。
参数innodb_flush_log_at_trx_commit决定了刷新到文件的方式,参数innodb_flush_log_at_timeout参数决定了刷新的频率。

• 系统表空间
InnoDB的系统表空间用来存放表和索引数据,同时也是doublewriter缓存,change缓存和回滚日志的存储空间,系统表空间是被多个表共享的表空间。
默认情况下,系统表空间只有一个系统数据文件,名为ibdata1。
系统数据文件的位置和个数由参数innodb_data_file_path参数决定。

• Doublewrite缓存
doublewrite缓存是位于系统表空间的存储区域,用来缓存InnoDB的数据页从innodb buffer pool中flush之后并写入到数据文件之前,所以当操作系统或者数据库进程在数据页写磁盘的过程中崩溃,Innodb可以在doublewrite缓存中找到数据页的备份而用来执行crash恢复。
数据页写入到doublewrite缓存的动作所需要的IO消耗要小于写入到数据文件的消耗,因为此写入操作会以一次大的连续块的方式写入。

在应用(apply)重做日志前,用户需要一个页的副本,当写入失效发生时,先通过页的副本来还原该页,再进行重做,这就是double write
doublewrite组成:
doublewrite buffer,大小2M,
(extend),大小同样为2M。
对缓冲池的脏页进行刷新时,不是直接写磁盘,而是会通过memcpy()函数将脏页先复制到内存中的doublewrite buffer,之后通过doublewrite再分两次,每次1M顺序地写入共享表空间的物理磁盘上,在这个过程中,因为doublewrite页是连续的,因此这个过程是顺序写的,开销并不是很大。在完成doublewrite页的写入后,再将doublewrite buffer中的页写入各个表空间文件中,此时的写入则是离散的。如果操作系统在将页写入磁盘的过程中发生了崩溃,在恢复过程中,innodb可以从共享表空间中的doublewrite中找到该页的一个副本,将其复制到表空间文件,再应用重做日志。

epub mysql内核innodb mysql内核:innodb存储引擎_回滚_06

• Undo日志
undo日志是由一系列事务的undo日志记录组成,每一条undo日志记录包含了事务数据回滚的相关原始信息,所以当其它的事务需要查看修改前的原始数据,则会从此undo日志记录中获取。
undo日志存放在回滚段中的undo日志段中。
默认情况下回滚段是作为系统表空间的一部分,但也可以有自己独立的undo表空间,通过设置innodb_undo_tablespaces和innodb_undo_directory两个参数。
Innodb支持最大128个回滚段,其中的32个用来服务临时表的相关事务操作,剩下的96个服务非临时表,每个回滚段可以同时支持1023个数据修改事务,也就是总共96K个数据修改事务。
Innodb_undo_logs参数用来设置回滚段的个数。
Undo Log的原理很简单,为了满足事务的原子性,在操作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为Undo Log),然后进行数据的修改。如果出现了错误或者用户执行了rollback语句,系统可以利用Undo Log中的备份将数据恢复到事务开始之前的状态

• File-per-table表空间
file-per-table表空间意味着InnoDB的数据表不是共享一个系统表空间,而是每个表一个独立的表空间。
可以通过设置innodb_file_per_table开启此属性。
开启之后每个表数据和索引数据都会默认单独存放在数据文件夹下的.ibd数据文件中。

mysql> show variables like '%per_table%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_file_per_table | ON    |
+-----------------------+-------+
mysql> use course;
mysql> create table temp_123(id int,name varchar(10));
mysql> insert into temp_123 value(1,'a'),(2,'b');
mysql> commit;
ls /data/3306/course
temp_123.ibd

mysql> Alter table temp_123 tablespace=innodb_system;ls /data/3306
ibdata1

mysql> create table temp_234(id int,name varchar(10)) tablespace=innodb_system;
mysql> insert into temp_234 values(1,'a'),(2,'b');ls /data/3306
ibdata1

• Temporary表空间
temporary临时表空间用来存放临时表,默认情况下是在数据文件夹下的ibtmp1数据文件,此数据文件被设置为每次自动增长12MB大小,当然也可以设置innodb_temp_data_file_path来指定临时表空间文件的存放位置。
临时表空间文件在正常的shutdown之后会自动清除,但在crash发生时不会清除,这就需要DBA手动去删除表空间文件或重启服务器。

mysql> show variables like '%innodb_temp%';
+-----------------------------+-----------------------+
| Variable_name               | Value                 |
+-----------------------------+-----------------------+
| innodb_temp_data_file_path  | ibtmp1:12M:autoextend |
| innodb_temp_tablespaces_dir | ./#innodb_temp/       |
+-----------------------------+-----------------------+

如果发现临时表空间数据文件比较大,可以考虑重启MySQL来释放空间大小。

• redo log
redo日志是存在于磁盘上的文件,包括ib_logfile0和ib_logfile1两个文件,常用于在crash恢复发生时将还没来得及写入到数据文件中但已经完成提交的事务在数据库初始化时重新执行一遍。
InnoDB对redo log buffer写入到redo log文件的方式提供了组提交(group commit)的方式,意味着针对一次写磁盘操作可以包含多个事务数据,用此方法提高性能。
为了IO效率,数据库修改的文件都在内存缓存中完成的;那么我们知道一旦断电,内存中的数据将消失,而数据库是如何保证数据的完整性?那就是数据持久化与事务日志
如果宕机了则:应用已经持久化好了的日志文件,读取日志文件中没有被持久化到数据文件里面的记录,将这些记录重新持久化到我们的数据文件中

epub mysql内核innodb mysql内核:innodb存储引擎_epub mysql内核innodb_07

innodb日志持久化相关参数innodb_flush_log_at_trx_commit
0:每秒写入并持久化一次(不安全,性能高,无论mysql或服务器宕机,都会丢数据最多1秒的数据)
1:每次commit都持久化(安全,性能低,IO负担重)
2:每次commit都写入内存的内存缓存,每秒再刷新到磁盘(安全,性能折中,mysql宕机数据不会丢失,服务器宕机数据会丢失最多1秒的数据)
innodb_flush_log_at_timeout参数决定最多丢失多少秒的数据,默认是1秒

5.InnoDB

启动配置
InnoDB合理的规划方法是在创建数据库实例之前就定义好数据文件,日志文件和数据页大小等相关属性

指定配置文件位置
MySQL实例启动需要依赖my.cnf配置文件,而配置文件可以存在于多个操作系统目录下my.cnf文件的默认查找路径,从上到下找到的文件先读,但优先级逐级提升

epub mysql内核innodb mysql内核:innodb存储引擎_epub mysql内核innodb_08


 MySQL 指定配置文件位置

• 系统表空间数据文件配置
可以通过innodb_data_file_path和innodb_data_home_dir来配置系统表空间数据文件

Innodb_data_file_path可以包含一个或多个数据文件,中间用;号分开
innodb_data_file_path=datafile_spec1[;datafile_spec2]...
datafile_spec1 = file_name:file_size[:autoextend[:max:max_file_size]]
其中autoextend和max选项只能用作最后的这个数据文件。
  autoextend默认情况下是一次增加64MB,如果要修改此值可通过innodb_autoextend_increment参数。
  max用来指定可扩展数据文件的最大容量用来避免数据文件大小超过可用磁盘空间大小。
mysql> show variables like '%innodb_data%';
+-----------------------+------------------------+
| Variable_name         | Value                  |
+-----------------------+------------------------+
| innodb_data_file_path | ibdata1:12M:autoextend |
| innodb_data_home_dir  |                        |
+-----------------------+------------------------+
[mysqld]
innodb_data_file_path=ibdata1:50M;ibdata2:50M:autoextend
表示指定ibdata1和ibdata2两个数据文件,其中ibdata1文件为固定的50M大小,而ibdata2文件初始化为50M并可自动扩展容量

[mysqld]
innodb_data_file_path=ibdata1:12M:autoextend:max:500M
其中:autoextend和max选项只能用作最后的这个数据文件

innodb_data_home_dir参数用来显示指定数据文件的存储目录,默认是MySQL安装后的数据文件目录,举例如下:
[mysqld]
innodb_data_home_dir = /path/to/myibdata/
innodb_data_file_path=ibdata1:50M;ibdata2:50M:autoextend

当然也可以在innodb_data_file_path中指定绝对路径的数据文件
[mysqld]
innodb_data_home_dir =
innodb_data_file_path=/path/to/myibdata/ibdata1:50M;/path/to/myibdata/ibdata2:50M:autoextend
[mysqld]
innodb_data_file_path=ibdata1:12M;ibdata2:50M:autoextend
表示指定ibdata1和ibdata2两个数据文件,其中ibdata1文件为固定的12M大小,而ibdata2文件初始化为50M并可自动扩展容量

/etc/init.d/mysql.server restart

ll /usr/local/mysql/data-rw-r----- 1 mysql mysql 12582912 Apr 28 19:25 ibdata1
-rw-r----- 1 mysql mysql 52428800 Apr 28 19:25 ibdata2
[mysqld]
innodb_data_file_path=ibdata1:30M;ibdata2:50M:autoextend                                           

/etc/init.d/mysql.server restart
ERROR! MySQL server PID file could not be found!
Starting MySQL. ERROR! The server quit without updating PID file (usr/local/mysql/data/mysql-master.pid).

cat abc.log
2020-04-28T11:24:04.791505Z 0 [ERROR] [MY-012261] [InnoDB] File Path Specification: 'ibdata1:12M:ibdata2:50M:autoextend' has unrecognized characters after 'ibdata1:12M'
[mysqld]
innodb_data_file_path=ibdata1:12M:autoextend;ibdata2:50M:autoextend

cat abc.log
2020-04-28T11:33:43.540220Z 1 [ERROR] [MY-012264] [InnoDB] The innodb_system data file './ibdata1' is of a different size 768 pages (rounded down to MB) than the 1920 pages specified in the .cnf file!
[mysqld]
innodb_data_file_path=ibdata1:12M;ibdata2:50M:autoextend                                        

/etc/init.d/mysql.server restart

mysql> show variables like '%innodb_data%';
+-----------------------+--------------------------------------------+
| Variable_name         | Value                                      |
+-----------------------+--------------------------------------------+
| innodb_data_file_path | ibdata1:12M;ibdata2:50M:autoextend:max:20G |
| innodb_data_home_dir  |                                            |
+-----------------------+--------------------------------------------+

• 日志文件配置
默认情况下InnoDB会在数据文件夹下创建两个48M的日志文件,分别是ib_logfile0和ib_logfile1。

innodb_log_files_in_group  参数用来定义日志文件的个数,默认和推荐值都是2
innodb_log_file_size  参数定义了每个日志文件的大小,日志文件越大意味着buffer pool进行文件间切换的操作越少,从而减少IO,一般至少要保证在高峰期的1小时内的所有日志都能存放在一个日志文件里而不发生切换,当然文件大小也有最大限制,就是所有日志文件的总大小不能超过512G。

mysql> show variables like '%innodb_log_file%';
+---------------------------+----------+
| Variable_name             | Value    |
+---------------------------+----------+
| innodb_log_file_size      | 50331648 |
| innodb_log_files_in_group | 2        |
+---------------------------+----------+

innodb_log_group_home_dir参数用来定义redo日志的文件位置

[mysqld]
innodb_log_group_home_dir = /dr3/iblogs

• Undo 表空间配置
默认情况下,undo日志是存放在系统表空间里,但也可以选择在独立的一个或多个undo表空间中存放undo日志
innodb_undo_directory 参数决定了独立的undo表空间存放目录
innodb_undo_logs
innodb_undo_tablespaces 参数决定了独立undo表空间的个数,比如设置为16时则会在undo表空间存放目录下创建16个undo文件,默认为10M

mysql> show variables like '%innodb_undo%';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_undo_directory    | ./    |
| innodb_undo_log_encrypt  | OFF   |
| innodb_undo_log_truncate | ON    |
| innodb_undo_tablespaces  | 2     |
+--------------------------+-------+

• 临时表空间配置
默认情况下,InnoDB会创建一个自增长的ibtmp1文件在数据文件夹下作为临时表空间数据文件。
innodb_temp_data_file_path参数可以指定文件路径,文件名和文件大小

mysql> show variables like '%innodb_temp%';
+-----------------------------+-----------------------+
| Variable_name               | Value                 |
+-----------------------------+-----------------------+
| innodb_temp_data_file_path  | ibtmp1:12M:autoextend |
| innodb_temp_tablespaces_dir | ./#innodb_temp/       |
+-----------------------------+-----------------------+

• 数据页配置
innodb_page_size参数用来指定所有InnoDB数据页大小。默认是16K大小,也可以设置为64K、32K、8K和4K。一般设置为存储磁盘的block size接近的大小.
    • 内存相关配置
innodb_buffer_pool_size参数确定了缓存表数据和索引数据的内存区域大小,默认为128M,推荐设置为系统内存的50%~80%。
在服务器有大量内存的情况下,也可以设置多个缓存以提高系统并发度:
innodb_buffer_pool_instances参数就是用来做这个设置。
innodb_log_buffer_size参数确定了redo log缓存的大小,默认值是16M,其大小取决于是否有某些大的事务会大量修改数据而导致在事务执行过程中就要写日志文件。

• InnoDB 只读设置
InnoDB可以通过innodb-read-only参数设置数据表只能读取

[mysqld]
innodb-read-only=1

mysql> update temp set id2=100;
ERROR 1015 (HY000): Can't lock file (errno: 165 - Table is read only)

6. InnoDB buffer pool配置

mysql> show variables like '%innodb_buffer%';
+-------------------------------------+----------------+
| Variable_name                       | Value          |
+-------------------------------------+----------------+
| innodb_buffer_pool_chunk_size       | 134217728      |
| innodb_buffer_pool_dump_at_shutdown | ON             |
| innodb_buffer_pool_dump_now         | OFF            |
| innodb_buffer_pool_dump_pct         | 25             |
| innodb_buffer_pool_filename         | ib_buffer_pool |
| innodb_buffer_pool_instances        | 1              |
| innodb_buffer_pool_load_abort       | OFF            |
| innodb_buffer_pool_load_at_startup  | ON             |
| innodb_buffer_pool_load_now         | OFF            |
| innodb_buffer_pool_size             | 134217728      |
+-------------------------------------+----------------+

• InnoDB buffer pool设置
buffer pool是内存中用来缓存数据和索引的存储区域,其是MySQL性能调优的重要一环。
理想情况下,设置的size越大,则缓存到内存的数据越多,InnoDB就越像是内存数据库。
buffer pool的底层是一个列表,通过LRU算法进行数据页的换进换出操作。当空间原因导致新页的加入需要换出一页时,InnoDB取出最近最少使用的页并将这个新的数据页加入到列表的中央。从方向上看,列表的头部是最常使用的数据页,而在尾部则是最少使用的数据页。
buffer pool中3/8的部分是保存最少使用的数据页,而中央部分其实是经常使用和最少使用的结合点。当在最少使用中保存的数据页被访问时,则数据页就会被移动到列表的头部变成最常使用的。

• 配置大小
InnoDB buffer pool的大小可以在启动时配置,也可以在启动之后配置。
增加和减少buffer pool的大小都是以大块的方式,块的大小由参数innodb_buffer_pool_chunk_size决定,默认为128M。
innodb_buffer_pool_size的大小可以自行设定,但必须是innodb_buffer_pool_chunk_size*innodb_buffer_pool_instances的整数倍,如果不是,则buffer pool会被调整成大于设定值且最接近的一个值,如下例:

mysqld --innodb_buffer_pool_size=9G --innodb_buffer_pool_instances=16
 
mysql> SELECT @@innodb_buffer_pool_size/1024/1024/1024;
+------------------------------------------+
| @@innodb_buffer_pool_size/1024/1024/1024 |
+------------------------------------------+
|                          10.000000000000 |
+------------------------------------------+

innodb_buffer_pool_chunk_size可以自行设定,且增加和减少都要以M为单位,并只能在启动前修改,修改后的值*innodb_buffer_pool_instances不能大于buffer pool的大小,否则修改无效。

[mysqld]
innodb_buffer_pool_chunk_size=134217728
buffer pool的大小可以动态修改,用set语句直接修改,当语句发起时,会一直等到当前所有的事务结束后才执行,而一旦执行则执行过程中的其他事务如果要访问buffer pool就会等待语句执行完毕。

mysql> SET GLOBAL innodb_buffer_pool_size=402653184;

当执行online的调整大小时,可以通过error log或者innodb_buffer_pool_resize_status查看进度
mysql> SHOW STATUS WHERE Variable_name='InnoDB_buffer_pool_resize_status';
+----------------------------------+----------------------------------+
| Variable_name                    | Value                            |
+----------------------------------+----------------------------------+
| Innodb_buffer_pool_resize_status | Resizing also other hash tables. |
+----------------------------------+----------------------------------+
mysql> set global innodb_buffer_pool_size=200*1024*1024;
Query OK, 0 rows affected, 1 warning (0.02 sec)

mysql> show variables like '%innodb_buffer_pool_size%';
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| innodb_buffer_pool_size | 268435456 |
+-------------------------+-----------+
1 row in set (0.05 sec)

mysql> select 268435456/1024/1024;
+---------------------+
| 268435456/1024/1024 |
+---------------------+
|        256.00000000 |
+---------------------+
1 row in set (0.00 sec)

mysql> select 134217728/1024/1024;
+---------------------+
| 134217728/1024/1024 |
+---------------------+
|        128.00000000 |
+---------------------+
1 row in set (0.00 sec)

mysql> select 128,128*2,128*3;
+-----+-------+-------+
| 128 | 128*2 | 128*3 |
+-----+-------+-------+
| 128 |   256 |   384 |
+-----+-------+-------+
1 row in set (0.00 sec)

• 配置多个buffer pool实例
当buffer pool的大小是GB级别时,将一个buffer pool分割成几个独立的实例能降低多个线程同时读写缓存页的竞争性而提高并发性。
通过innodb_buffer_pool_instances参数可以调整实例个数。如果有多个实例,则缓存的数据页会随机放置到任意的实例中,且每个实例都有独立的buffer pool所有的特性。
innodb_buffer_pool_instances的默认值是1,最大可以调整成64。

•  Making the Buffer Pool Scan Resistant
新读取的数据页被插入到buffer pool的LRU列表的中间位置,默认位置是从尾部开始算起的3/8的位置。当被放入buffer pool的页被第一次访问时就开始往列表的前方移动,而这样列表的后部就是不经常访问的页甚至是从不访问的页。
通过参数innodb_old_blocks_pct可以控制列表中"old"数据页所占的百分比,默认是37%,等同于3/8,取值范围是5~95。
innodb_old_blocks_time参数默认是1000毫秒,指定了页面读取到buffer pool后但没有移动到经常被访问列表位置的时间窗口。

• InnoDB buffer pool预存取(read-ahead)
read ahead是异步地预先获取多个数据页到buffer pool的IO操作,这些数据页都是假定会随后被用到的。
InnoDB通过两种read-ahead算法提高IO性能:
(1)线性read ahead:预测哪些页会被顺序访问。通过innodb_read_ahead_threshold参数调整顺序数据页的数量。当从一个区中顺序读取的页数量大于等于innodb_read_ahead_threshold时,innodb会触发异步read ahead操作将真个区都读到buffer pool中。该参数的默认值是56,取值范围是0~64。
(2)随机read ahead:通过已经在buffer pool中的数据页来预测哪些页会被随后访问到。如果13个连续的处于相同区的页存在于buffer pool中,则InnoDB会把同一个区的其它页都读取进来。通过设置innodb_random_read_ahead=ON来开启此方式。
通过执行show engine innodb status命令显示的三个参数判断read-ahead算法的有效性:
innodb_buffer_pool_read_ahead
innodb_buffer_pool_read_ahead_evicted
innodb_buffer_pool_read_ahead_rnd

• InnoDB buffer pool flushing配置
InnoDB会在后台将buffer pool中的脏页(已经修改但没有写到数据文件)flush掉。
当buffer pool中的脏页所占百分比达到innodb_max_dirty_pages_pct_lvm会触发flush;
当buffer pool中的脏页所占比例达到innodb_max_dirty_pages_pct时,则innodb会“强烈”的flush。
针对数据修改操作频繁的系统,flush可能会严重滞后导致有大量的buffer pool内存占用,有一些参数专门针对修改繁忙的系统可以调整:
innodb_adaptive_flushing_lwm    :为防止redo log被填满,此参数设置一个阈值,如果redo log的容量超过此阈值,则执行adaptive flush操作。
innodb_max_drity_pages_pct_lwm
innodb_io_capacity_max
innodb_flushing_avg_loops

• 重置buffer pool状态
InnoDB可以通过配置innodb_buffer_pool_dump_at_shutdown参数来确保在mysql正常重启时部分经常使用的数据页能直接加载到buffer pool中,通过批量加载的方式,以节省重启mysql导致的warmup时间(原先在buffer pool中的数据页要从磁盘再次加载到内存中)。
buffer pool的状态可以在任意时刻被保存,而重置状态也可以恢复任意保存的副本。
(1)在数据库运行期间动态配置buffer pool数据页保留占比的方式是:

SET GLOBAL innodb_buffer_pool_dump_pct=40;

(2)在配置文件中的配置方法为:

[mysqld]
innodb_buffer_pool_dump_pct=40

配置当服务器关闭时保存buffer pool的当前状态的方法是:
SET GLOBAL innodb_buffer_pool_dump_at_shutdown=ON;

配置当服务器开启时重新加载buffer pool的方法是:
mysqld --innodb_buffer_pool_load_at_startup=ON;

默认情况下innodb_buffer_pool_dump_at_shutdown和innodb_buffer_pool_load_at_startup两个配置是开启状态
在关闭MySQL时,会把内存中的热数据保存在磁盘里ib_buffer_pool文件中,位于数据目录下。

数据库运行期间保存和重新加载buffer pool的方法是:

SET GLOBAL innodb_buffer_pool_dump_now=ON;
SET GLOBAL innodb_buffer_pool_load_now=ON;

查看buffer pool保存和重新加载的进度的方法是:

mysql> SHOW STATUS LIKE 'Innodb_buffer_pool_dump_status';
+--------------------------------+------------------------------------+
| Variable_name                  | Value                              |
+--------------------------------+------------------------------------+
| Innodb_buffer_pool_dump_status | Dumping of buffer pool not started |
+--------------------------------+------------------------------------+

mysql> SHOW STATUS LIKE 'Innodb_buffer_pool_load_status';
+--------------------------------+--------------------------------------------------+
| Variable_name                  | Value                                            |
+--------------------------------+--------------------------------------------------+
| Innodb_buffer_pool_load_status | Buffer pool(s) load completed at 200806 11:51:17 |
+--------------------------------+--------------------------------------------------+

• 监控buffer pool的状态情况
通过show engine innodb status命令可以查看buffer pool的运行情况

----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 137428992
Dictionary memory allocated 426692
Buffer pool size   8192
Free buffers       7196
Database pages     991
Old database pages 385
Modified db pages  0
Pending reads      0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 848, created 143, written 203
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 991, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]

7. InnoDB其他配置

• InnoDB change buffer设置
change buffering是MySQL5.5加入的新特性,change buffering是insert buffer的加强,insert buffer只针对insert有效,change buffering对insert、delete、update(delete+insert)、purge都有效。
当修改一个索引块(secondary index)时的数据时,索引块在buffter pool中不存在,修改信息就会被cache在change buffer中,当通过索引扫描把需要的索引块读取到buffer pool时,会和change buffer中修改信息合并,再择机写回disk。
目的还是为了减少随机IO带来性能损耗
change buffer是作为buffer pool中的一部分存在。
innodb_change_buffering参数缓存所对应的操作:(update会被认为是delete+insert)
all   : 默认值,缓存insert, delete, purges操作
none   : 不缓存
inserts   : 缓存insert操作
deletes  : 缓存delete操作
changes   : 缓存insert和delete操作
purges  : 缓存后台执行的物理删除操作
innodb_change_buffer_max_size参数配置change buffer在buffer pool中所占的最大百分比,默认是25%,最大可以设置为50%。当MySQL实例中有大量的修改操作时,要考虑增大innodb_change_buffer_max_size。

• InnoDB线程并发度配置
InnoDB利用操作系统的线程技术达到多线程实现。
innodb_thread_concurrency参数限制同时执行的线程数。默认值是0代表没有限制。
innodb_thread_sleep_delay参数确定

    • InnoDB后台IO线程配置
通过配置innodb_read_io_threads和innodb_write_io_threads参数来指定后台读和写数据页的线程的个数,默认值是4,容许的取值范围是1-64。

mysql> show engine innodb status\G--------
FILE I/O
--------
I/O thread 0 state: waiting for completed aio requests (insert buffer thread)
I/O thread 1 state: waiting for completed aio requests (log thread)
I/O thread 2 state: waiting for completed aio requests (read thread)
I/O thread 3 state: waiting for completed aio requests (read thread)
I/O thread 4 state: waiting for completed aio requests (read thread)
I/O thread 5 state: waiting for completed aio requests (read thread)
I/O thread 6 state: waiting for completed aio requests (write thread)
I/O thread 7 state: waiting for completed aio requests (write thread)
I/O thread 8 state: waiting for completed aio requests (write thread)
I/O thread 9 state: waiting for completed aio requests (write thread)
Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,
 ibuf aio reads:, log i/o's:, sync i/o's:
Pending flushes (fsync) log: 0; buffer pool: 0
873 OS file reads, 319 OS file writes, 112 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s

• 使用Linux异步IO
InnoDB在Linux平台使用异步IO子系统完成数据文件页的读写请求,可以通过innodb_user_native_aio参数控制,默认是开启状态,并且需要libaio系统库支持。
    •  InnoDB主线程配置
InnoDB的主线程在后台承担了诸多的任务,绝大多数是和IO操作相关的,比如将buffer pool中的修改后的数据刷新的磁盘文件中。
Innodb_io_capacity参数设置了InnoDB的整体IO能力。该参数应该被设置为等同于操作系统每秒的IO操作数量。该参数可以设置为100及以上的任意数值,默认值是200。其中设置为100相当于7200RPM的磁盘性能。

• InnoDB purge配置
InnoDB的purge操作是一类垃圾回收操作,是由一个或多个独立线程自动执行。
通过innodb_purge_threads参数设置purge线程的数量,如果DML操作比较复杂且涉及到多个表时,则可以考虑增加此值,最大可以设置为32。
事务被提交后,其所使用的undolog可能不再需要,因此需要PurgeThread来回收已经使用并分配的undo页.
    • InnoDB
InnoDB表的优化器统计信息分为永久和非永久两种。
永久的优化器统计信息即使是服务器重启的情况下也会存在,其用来选出更优的执行计划以便提供更好的查询性能。
通过配置innodb_stats_auto_recalc参数来控制统计信息是否在表发生巨大变化(超过10%的行)之后是否自动更新,但由于自动更新统计信息本身是异步的,所以有时未必能马上更新,这是可以执行analyze table语句来同步更新统计信息。

create table和alter table语句中的stats_persistent, stats_auto_recalc, stats_sample_pages子句可用来配置单个表的优化器统计信息规则
stats_persistent用来指定是否对此表开启永久统计资料,1代表开启,0代表不开启。当开启之后,可以执行analyze table命令来收集统计资料。
stats_auto_recalc表示是否自动对表的永久统计资料进行重新计算,默认值和全局参数innodb_stats_auto_recalc一致。1代表当表中数据10%以上更新时重新计算,0代表不自动更新,而是通过analyze table命令重新计算
stats_sample_pages表示当计算索引列的统计资料是需要的索引页的样本数量

CREATE TABLE `t1` (
`id` int(8) NOT NULL auto_increment,
`data` varchar(255),
`date` datetime,
PRIMARY KEY (`id`),
INDEX `DATE_IX` (`date`)
) ENGINE=InnoDB,
STATS_PERSISTENT=1,
STATS_AUTO_RECALC=1,
STATS_SAMPLE_PAGES=25;

mysql> select * from mysql.innodb_table_stats where table_name='students';
+---------------+------------+---------------------+--------+----------------------+--------------------------+
| database_name | table_name | last_update | n_rows | clustered_index_size | sum_of_other_index_sizes |
+---------------+------------+---------------------+--------+----------------------+--------------------------+
| course | students | 2017-05-18 10:48:39 | 0 | 1 | 1 |
+---------------+------------+---------------------+--------+----------------------+--------------------------+
1 row in set (0.00 sec)

mysql> call proc1();
Query OK, 1 row affected (1 min 51.63 sec)

mysql> select * from mysql.innodb_table_stats where table_name='students'; 
+---------------+------------+---------------------+--------+----------------------+--------------------------+
| database_name | table_name | last_update | n_rows | clustered_index_size | sum_of_other_index_sizes |
+---------------+------------+---------------------+--------+----------------------+--------------------------+
| course | students | 2017-05-18 10:51:10 | 96096 | 289 | 97 |
+---------------+------------+---------------------+--------+----------------------+--------------------------+

mysql> select * from mysql.innodb_table_stats where table_name='students';
+---------------+------------+---------------------+--------+----------------------+--------------------------+
| database_name | table_name | last_update | n_rows | clustered_index_size | sum_of_other_index_sizes |
+---------------+------------+---------------------+--------+----------------------+--------------------------+
| course | students | 2017-05-18 10:51:10 | 96096 | 289 | 97 |
+---------------+------------+---------------------+--------+----------------------+--------------------------+
1 row in set (0.00 sec)

mysql> delete from students limit 10000;
Query OK, 10000 rows affected (0.03 sec)

mysql> select * from mysql.innodb_table_stats where table_name='students';
+---------------+------------+---------------------+--------+----------------------+--------------------------+
| database_name | table_name | last_update | n_rows | clustered_index_size | sum_of_other_index_sizes |
+---------------+------------+---------------------+--------+----------------------+--------------------------+
| course | students | 2017-05-18 10:56:22 | 90230 | 289 | 0 |
+---------------+------------+---------------------+--------+----------------------+--------------------------+

优化器永久统计资料数据在系统表mysql.innodb_table_stats和mysql.innodb_index_stats表中存储,这两个表中有个字段last_update可以用来判断统计信息最后更改时间。这两个表的数据也可以被手工更改。当手工更改完数据之后,要执行flush table 表名命令来重新load此表的统计资料。innodb_table_stats表中每个目标表一行记录,而innodb_index_stats表中每个索引会有多条记录

innodb_table_stats表结构:

epub mysql内核innodb mysql内核:innodb存储引擎_epub mysql内核innodb_09

mysql> select * from mysql.innodb_table_stats where table_name='students'\G
*************************** 1. row ***************************
database_name: test
table_name: students
last_update: 2017-04-21 17:12:07
n_rows: 5
clustered_index_size: 1
sum_of_other_index_sizes: 2

innodb_index_stats表结构:

epub mysql内核innodb mysql内核:innodb存储引擎_mysql_10

mysql> select * from mysql.innodb_index_stats where table_name='students';
+---------------+------------+------------+---------------------+--------------+------------+-------------+-----------------------------------+
| database_name | table_name | index_name | last_update         | stat_name    | stat_value | sample_size | stat_description                  |
+---------------+------------+------------+---------------------+--------------+------------+-------------+-----------------------------------+
| course        | students   | PRIMARY    | 2020-08-06 09:52:24 | n_diff_pfx01 |          8 |           1 | sid                               |
| course        | students   | PRIMARY    | 2020-08-06 09:52:24 | n_leaf_pages |          1 |        NULL | Number of leaf pages in the index |
| course        | students   | PRIMARY    | 2020-08-06 09:52:24 | size         |          1 |        NULL | Number of pages in the index      |
| course        | students   | for_1      | 2020-08-06 09:52:24 | n_diff_pfx01 |          3 |           1 | dept_id                           |
| course        | students   | for_1      | 2020-08-06 09:52:24 | n_diff_pfx02 |          8 |           1 | dept_id,sid                       |
| course        | students   | for_1      | 2020-08-06 09:52:24 | n_leaf_pages |          1 |        NULL | Number of leaf pages in the index |
| course        | students   | for_1      | 2020-08-06 09:52:24 | size         |          1 |        NULL | Number of pages in the index      |
+---------------+------------+------------+---------------------+--------------+------------+-------------+-----------------------------------+

stat_name=n_diff_pfxNN参数:当是n_diff_pfx01时stat_value列表示索引第一列上的区别值有几个,当是n_diff_pfx02时stat_value列表示索引第一、二列上的区别值有几个,以此类推。而stat_description列显示了对应的逗号可开的索引列值。
默认情况下永久优化器统计信息的属性是开启的,innodb_stats_persistent=ON;非永久优化器统计信息会在每次服务器重启或者其他一些操作时被清理。

优化器统计信息会被存储在磁盘上,通过设置innodb_stats_persistent=ON参数(默认)。
MySQL的查询优化器会基于评估好的统计资料选择合适的索引参与到执行计划中,而类似analyze table的语句会从索引中随机选取数据页参与到每个索引的基数评估中。而参数innodb_stats_persistent_sample_pages决定了参与评估的数据页的数量,默认值是20。当语句执行的执行计划不是最优选择时,则考虑增加此参数,以便获得正确的统计资料。

当设置innodb_stats_persistent=OFF参数或者对单个表设置stats_persistent=0时,对应的统计资料就仅存在于内存中而非磁盘上,当服务器重启之后统计资料丢失。当然此类统计资料也可以周期性的更新。
比如执行analyze table语句手动刷新统计资料,或者在innodb_stats_on_metadata选项打开之后执行show table status/show index或查询information_schema.tables/statistics表时非永久统计资料会自动更新,当InnoDB检测到1/16的表数据被修改时也会更新。

mysql> alter table students stats_persistent=0;
mysql> truncate table students;
mysql> analyze table students;
+-----------------+---------+----------+----------+
| Table           | Op      | Msg_type | Msg_text |
+-----------------+---------+----------+----------+
| course.students | analyze | status   | OK       |
+-----------------+---------+----------+----------+mysql> select * from mysql.innodb_index_stats where table_name='students'; # 最新统计信息在表中不更新
Empty set (0.00 sec)

mysql> alter table students stats_persistent=1;
mysql> analyze table students;
+-----------------+---------+----------+----------+
| Table           | Op      | Msg_type | Msg_text |
+-----------------+---------+----------+----------+
| course.students | analyze | status   | OK       |
+-----------------+---------+----------+----------+

mysql> select * from mysql.innodb_index_stats where table_name='students';
+---------------+------------+------------+---------------------+--------------+------------+-------------+-----------------------------------+
| database_name | table_name | index_name | last_update         | stat_name    | stat_value | sample_size | stat_description                  |
+---------------+------------+------------+---------------------+--------------+------------+-------------+-----------------------------------+
| course        | students   | PRIMARY    | 2020-08-07 00:31:50 | n_diff_pfx01 |          0 |           1 | sid                               |
| course        | students   | PRIMARY    | 2020-08-07 00:31:50 | n_leaf_pages |          1 |        NULL | Number of leaf pages in the index |
| course        | students   | PRIMARY    | 2020-08-07 00:31:50 | size         |          1 |        NULL | Number of pages in the index      |
| course        | students   | for_1      | 2020-08-07 00:31:50 | n_diff_pfx01 |          0 |           1 | dept_id                           |
| course        | students   | for_1      | 2020-08-07 00:31:50 | n_diff_pfx02 |          0 |           1 | dept_id,sid                       |
| course        | students   | for_1      | 2020-08-07 00:31:50 | n_leaf_pages |          1 |        NULL | Number of leaf pages in the index |
| course        | students   | for_1      | 2020-08-07 00:31:50 | size         |          1 |        NULL | Number of pages in the index      |
+---------------+------------+------------+---------------------+--------------+------------+-------------+-----------------------------------+

• 索引页之间合并阈值
通过配置merge_threshold来确保当索引页的数据由于删除操作或者修改操作低于阈值,InnoDB会将此索引页和邻近的索引页合并。默认值是50,取值范围是1到50。
merge_threshold参数可以定义在表上,也可以定义在一个独立的索引上。

CREATE TABLE t1 (
id INT,
KEY id_index (id)
) COMMENT='MERGE_THRESHOLD=45';
CREATE TABLE t1 (
id INT,
KEY id_index (id)
);
ALTER TABLE t1 COMMENT='MERGE_THRESHOLD=40'; 
CREATE TABLE t1 (
id INT,
KEY id_index (id) COMMENT 'MERGE_THRESHOLD=40'
);
CREATE TABLE t1 (id INT);
CREATE INDEX id_index ON t1 (id) COMMENT 'MERGE_THRESHOLD=40';

评估merge_threshold参数合理的方法是查看innodb_metrics表里的相关参数,确保发生了较少的索引页合并且合并请求和成功合并的数量相当

mysql> SELECT NAME, COMMENT FROM INFORMATION_SCHEMA.INNODB_METRICS
WHERE NAME like '%index_page_merge%';
+-----------------------------+----------------------------------------+
| NAME | COMMENT |
+-----------------------------+----------------------------------------+
| index_page_merge_attempts | Number of index page merge attempts |
| index_page_merge_successful | Number of successful index page merges |
+-----------------------------+----------------------------------------+

• 重置InnoDB系统表空间
最简单的增加系统表空间的办法就是在初始化阶段配置数据文件的自增长,通过配置最后一个文件的autoextend属性,当数据文件空间不足时默认自动增长64M大小。

也可以通过修改innodb_autoextend_increment参数修改自动增长的大小。
也可以通过增加另一个数据文件方法扩展表空间,步骤如下:
关闭MySQL
检查配置的最后一个数据文件是否是autoextend,如果是则根据当前数据文件的大小去掉自动扩展属性,改成当前大小
在配置文件的innodb_data_file_path参数里增加一个新的数据文件,选择是否自动扩展
启动MySQL
innodb_data_home_dir =
innodb_data_file_path = /ibdata/ibdata1:10M:autoextend
####改成
innodb_data_home_dir =
innodb_data_file_path = /ibdata/ibdata1:988M;/disk2/ibdata2:50M:autoextend

减小系统表空间大小的方法如下:

mysqldump出所有的InnoDB表,包括mysql系统数据库下的五个表
mysql> select table_name from information_schema.tables where table_schema='mysql' and engine='InnoDB';
+----------------------+
| table_name           |
+----------------------+
| innodb_index_stats |
| innodb_table_stats |
| slave_master_info |
| slave_relay_log_info |
| slave_worker_info |
+----------------------+
关闭MySQL
删除所有InnoDB的数据文件和日志文件,包括*.ibd和ib_log文件,还有在MySQL库文件夹下的*.ibd文件
删除所有.frm的InnoDB表文件
在配置文件里配置新的表空间文件
启动MySQL
导入备份出的dump文件

重置InnoDB redo log文件大小

关闭MySQL
通过innodb_log_file_size更改文件大小,通过innodb_log_files_in_group更改文件数量
启动MySQL
innodb_log_file_size=30M
innodb_log_files_in_group=3
[root@vmware1 bin]# /etc/init.d/mysql.server restart

• 配置单表数据文件表空间
InnoDB的单表数据文件表空间代表每个InnoDB表的数据和索引数据都存放在单独的.ibd数据文件中,每个.ibd数据文件代表独立的表空间。此属性通过innodb_file_per_table配置。
此配置的主要优势:
当删除表或者truncate表的时候,意味着对磁盘空间可以回收。而共享表空间时删除一个表时空间不会释放而只是文件里有空闲空间
truncate table命令要比共享表空间快
通过定义create table …data directory=绝对路径,可以将特定的表放在特定的磁盘或者存储空间
可以将单独的表物理拷贝到另外的MySQL实例中
此配置的劣势:
每个表都有未使用的空间,意味着磁盘空间有些浪费

启动单独表空间的方式如下:
[mysqld]
innodb_file_per_table=1
当设置innodb_file_per_table=0时,所有创建的新表都会放置到共享表空间里,除非在create table命令里显示的使用tablespace选项。
将已经存在于共享表空间的表修改为独立表空间的方法:
SET GLOBAL innodb_file_per_table=1;
ALTER TABLE table_name ENGINE=InnoDB;

通过命令create table … data directory=绝对路径可以将单表数据文件创建在另外的目录里。在指定的绝对路径下,会创建数据库名相同的文件夹,里面含有此表的.ibd文件,同时在MySQL的默认数据文件下的数据库名文件夹下会创建table_name.isl文件包含了此表的路径,相当于link文件。

mysql> USE test;
Database changed
mysql> SHOW VARIABLES LIKE 'innodb_file_per_table';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| innodb_file_per_table | ON |
+-----------------------+-------+
1 row in set (0.00 sec)

mysql> set global innodb_file_per_table=0;
Query OK, 0 rows affected (0.00 sec)
mysql> create table students2(id int,name varchar(100));
mysql> set global innodb_file_per_table=1;
Query OK, 0 rows affected (0.00 sec)
mysql> alter table students2 engine=innodb;
root@ip-172-31-18-152:/usr/local/mysql/data/course# ls students2.*
students2.frm students2.ibd

mysql> CREATE TABLE t1 (c1 INT PRIMARY KEY) DATA DIRECTORY = '/alternative/directory';
Query OK, 0 rows affected (0.03 sec)
# MySQL creates a .ibd file for the new table in a subdirectory that corresponding
# to the database namedb_user@ubuntu:~/alternative/directory/test$ lst1.ibd
# MySQL creates a .isl file containing the path name for the table in a directory
# beneath the MySQL data directory db_user@ubuntu:~/mysql/data/test$ ls db.opt t1.frm t1.isl
当没有开启innodb_file_per_table时,可以将tablespace和data directory两个参数配合使用
CREATE TABLE t2 (c1 INT PRIMARY KEY) TABLESPACE = innodb_file_per_table
DATA DIRECTORY = '/alternative/directory';

不管是出于备份复制还是什么原因要将单表复制到另外的数据库实例下,可以使用传输表空间的方法

在原实例下创建表
mysql> use test;
mysql> CREATE TABLE t(c1 INT) engine=InnoDB;
在目标实例下创建表
mysql> use test;
mysql> CREATE TABLE t(c1 INT) engine=InnoDB;
在目标实例下将表的表空间属性去除
mysql> ALTER TABLE t DISCARD TABLESPACE;
此命令对有外键的表不支持,必须首先执行foreign_key_checks=0
在原实例下表加锁仅允许读操作,并生成.cfg元文件
mysql> use test;
mysql> FLUSH TABLES t FOR EXPORT;
将.ibd和.cfg文件拷贝到目标实例的指定目录下
shell> scp /path/to/datadir/test/t.{ibd,cfg} destinationserver:/path/to/datadir/test
• 原实例下释放锁
mysql> use test;
mysql> UNLOCK TABLES;
目标实例下执行导入表空间操作
mysql> use test;
mysql> ALTER TABLE t IMPORT TABLESPACE;

• 设置Undo log独立表空间
默认情况下undo log是存储在系统表空间里,我们也可以将其存放在一个或多个独立表空间下。
innodb_undo_tablespaces参数定义了有多少个undo表空间,此参数只能在建立MySQL实例时被配置
innodb_undo_directory参数定义了undo表空间的存放路径
innodb_undo_logs参数定义了回滚段的数量

mysql> show variables like '%innodb_undo%';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_undo_directory    | ./    |
| innodb_undo_log_encrypt  | OFF   |
| innodb_undo_log_truncate | ON    |
| innodb_undo_tablespaces  | 2     |
+--------------------------+-------+

innodb_undo_log_truncate参数决定是否开启undo表空间清空

mysql> SET GLOBAL innodb_undo_log_truncate=ON;
当设置了此参数为ON后,则代表undo文件大小超过innodb_max_undo_log_size(默认值是128M)的都标记为清空

mysql> SELECT @@innodb_max_undo_log_size;
+----------------------------+
| @@innodb_max_undo_log_size |
+----------------------------+
|                 1073741824 |
+----------------------------+

mysql> SET GLOBAL innodb_max_undo_log_size=2147483648;

当标记为清空后,回滚段标记为非激活状态表示不接收新的事务,而已存在的事务会等到完成;然后通过purge操作将回滚段空间释放;当undo表空间的所有回滚段都释放后,表空间就会清空成初始10M大小;然后回滚段重新变成激活状态以接收新的事务

• InnoDB普通表空间
通过create tablespace命令可以创建一个共享的InnoDB表空间,和系统表空间一样,多个表可以在此表空间上存储数据,此表空间的数据文件可以放置在任意的文件夹下。

CREATE TABLESPACE tablespace_name
ADD DATAFILE 'file_name'
[FILE_BLOCK_SIZE = value]
[ENGINE [=] engine_name]
mysql> CREATE TABLESPACE `ts1` ADD DATAFILE 'ts1.ibd' Engine=InnoDB; ##创建在MySQL数据目录下
mysql> CREATE TABLESPACE `ts1` ADD DATAFILE '/my/tablespace/directory/ts1.ibd' Engine=InnoDB;

当创建完表空间之后,就可以通过create table …tablespace或者alter table … tablespace命令将表增加到此表空间上

mysql> CREATE TABLE t1 (c1 INT PRIMARY KEY) TABLESPACE ts1 ROW_FORMAT=COMPACT;
mysql> ALTER TABLE t2 TABLESPACE ts1;

通过alter table命令可以将InnoDB表在系统表空间、独立表空间和普通表空间之间转化:

ALTER TABLE tbl_name TABLESPACE [=] tablespace_name ##从系统表空间或者独立表空间上转移到普通表空间
ALTER TABLE tbl_name ... TABLESPACE [=] innodb_system ##从普通表空间或者独立表空间上转移到系统表空间
ALTER TABLE tbl_name ... TABLESPACE [=] innodb_file_per_table ##从系统表空间或者普通表空间转移到独立表空间

Alter table … tablespace语句的执行都会导致此表会重建,即使表空间的属性和之前是一样的。

mysql> create tablespace ts1 add datafile
'/usr/local/mysql/data/ts1.ibd';
Query OK, 0 rows affected (0.02 sec)
mysql> use course;
mysql> create table students4(id int,name varchar(10)) tablespace ts1;
Query OK, 0 rows affected (0.00 sec)
mysql> alter table students4 tablespace=innodb_file_per_table;
root@ip-172-31-18-152:/usr/local/mysql/data/course# ls students4.*
students4.frm students4.ibd

当删除一个普通表空间时,首先需要保证此表空间上的所有表都被删除,否则会报错。
删除表空间是用drop tablespace语句来执行。
drop database的动作会删除所有的表,但创建的tablespace不会被自动删除,必须通过drop tablespace显示执行。
普通表空间不支持临时表,而且也不支持alter table … discard tablespace和alter table …import tablespace命令。

mysql> drop tablespace ts1;
ERROR 1529 (HY000): Failed to drop TABLESPACE ts1
mysql> drop table temp123;
Query OK, 0 rows affected (0.00 sec)
mysql> drop tablespace ts1;
Query OK, 0 rows affected (0.01 sec)

• 创建InnoDB表
通过create table语句创建InnoDB表,因为默认存储引擎就是InnoDB,所以不需要在创建表的语句最后指定engine=innodb。

CREATE TABLE `students` (
`sid` int(11) DEFAULT NULL,
`sname` varchar(10) DEFAULT NULL,
`gender` int(11) DEFAULT NULL,
`dept_id` int(11) DEFAULT NULL
)

InnoDB的表数据和索引数据默认是存储在系统表空间中,但可以通过开启innodb_file_per_table选项将表数据和索引数据存放在独立表空间中。当表创建完之后,会在表所在的数据库文件夹里创建.frm文件用来存储表的结构,系统表空间对应的.ibdata文件存储数据文件,而当开启独立表空间时,则会在表所在的数据库文件夹里创建.ibd用来存储表数据和索引数据。

epub mysql内核innodb mysql内核:innodb存储引擎_mysql_11

通过show table status语句可以查看InnoDB的表属性

mysql> show table status like 'students'\G
*************************** 1. row ***************************
           Name: students
         Engine: InnoDB
        Version: 10
     Row_format: Dynamic
           Rows: 8
 Avg_row_length: 2048
    Data_length: 16384
Max_data_length: 0
   Index_length: 16384
      Data_free: 0
 Auto_increment: 9
    Create_time: 2020-08-06 09:52:24
    Update_time: NULL
     Check_time: NULL
      Collation: utf8mb4_0900_ai_ci
       Checksum: NULL
 Create_options: 
        Comment:

• 修改表的存储引擎
通过alter table语句修改已有表的存储引擎

ALTER TABLE table_name ENGINE=InnoDB;

     • 自增长字段设置
当对InnoDB表设置了自增长字段之后,表会在内存中保存一个自增长计数器。
默认情况下自增长字段的初始值是1,但也可以通过配置auto_increment_offset参数将所有的自增长字段初始值设置为另外的值,而当表中插入数值时,InnoDB会求出当前表中的该列的最大值,然后在此基础上加1作为插入的数据。默认是以+1为增长的进度,但也可以通过auto_increment_increment配置所有自增长字段的自定义增长进度。

• InnoDB表主要的限制
InnoDB表目前只支持最多1017个列
InnoDB表目前支持最大64个二级索引
多列索引目前支持最大16个列
如果表中不存在text或者blob类型字段时,行数据整体的最大长度是65535个字节

mysql> CREATE TABLE t (a VARCHAR(8000), b VARCHAR(10000), c VARCHAR(10000), d 
VARCHAR(10000), e VARCHAR(10000),f VARCHAR(10000), g VARCHAR(10000)) 
ENGINE=InnoDB;
ERROR 1118 (42000): Row size too large. The maximum row size for the
used table type, not counting BLOBs, is 65535. You have to change some
columns to TEXT or BLOBs