一、InnoDB存储引擎介绍
1)MySQL从5.5版本开始将InnoDB作为默认存储引擎,该存储引擎是第一个完整支持事务ACID特性的存储引擎,且支持数据行锁,多版本并发控制(MVCC),外键,以及一致性非锁定读。
2)作为默认存储引擎,也就意味着默认创建的表都会使用此存储引擎,除非使用ENGINE=参数指定创建其他存储引擎的表。
1.1、InnoDB的关键属性
InnoDB的关键属性包括:- ACID事务特性支持,包括commit,rollback以及crash恢复的能力
- 行级别锁以及多版本并发控制MVCC
- 利用主键的聚簇索引(clustered index)在底层存储数据,以提升对主键查询的IO性能
- 支持外键功能,管理数据的完整性
InnoDB: 1、事务(Transaction) 2、MVCC(Multi-Version Concurrency Control多版本并发控制) 3、行级锁(Row-level Lock) 4、ACSR(Auto Crash Safe Recovery)自动的故障安全恢复 5、支持热备份(Hot backup) 6、Replication: Group Commit , GTID (Global Transaction ID) ,多线程(SQL Multi-Threads)
事务补充:
START TRANSACTION(或 BEGIN):显式开始一个新事务 SAVEPOINT:分配事务过程中的一个位置,以供将来引用 COMMIT:永久记录当前事务所做的更改 ROLLBACK:取消当前事务所做的更改 ROLLBACK TO SAVEPOINT:取消在 savepoint 之后执行的更改 RELEASE SAVEPOINT:删除 savepoint 标识符 SET AUTOCOMMIT:为当前连接禁用或启用默认 autocommit 模式 -------------------------------------------------------- 用于隐式提交的 SQL 语句: START TRANSACTION SET AUTOCOMMIT = 1 导致提交的非事务语句: DDL语句: (ALTER、CREATE 和 DROP) DCL语句: (GRANT、REVOKE 和 SET PASSWORD) 锁定语句:(LOCK TABLES 和 UNLOCK TABLES) 导致隐式提交的语句示例: TRUNCATE TABLE LOAD DATA INFILE SELECT FOR UPDATE
1.2、ACID模型
ACID模型是关系型数据库普遍支持的事务模型,用来保证数据的一致性,其中的ACID分别代表:
- A:atomicity原子性:事务是一个不可再分割的工作单位,事务中的操作要么都发生,要么都不发生
- C:consistency一致性:事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。这是说数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性
- I:isolation独立性:多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果
- D:durability持续性:在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚
举例来说,比如银行的汇款1000元的操作,简单可以拆分成A账户的余额-1000,B账户的余额+1000,还要分别在A和B的账户流水上记录余额变更日志,这四个操作必须放在一个事务中完成,否则丢失其中的任何一条记录对整个系统来说都是不完整的。 对上述例子来说, 原子性体现在要么四条操作每个都成功,意味着汇款成功,要么其中某一个操作失败,则整个事务中的四条操作都回滚,汇款失败;一致性表示当汇款结束时,A账户和B账户里的余额变化和操作日志记录是可以对应起来的; 独立性表示当汇款操作过程中如果有C账户也在往B账户里汇款的话,两个事务相互不影响,即A->B有四个独立操作,C->B有四个独立操作; 持久性表示当汇款成功时,A和B的余额就变更了,不管是数据库重启还是什么原因,该数据已经写入到磁盘中作为永久存储,不会再变化,除非有新的事务 #其中事务的隔离性是通过MySQL锁机制实现 原子性,一致性,持久性则通过MySQL的redo和undo日志记录来完成
#1.隐时开启事务set autocommit=off; mysql> show variables like '%autocommit%'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ mysql> select * from score; +------+-----------+-------+ | sid | course_id | score | +------+-----------+-------+ | 1 | 1 | 76 | | 1 | 2 | 90 | | 1 | 3 | 82 | | 1 | 5 | 56 | | 2 | 2 | 78 | | 2 | 4 | 92 | | 2 | 3 | 77 | | 2 | 5 | 65 | | 3 | 1 | 48 | | 3 | 2 | 95 | | 3 | 3 | 75 | | 3 | 4 | 89 | | 3 | 5 | 92 | +------+-----------+-------+ mysql> set autocommit=off; mysql> update score set score=90 where sid =1; mysql> select * from score; +------+-----------+-------+ | sid | course_id | score | +------+-----------+-------+ | 1 | 1 | 90 | | 1 | 2 | 90 | | 1 | 3 | 90 | | 1 | 5 | 90 | | 2 | 2 | 78 | | 2 | 4 | 92 | | 2 | 3 | 77 | | 2 | 5 | 65 | | 3 | 1 | 48 | | 3 | 2 | 95 | | 3 | 3 | 75 | | 3 | 4 | 89 | | 3 | 5 | 92 | +------+-----------+-------+ #rollback或者commit mysql> rollback; mysql> select * from score; +------+-----------+-------+ | sid | course_id | score | +------+-----------+-------+ | 1 | 1 | 76 | | 1 | 2 | 90 | | 1 | 3 | 82 | | 1 | 5 | 56 | | 2 | 2 | 78 | | 2 | 4 | 92 | | 2 | 3 | 77 | | 2 | 5 | 65 | | 3 | 1 | 48 | | 3 | 2 | 95 | | 3 | 3 | 75 | | 3 | 4 | 89 | | 3 | 5 | 92 | +------+-----------+-------+ #2.显示开启事务 start transaction;或begin
1.3、InnoDB多版本控制(MVCC)
1)为保证并发操作和回滚操作,InnoDB会将修改前的数据存放在回滚段中。
2)InnoDB会在数据库的每一行上额外增加三个字段以实现多版本控制
- DB_TRX_ID:用来存放针对该行最后一次执行insert、update操作的事务ID,而delete操作也会被认为是update,只是会有额外的一位来代表事务为删除操作;
- DB_ROLL_PTR:指针指向回滚段里对应的undo日志记录;
- DB_ROW_ID:代表每一行的行ID。
3)回滚段中的undo日志记录只有在事务commit提交之后才会被丢弃,为避免回滚段越来越大,要注意及时执行commit命令
#在两个数据库链接下实验多版本控制 链接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也能访问相同数据 链接1:mysql>commit; 链接2:mysql> select * from score where sid=1; ###链接1锁释放,但链接2访问到的数据依然是之前的数据 链接2:mysql> commit; 链接2:mysql> select * from score where sid=1; ###链接2提交之后,再访问到的数据是修改后的数据
1.4、查看存储引擎情况
在MySQL实例中执行show engines命令查看存储引擎情况 Support=YES代表当前支持的存储引擎,DEFAULT代表默认存储引擎
mysql> show engines; +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ | Engine | Support | Comment | Transactions | XA | Savepoints | +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ | MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO | | CSV | YES | CSV storage engine | NO | NO | NO | | MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO | | BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO | | InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES | | PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO | | ARCHIVE | YES | Archive storage engine | NO | NO | NO | | MyISAM | YES | MyISAM storage engine | NO | NO | NO | | FEDERATED | NO | Federated MySQL storage engine | NULL | NULL | NULL | +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
1.5、InnoDB表数据存储格式
特点:
- 根据主键寻址速度很快
- 主键值递增的insert插入效率较好
- 主键值随机insert插入操作效率差
二、InnoDB体系结构
2.1、buffer pool缓存池
1)buffer pool缓存池是InnoDB在内存中开辟的用来缓存表数据和索引数据的区域,一般 可以设置为50%~80%的物理内存大小,通过对经常访问的数据放置到内存当中来加快访 问速度。
2)Buffer pool以page页的格式组成,页之间组成list列表,并通过LRU算法(最近最少使用算法)对长久不使用的页进行置换。
3)数据的读写需要经过缓存(缓存在buffer pool 即在内存中) 数据以整页(16K)位单位读取到缓存中 缓存中的数据以LRU策略换出(最少使用策略) IO效率高,性能好
2.1.1、Adaptive Hash Index(自适应哈希索引)
1)Adaptive Hash index属性使得InnoDB更像是内存数据库。该属性通过innodb_adapitve_hash_index开启,也可以通过—skip-innodb_adaptive_hash_index参数关闭
mysql> show variables like '%hash%'; +----------------------------------+-------+ | Variable_name | Value | +----------------------------------+-------+ | innodb_adaptive_hash_index | ON | | innodb_adaptive_hash_index_parts | 8 | | metadata_locks_hash_instances | 8 | +----------------------------------+-------+
2)InnoDB存储引擎会监控对表上索引的查找,如果观察到建立哈希索引可以带来速度的提升,则建立哈希索引,所以称之为自适应(adaptive) 的。自适应哈希索引通过缓冲池的B+树构造而来,因此建立的速度很快。而且不需要将整个表都建哈希索引,InnoDB存储引擎会自动根据访问的频率和模式 来为某些页建立哈希索引。
3)哈希(hash)是一种非常快的等值查找方法,在一般情况下这种查找的时间复杂度为O(1),即一般仅需要一次查找就能定位数据。 而B+树的查找次数,取决于B+树的高度,在生产环境中,B+树的高度一般3-4层,故需要3-4次的查询。 innodb会监控对表上个索引页的查询。如果观察到建立哈希索引可以带来速度提升,则自动建立哈希索引,称之为自适应哈希索引(Adaptive Hash Index,AHI)。
4)AHI有一个要求,就是对这个页的连续访问模式必须是一样的。 例如对于(a,b)访问模式情况:
- where a = xxx
- where a = xxx and b = xxx
5)AHI启动后,读写速度提高了2倍,辅助索引的连接操作性能可以提高5倍。 AHI,是数据库自动优化的,DBA只需要指导开发人员去尽量使用符合AHI条件的查询,以提高效率
2.2、Redo log buffer
Redo log buffer是一块用来存放写入redo log文件内容的内存区域,内存的大小由innodb_log_buffer_size参数确定。该buffer的内容会定期刷新到磁盘的redo log文件中。
1)参数innodb_flush_log_at_trx_commit决定了刷新到文件的方式,
2)参数innodb_flush_log_at_timeout参数决定了刷新的频率。
2.3、系统表空间
1)InnoDB的系统表空间用来存放表和索引数据,同时也是doublewriter缓存,change缓存和回滚日志(undo log)的存储空间,系统表空间是被多个表共享的表空间。
默认情况下,系统表空间只有一个系统数据文件,名为ibdata1。系统数据文件的位置和个数由参数innodb_data_file_path参数决定。
mysql> show variables like '%innodb_data_file_path%'; +-----------------------+------------------------+ | Variable_name | Value | +-----------------------+------------------------+ | innodb_data_file_path | ibdata1:12M:autoextend | +-----------------------+------------------------+
2.4、Double write缓存
1)Doublewrite缓存是位于系统表空间的存储区域,用来缓存InnoDB的数据页从innodb buffer pool中flush之后并写入到数据文件之前,所以当操作系统或者数据库进程在数据页写磁盘的过程中崩溃,Innodb可以在doublewrite缓存中找到数据页的备份而用来执行crash恢复。
2)数据页写入到doublewrite缓存的动作所需要的IO消耗要小于写入到数据文件的消耗,因为此写入操作会以一次大的连续块的方式写入。
3)在应用(apply)重做日志前,用户需要一个页的副本,当写入失效发生时,先通过页的副本来还原该页,再进行重做,这就是double write
4)doublewrite组成
- 内存中的doublewrite buffer,大小2M
- 物理磁盘上共享表空间中连续的128个页,即2个区(extend),大小同样为2M。
5)double write原理
对缓冲池的脏页进行刷新时,不是直接写磁盘,而是会通过memcpy()函数将脏页先复制到内存中的doublewrite buffer, 之后通过doublewrite 再分两次,每次1M顺序地写入共享表空间的物理磁盘上,在这个过程中,因为doublewrite页是连续的,因此这个过程是顺序写的,开销并不是很大。在完成doublewrite页的写入后,再将doublewrite buffer 中的页写入各个 表空间文件中,此时的写入则是离散的。如果操作系统在将页写入磁盘的过程中发生了崩溃,在恢复过程中,innodb可以从共享表空间中的doublewrite中找到该页的一个副本,将其复制到表空间文件,再应用重做日志。
2.5、Undo日志
1)Undo日志是由一系列事务的undo日志记录组成,每一条undo日志记录包含了事务数据回滚的相关原始信息,所以当其它的事务需要查看修改前的原始数据,则会从此undo日志记录中获取。Undo日志存放在回滚段中的undo日志段中。
2)默认情况下回滚段是作为系统表空间的一部分,但也可以有自己独立的undo表空间,通过设置innodb_undo_tablespaces和innodb_undo_directory两个参数。
3)Innodb支持最大128个回滚段,其中的32个用来服务临时表的相关事务操作,剩下的96个服务非临时表,每个回滚段可以同时支持1023个数据修改事务,也就是总共96K个数据修改事务。
4)Innodb_undo_logs参数用来设置回滚段的个数。
mysql> show variables like '%undo%%'; +--------------------------+------------+ | Variable_name | Value | +--------------------------+------------+ | innodb_max_undo_log_size | 1073741824 | | innodb_undo_directory | ./ | | innodb_undo_log_truncate | OFF | | innodb_undo_logs | 128 | | innodb_undo_tablespaces | 0 | +--------------------------+------------+
5)Undo Log的原理很简单,为了满足事务的原子性,在操作任何数据之前,首先将数据备份到一个地方 (这个存储数据备份的地方称为Undo Log)。然后进行数据的修改。如果出现了错误或者用户执行了 ROLLBACK语句,系统可以利用Undo Log中的备份将数据恢复到事务开始之前的状态
2.6、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 | +-----------------------+-------+
2.7、temporary表空间
1)temporary临时表空间用来存放临时表,默认情况下是在数据文件夹下的ibtmp1数据文件,此数据文件被设置为每次自动增长12MB大小,当然也可以设置innodb_temp_data_file_path来指定临时表空间文件的存放位置。
2)临时表空间文件在正常的shutdown之后会自动清除,但在crash发生时不会清除,这就需要DBA手动去删除表空间文件或重启服务器。
mysql> show variables like '%innodb_temp%'; +----------------------------+-----------------------+ | Variable_name | Value | +----------------------------+-----------------------+ | innodb_temp_data_file_path | ibtmp1:12M:autoextend | +----------------------------+-----------------------+
3)如果发现临时表空间数据文件比较大,可以考虑重启MySQL来释放空间大小。
2.9、redo log重做日志
1)redo日志是存在于磁盘上的文件,包括ib_logfile0和ib_logfile1两个文件,常用于在crash恢复发生时将还没来得及写入到数据文件中但已经完成提交的事务在数据库初始化时重新执行一遍。
2)InnoDB对redo log buffer写入到redo log文件的方式提供了组提交(group commit)的方式,意味着针对一次写磁盘操作可以包含多个事务数据,用此方法提高性能。
3)为了IO效率,数据库修改的文件都在内存缓存中完成的;那么我们知道一旦断电,内存中的数据将消失,而数据库是如何保证数据的完整性?那就是数据持久化与事务日志
4)如果宕机了则:应用已经持久化好了的日志文件,读取日志文件中没有被持久化到数据文件里面的记录;将这些记录重新持久化到我们的数据文件中
5)innodb日志持久化相关参数
①innodb_flush_log_at_trx_commit
- 0:每秒写入并持久化一次(不安全,性能高,无论mysql或服务器宕机,都会丢数据最多1秒的数据)
- 1:每次commit都持久化(安全,性能低,IO负担重)
- 2:每次commit都写入内存的内存缓存,每秒再刷新到磁盘(安全,性能折中,mysql宕机数据不会丢失,服务器宕机数据会丢失最多1秒的数据)
innodb_flush_method主要有三个参数:
- O_DIRECT: 数据缓冲区写磁盘,不走OS buffer
- fdatasync: 日志和数据缓冲区写磁盘,都走OS buffer
- O_DSYNC: 日志缓冲区写磁盘,不走 OS buffer
一般来说innodb_flush_log_at_trx_commit和Innodb_flush_method是一起配合使用的
#使用建议: #最高安全模式 innodb_flush_log_at_trx_commit=1 #每次提交都写磁盘 Innodb_flush_method=O_DIRECT #数据缓冲区写磁盘,不走OS buffer #最高性能: innodb_flush_log_at_trx_commit=0 #每秒钟将log buffer中的数据写入日志文件并flush磁盘一次 Innodb_flush_method=fdatasync #日志和数据缓冲区写磁盘,都走OS buffer
②innodb_flush_log_at_timeout:参数决定最多丢失多少秒的数据,默认是1秒
三、InnoDB存储引擎配置
3.1、启动配置
InnoDB合理的规划方法是在创建数据库实例之前就定义好数据文件,日志文件和数据页大小等相关属性
3.2、指定配置文件位置
MySQL实例启动需要依赖my.cnf配置文件,而配置文件可以存在于多个操作系统目录下 my.cnf文件的默认查找路径,从上到下找到的文件先读,但优先级逐级提升
3.3、系统表空间数据文件配置
可以通过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并可自动扩展容量(当修改时,ibdata1需要根据实际来配置数值) [mysqld] innodb_data_file_path=ibdata1:12M:autoextend:max:500M #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_file_path=/path/to/myibdata/ibdata1:50M;/path/to/myibdata/ibdata2:50M:autoextend
[root@db01 ~]# /etc/init.d/mysql stop [root@db01 ~]# vim /etc/my.cnf [mysqld] innodb_data_file_path=ibdata1:50M;ibdata2:50M:autoextend [root@db01 ~]# /etc/init.d/mysql start Starting MySQL.. ERROR! The server quit without updating PID file (/data/mysql/db01.pid). [root@db01 ~]# vim /data/mysql/mysql.err 2019-03-13T12:17:06.492590Z 0 [ERROR] InnoDB: The innodb_system data file './ibdata1' is of a different size 4864 pages (rounded down to MB) than the 3200 pages specified in the .cnf file! #ibdata1大小更改有问题 [root@db01 ~]# vim /etc/my.cnf innodb_data_file_path=ibdata1:76M;ibdata2:50M:autoextend [root@db01 ~]# /etc/init.d/mysql restart ERROR! MySQL server PID file could not be found! Starting MySQL.. SUCCESS! [(none)]>show variables like 'innodb_data_file_path'; +-----------------------+------------------------------------+ | Variable_name | Value | +-----------------------+------------------------------------+ | innodb_data_file_path | ibdata1:76M;ibdata2:50M:autoextend | +-----------------------+------------------------------------+ 1 row in set (0.00 sec) #为何要更改为76M而不是50M? [root@db01 ~]# du -sh /data/mysql/* 76M /data/mysql/ibdata1
3.4、日志文件配置
默认情况下InnoDB会在数据文件夹下创建两个48M的日志文件,分别是ib_logfile0和ib_logfile1。 Innodb_log_group_home_dir参数用来定义redo日志的文件位置
mysql> show variables like '%innodb_log_file%'; +---------------------------+----------+ | Variable_name | Value | +---------------------------+----------+ | innodb_log_file_size | 50331648 | | innodb_log_files_in_group | 2 | +---------------------------+----------+ [mysqld] innodb_log_group_home_dir = /dr3/iblogs innodb_log_files_in_group参数用来定义日志文件的个数,默认和推荐值都是2 innodb_log_file_size参数定义了每个日志文件的大小,日志文件越大意味着buffer pool进行文件间切换的操作越少,从而减少IO,一般至少要保证在高峰期的1小时内的所有日志都能存放在一个日志文件里而不发生切换(可以查看连个文件的修改时间),当然文件大小也有最大限制,就是所有日志文件的总大小不能超过512G。
3.5、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_truncate | OFF | | innodb_undo_logs | 128 | | innodb_undo_tablespaces | 0 | +--------------------------+-------+
3.6、临时表空间配置
默认情况下,innodb会创建一个自增长的ibtmp1文件在数据文件夹下作为临时表空间数据文件。
#Innodb_temp_data_file_path参数可以指定文件路径,文件名和文件大小 mysql> show variables like '%innodb_temp%'; +----------------------------+-----------------------+ | Variable_name | Value | +----------------------------+-----------------------+ | innodb_temp_data_file_path | ibtmp1:12M:autoextend | +----------------------------+-----------------------+
3.7、数据页配置
Innodb_page_size参数用来指定所有innodb表空间的数据页大小。默认是16K大小,也可以设置为64K、32K、8K和4K。一般设置为存储磁盘的block size接近的大小.
mysql> show variables like '%page_size%'; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | innodb_page_size | 16384 | | large_page_size | 0 | +------------------+-------+
3.8、内存相关配置
1)Innodb_buffer_pool_size参数确定了缓存表数据和索引数据的内存区域大小,默认为128M,推荐设置为系统内存的50%~80%。
2)在服务器有大量内存的情况下,也可以设置多个缓存以提高系统并发度。Innodb_buffer_pool_instances参数就是用来做这个设置。
3) Innodb_log_buffer_size参数确定了redo log缓存的大小,默认值是16M,其大小取决于是否有某些大的事务会大量修改数据而导致在事务执行过程中就要写日志文件。
3.9、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)
3.10、InnoDB buffer pool设置
1)Buffer pool是内存中用来缓存数据和索引的存储区域,其是MySQL性能调优的重要一环。
2)理想情况下,设置的size越大,则缓存到内存的数据越多,InnoDB就越像是内存数据库。
3)Buffer pool的底层是一个列表,通过LRU算法进行数据页的换进换出操作。当空间原因导致新页的加入需要换出一页时,InnoDB取出最近最少使用的页并将这个新的数据页加入到列表的中央。从方向上看,列表的头部是最常使用的数据页,而在尾部则是最少使用的数据页。 Buffer pool中3/8的部分是保存最少使用的数据页,而中央部分其实是经常使用和最少使用的结合点。当在最少使用中保存的数据页被访问时,则数据页就会被移动到列表的头部变成最常使用的。
3.10.1、配置大小
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会被调整成大于设定值且最接近的一个值。
Innodb_buffer_pool_chunk_size可以自行设定,且增加和减少都要以M为单位,并只能在启动前修改,修改后的值*innodb_buffer_pool_instances不能大于buffer pool的大小,否则修改无效
#配置innodb buffer pool大小 mysql> select @@innodb_buffer_pool_size/1024/1024; +-------------------------------------+ | @@innodb_buffer_pool_size/1024/1024 | +-------------------------------------+ | 128.00000000 | #128M +-------------------------------------+ 增加和减少buffer pool的大小都是以大块的方式,块的大小由参数innodb_buffer_pool_chunk_size决定,默认为128M。 Innodb_buffer_pool_size的大小可以自行设定,但必须是innodb_buffer_pool_chunk_size*innodb_buffer_pool_instances的整数倍,如果不是,则buffer pool会被调整成大于设定值且最接近的一个值 mysql> set global innodb_buffer_pool_size=262144000; 250*1024*1024 250M mysql> select @@innodb_buffer_pool_size/1024/1024; +-------------------------------------+ | @@innodb_buffer_pool_size/1024/1024 | +-------------------------------------+ | 256.00000000 | #大于250且是128的整数倍 +-------------------------------------+ ------------------------------------------------------------------------------------ [mysqld] innodb_buffer_pool_chunk_size=134217728 bufferpool的大小可以动态修改,用set语句直接修改,当语句发起时,会一直等到当前所有的事务结束后才执行, 而一旦执行则执行过程中的其他事务如果要访问buffer pool就会等待语句执行完毕。 mysql> SET GLOBAL innodb_buffer_pool_size=402653184; 当执行online的调整大小时,可以通过error log或者innodb_buffer_pool_resize_status查看进度 SHOW STATUS WHERE Variable_name='InnoDB_buffer_pool_resize_status';
3.10.2、配置多个buffer pool实例
当buffer pool的大小是GB级别时,将一个buffer pool分割成几个独立的实例能降低多个线程同时读写缓存页的竞争性而提高并发性。通过innodb_buffer_pool_instances参数可以调整实例个数。如果有多个实例,则缓存的数据页会随机放置到任意的实例中,且每个实例都有独立的buffer pool所有的特性。 Innodb_buffer_pool_instances的默认值是1,最大可以调整成64。
3.10.3、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后但没有移动到经常被访问列表位置的时间窗口。
3.10.4、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来开启此方式。
3)通过执行show engine innodb status命令显示的三个参数判断read-ahead算法的有效性:
#参数配置 Innodb_buffer_pool_read_ahead Innodb_buffer_pool_read_ahead_evicted Innodb_buffer_pool_read_ahead_rnd mysql> show engine innodb status; ..... ---------------------- BUFFER POOL AND MEMORY ---------------------- Total large memory allocated 137428992 Dictionary memory allocated 297257 Buffer pool size 8191 Free buffers 3969 Database pages 4215 Old database pages 1535 Modified db pages 0 Pending reads 0 Pending writes: LRU 0, flush list 0, single page 0 Pages made young 3844, not young 265 0.00 youngs/s, 0.00 non-youngs/s Pages read 312, created 4562, written 8066 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: 4215, unzip_LRU len: 0 I/O sum[0]:cur[0], unzip sum[0]:cur[0] -------------- ......
3.10.5、InnoDB buffer pool flushing配置
Innodb会在后台将buffer pool中的脏页(已经修改但没有写到数据文件)flush掉。当buffer pool中的脏页所占百分比达到innodb_max_dirty_pages_pct_lwm会触发flush,当所占比例达到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
3.10.6、重置buffer pool状态
1)InnoDB可以通过配置innodb_buffer_pool_dump_at_shutdown参数来确保在mysql正常重启时部分经常使用的数据页能直接加载到buffer pool中,通过批量加载的方式,以节省重启mysql导致的warmup时间(原先在buffer pool中的数据页要从磁盘再次加载到内存中)。
2)Buffer pool的状态可以在任意时刻被保存,而重置状态也可以恢复任意保存的副本。
3)在数据库运行期间动态配置buffer pool数据页保留占比的方式是:
SET GLOBAL innodb_buffer_pool_dump_pct=40;
4)而在配置文件中的配置方法为:
[mysqld] innodb_buffer_pool_dump_pct=405)配置当服务器关闭时保存buffer pool的当前状态的方法是:
SET GLOBAL innodb_buffer_pool_dump_at_shutdown=ON;6)当服务器开启时重新加载buffer pool的方法是:
mysqld --innodb_buffer_pool_load_at_startup=ON7)默认情况下innodb_buffer_pool_dump_at_shutdown和innodb_buffer_pool_load_at_startup两个配置是开启状态
8)在关闭MySQL时,会把内存中的热数据保存在磁盘里ib_buffer_pool文件中,位于数据目录下。
9)数据库运行期间保存和重新加载buffer pool的方法
SET GLOBAL innodb_buffer_pool_dump_now=ON; SET GLOBAL innodb_buffer_pool_load_now=ON;
9)查看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 191006 17:06:45 | +--------------------------------+--------------------------------------------------+
3.10.7、监控buffer pool的状态情况
mysql> show engine innodb status; BUFFER POOL AND MEMORY ---------------------- Total large memory allocated 137428992 Dictionary memory allocated 297257 Buffer pool size 8191 Free buffers 3969 Database pages 4215 Old database pages 1535 Modified db pages 0 Pending reads 0 Pending writes: LRU 0, flush list 0, single page 0 Pages made young 3844, not young 265 0.00 youngs/s, 0.00 non-youngs/s Pages read 312, created 4562, written 8066 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: 4215, unzip_LRU len: 0 I/O sum[0]:cur[0], unzip sum[0]:cur[0]
3.11、InnoDB change buffer设置
1)change buffering是MySQL5.5加入的新特性,change buffering是insert buffer的加强,insert buffer只针对insert有效,change buffering对insert、delete、update(delete+insert)、purge都有效。
2)当修改一个索引块(secondary index非主键)时的数据时,索引块在buffter pool中不存在,修改信息就会被cache在change buffer中,当通过索引扫描把需要的索引块读取到buffer pool时,会和change buffer中修改信息合并,再择机写回disk。
3)目的还是为了减少随机IO带来性能损耗
4)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。
3.12、InnoDB线程并发度配置
InnoDB利用操作系统的线程技术达到多线程实现。
- Innodb_thread_concurrency参数限制同时执行的线程数。默认值是0代表没有限制。
- Innodb_thread_sleep_delay参数确定
3.13、InnoDB后台IO线程配置
通过配置innodb_read_io_threads和innodb_write_io_threads参数来指定后台读和写数据页的线程的个数,默认值是4,容许的取值范围是1-64。
mysql> show engine innodb status\G 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)
3.14、使用Linux异步IO
InnoDB在Linux平台使用异步IO子系统完成数据文件页的读写请求,可以通过innodb_user_native_aio参数控制,默认是开启状态,并且需要libaio系统库支持。
3.15、InnoDB主线程配置
InnoDB的主线程在后台承担了诸多的任务,绝大多数是和IO操作相关的,比如将buffer pool中的修改后的数据刷新的磁盘文件中。
Innodb_io_capacity参数设置了InnoDB的整体IO能力。该参数应该被设置为等同于操作系统每秒的IO操作数量。该参数可以设置为100及以上的任意数值,默认值是200。其中设置为100相当于7200RPM的磁盘性能。
3.16、InnoDB purge配置
1)InnoDB的purge操作是一类垃圾回收操作,是由一个或多个独立线程自动执行。
2)通过innodb_purge_threads参数设置purge线程的数量,如果DML操作比较复杂且涉及到多个表时,则可以考虑增加此值,最大可以设置为32。
3)事务被提交后,其所使用的undo log可能不再需要,因此需要PurgeThread来回收已经使用并分配的undo页.
3.17、InnoDB 优化器统计信息配置
1)Innodb表的优化器统计信息分为永久和非永久两种。 永久的优化器统计信息即使是服务器重启的情况下也会存在,其用来选出更优的执行计划以便提供更好的查询性能。
2)通过配置innodb_stats_auto_recalc参数来控制统计信息是否在表发生巨大变化(超过10%的行)之后是否自动更新,但由于自动更新统计信息本身是异步的,所以有时未必能马上更新,这是可以执行analyze table语句来同步更新统计信息。
3)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;
4)优化器永久统计资料数据在系统表mysql.innodb_table_stats和mysql.innodb_index_stats表中存储,这两个表中有个字段last_update可以用来判断统计信息最后更改时间。这两个表的数据也可以被手工更改。当手工更改完数据之后,要执行flush table 表名命令来重新load此表的统计资料。innodb_table_stats表中每个目标表一行记录,而innodb_index_stats表中每个索引会有多条记录
Innodb_table_stats表结构:
mysql> select * from mysql.innodb_table_stats where table_name='city'; +---------------+------------+---------------------+--------+----------------------+--------------------------+ | database_name | table_name | last_update | n_rows | clustered_index_size | sum_of_other_index_sizes | +---------------+------------+---------------------+--------+----------------------+--------------------------+ | world | city | 2019-10-08 13:52:39 | 4188 | 25 | 8 | +---------------+------------+---------------------+--------+----------------------+--------------------------+
Innodb_index_stats表结构:
mysql> select * from mysql.innodb_index_stats where table_name='city'; +---------------+------------+-------------+---------------------+--------------+------------+-------------+-----------------------------------+ | database_name | table_name | index_name | last_update | stat_name | stat_value | sample_size | stat_description | +---------------+------------+-------------+---------------------+--------------+------------+-------------+-----------------------------------+ | world | city | CountryCode | 2019-10-08 13:52:39 | n_diff_pfx01 | 232 | 7 | CountryCode | | world | city | CountryCode | 2019-10-08 13:52:39 | n_diff_pfx02 | 4079 | 7 | CountryCode,ID | | world | city | CountryCode | 2019-10-08 13:52:39 | n_leaf_pages | 7 | NULL | Number of leaf pages in the index | | world | city | CountryCode | 2019-10-08 13:52:39 | size | 8 | NULL | Number of pages in the index | | world | city | PRIMARY | 2019-10-08 13:52:39 | n_diff_pfx01 | 4188 | 20 | ID | | world | city | PRIMARY | 2019-10-08 13:52:39 | n_leaf_pages | 24 | NULL | Number of leaf pages in the index | | world | city | PRIMARY | 2019-10-08 13:52:39 | size | 25 | NULL | Number of pages in the index | +---------------+------------+-------------+---------------------+--------------+------------+-------------+-----------------------------------+ Stat_name=n_diff_pfxNN参数: 当是n_diff_pfx01时stat_value列表示索引第一列上的区别值有几个, 当是n_diff_pfx02时stat_value列表示索引第一、二列上的区别值有几个,以此类推。 而stat_description列显示了对应的逗号可开的索引列值。
5)默认情况下永久优化器统计信息的属性是开启的,innodb_stats_persistent=ON
6)非永久优化器统计信息会在每次服务器重启或者其他一些操作时被清理。
7)优化器统计信息会被存储在磁盘上,通过设置innodb_stats_persistent=ON参数(默认)。 MySQL的查询优化器会基于评估好的统计资料选择合适的索引参与到执行计划中,而类似analyze table的语句会从索引中随机选取数据页参与到每个索引的基数评估中。而参数innodb_stats_persistent_sample_pages决定了参与评估的数据页的数量,默认值是20。当语句执行的执行计划不是最优选择时,则考虑增加此参数,以便获得正确的统计资料。
8)当设置innodb_stats_persistent=OFF参数或者对单个表设置stats_persistent=0时,对应的统计资料就仅存在于内存中而非磁盘上,当服务器重启之后统计资料丢失。
9)当然此类统计资料也可以周期性的更新。 比如执行analyze table语句手动刷新统计资料,或者在innodb_stats_on_metadata选项打开之后执行show table status/show index或查询information_schema.tables/statistics表时非永久统计资料会自动更新,当InnoDB检测到1/16的表数据被修改时也会更新。
3.18、索引页之间合并阈值
1)通过配置merge_threshold来确保当索引页的数据由于删除操作或者修改操作低于阈值,InnoDB会将此索引页和邻近的索引页合并。默认值是50,取值范围是1到50。
2)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';
3)评估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 | +-----------------------------+----------------------------------------+
3.19、重置InnoDB系统表空间
1)最简单的增加系统表空间的办法就是在初始化阶段配置数据文件的自增长,通过配置最后一个文件的autoextend属性,当数据文件空间不足时默认自动增长64M大小。
2)也可以通过修改innodb_autoextend_increment参数修改自动增长的大小。
3)也可以通过增加另一个数据文件方法扩展表空间,步骤如下:
- 关闭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
3.20、减小系统表空间大小的方法
Mysqldump出所有的InnoDB表,包括mysql系统数据库下的几个表 mysql> select table_name from information_schema.tables where table_schema='mysql' and engine='InnoDB'; +----------------------+ | table_name | +----------------------+ | engine_cost | | gtid_executed | | innodb_index_stats | | innodb_table_stats | | server_cost | | slave_master_info | | slave_relay_log_info | | slave_worker_info | +----------------------+ 关闭MySQL 删除所有InnoDB的数据文件和日志文件,包括*.ibd和ib_log文件,还有在MySQL库文件夹下的*.ibd文件 删除所有.frm的InnoDB表文件 在配置文件里配置新的表空间文件 启动MySQL 导入备份出的dump文件
3.21、重置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
3.22、配置单表数据文件表空间
1)InnoDB的单表数据文件表空间代表每个InnoDB表的数据和索引数据都存放在单独的.ibd数据文件中,每个.ibd数据文件代表独立的表空间。此属性通过innodb_file_per_table配置。
2)此配置的主要优势: 当删除表或者truncate表的时候,意味着对磁盘空间可以回收。而共享表空间时删除一个表时空间不会释放而只是文件里有空闲空间
3)Truncate table命令要比共享表空间快 通过定义create table …data directory=绝对路径,可以将特定的表放在特定的磁盘或者存储空间
4)可以将单独的表物理拷贝到另外的MySQL实例中
5)此配置的劣势: 每个表都有未使用的空间,意味着磁盘空间有些浪费
6)启动单独表空间的方式
[mysqld] innodb_file_per_table=1 #当设置innodb_file_per_table=0时,所有创建的新表都会放置到共享表空间里,除非在create table命令里显示的使用tablespace选项。
7)将已经存在于共享表空间的表修改为独立表空间的方法
SET GLOBAL innodb_file_per_table=1; ALTER TABLE table_name ENGINE=InnoDB;
8)通过命令create table … data directory=绝对路径可以将单表数据文件创建在另外的目录里。在指定的绝对路径下,会创建数据库名相同的文件夹,里面含有此表的.ibd文件,同时在MySQL的默认数据文件下的数据库名文件夹下会创建table_name.isl文件包含了此表的路径,相当于link文件。
3.23、传输表空间
3.24、设置Undo log独立表空间
默认情况下undo log是存储在系统表空间里,我们也可以将其存放在一个或多个独立表空间下
- Innodb_undo_tablespaces参数定义了有多少个undo表空间,此参数只能在建立MySQL实例时被配
- innodb_undo_directory参数定义了undo表空间的存放路径
- innodb_undo_logs参数定义了回滚段的数量
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大小;然后回滚段重新变成激活状态以接收新的事务
3.25、InnoDB普通表空间
1)通过create tablespace命令可以创建一个共享的InnoDB表空间,和系统表空间一样,多个表可以在此表空间上存储数据,此表空间的数据文件可以放置在任意的文件夹下。
CREATE TABLESPACE tablespace_name ADD DATAFILE 'file_name' [FILE_BLOCK_SIZE = value] [ENGINE [=] engine_name] ##创建在MySQL数据目录下 mysql> CREATE TABLESPACE `ts1` ADD DATAFILE 'ts1.ibd' Engine=InnoDB; ##创建在自定义目录下 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;
2)通过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语句的执行都会导致此表会重建,即使表空间的属性和之前是一样的。
3)当删除一个普通表空间时,首先需要保证此表空间上的所有表都被删除,否则会报错。删除表空间是用drop tablespace语句来执行。Drop database的动作会删除所有的表,但创建的tablespace不会被自动删除,必须通过drop tablespace显示执行。
4)普通表空间不支持临时表,而且也不支持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)
3.26、创建InnoDB表
1)通过create table语句创建InnoDB表,因为默认存储引擎就是InnoDB,所以不需要在创建表的语句最后指定engine=innodb。
2)InnoDB的表数据和索引数据默认是存储在系统表空间中,但可以通过开启innodb_file_per_table选项将表数据和索引数据存放在独立表空间中。当表创建完之后,会在表所在的数据库文件夹里创建.frm文件用来存储表的结构,系统表空间对应的.ibdata文件存储数据文件,而当开启独立表空间时,则会在表所在的数据库文件夹里创建.ibd用来存储表数据和索引数据。
四、存储引擎补充
4.1、查看库中表的存储引擎
select table_name,engine from information_schema.tables where table_schema='world';
4.2、修改表的存储引擎-判断碎片
alter table t1 engine myisam; #修改引擎为myisam alter table t1 engine innodb; #修改引擎为innodb 注意:此命令既可以修改存储引擎类型,也可以自动分析整理innodb表的碎片,避开业务繁忙期. alter table t1 engine innodb; è表本身就是innodb 如何判断是否有大量的碎片? 表真实的空间占用和磁盘的ibd文件比较 1.真正占用空间大小 [zabbix]>desc information_schema.tables; #很重要的表 +-----------------+---------------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------------+---------------------+------+-----+---------+-------+ | TABLE_CATALOG | varchar(512) | NO | | | | | TABLE_SCHEMA | varchar(64) | NO | | | | | TABLE_NAME | varchar(64) | NO | | | | | TABLE_TYPE | varchar(64) | NO | | | | | ENGINE | varchar(64) | YES | | NULL | | | VERSION | bigint(21) unsigned | YES | | NULL | | | ROW_FORMAT | varchar(10) | YES | | NULL | | | TABLE_ROWS | bigint(21) unsigned | YES | | NULL | | | AVG_ROW_LENGTH | bigint(21) unsigned | YES | | NULL | | | DATA_LENGTH | bigint(21) unsigned | YES | | NULL | | | MAX_DATA_LENGTH | bigint(21) unsigned | YES | | NULL | | | INDEX_LENGTH | bigint(21) unsigned | YES | | NULL | | | DATA_FREE | bigint(21) unsigned | YES | | NULL | | | AUTO_INCREMENT | bigint(21) unsigned | YES | | NULL | | | CREATE_TIME | datetime | YES | | NULL | | | UPDATE_TIME | datetime | YES | | NULL | | | CHECK_TIME | datetime | YES | | NULL | | | TABLE_COLLATION | varchar(32) | YES | | NULL | | | CHECKSUM | bigint(21) unsigned | YES | | NULL | | | CREATE_OPTIONS | varchar(255) | YES | | NULL | | | TABLE_COMMENT | varchar(2048) | NO | | | | +-----------------+---------------------+------+-----+---------+-------+ 21 rows in set (0.00 sec) [zabbix]>SELECT table_schema,SUM(AVG_ROW_LENGTH * TABLE_ROWS+ INDEX_LENGTH)/1024 AS Total_KB FROM information_schema.tables GROUP BY table_schema; #按库分组 +--------------------+------------+ | table_schema | Total_KB | +--------------------+------------+ | db | 15.9961 | | information_schema | NULL | | mmm | 0.0000 | | mysql | 880.1045 | | oldboy | 63375.1416 | | oldguo | 0.0000 | | performance_schema | 0.0000 | | school | 128.9863 | | sys | 15.9961 | | test | 63.8682 | | testdb | 0.0000 | | world | 779.7744 | | zabbix | 0.0000 | +--------------------+------------+ 13 rows in set, 48 warnings (0.73 sec) [zabbix]>SELECT table_name,SUM(AVG_ROW_LENGTH * TABLE_ROWS+ INDEX_LENGTH)/1024 AS Total_KB FROM information_schema.tables where table_schema='oldboy' group by table_name; #按库中的表分组 +------------+------------+ | table_name | Total_KB | +------------+------------+ | t1 | 63375.1416 | | t2 | 0.0000 | +------------+------------+ 2 rows in set (0.00 sec) 2. 磁盘方面查看IBD文件占用的磁盘空间 du -sh ll -h --------------------------------------------------------------------------------------------------- 需求:将zabbix库中的所有表,innodb替换为tokudb select concat("alter table zabbix.",table_name," engine tokudb;") from information_schema.tables where table_schema='zabbix' into outfile '/tmp/tokudb.sql'; 实验模拟: [oldboy]>create database zabbix; [oldboy]>use zabbix [zabbix]>create table t1(id int) engine myisam; [zabbix]>create table t2(id int) engine myisam; [zabbix]>create table t3(id int) engine myisam; [zabbix]>select concat("alter table zabbix.",table_name," engine innodb;") from information_schema.tables where table_schema='zabbix'; +------------------------------------------------------------+ | concat("alter table zabbix.",table_name," engine innodb;") | +------------------------------------------------------------+ | alter table zabbix.t1 engine innodb; | | alter table zabbix.t2 engine innodb; | | alter table zabbix.t3 engine innodb; | +------------------------------------------------------------+ 3 rows in set (0.00 sec) [zabbix]>select concat("alter table zabbix.",table_name," engine innodb;") from information_schema.tables where table_schema='zabbix' into outfile '/tmp/innodb.sql'; [zabbix]>system cat /tmp/innodb.sql alter table zabbix.t1 engine innodb; alter table zabbix.t2 engine innodb; alter table zabbix.t3 engine innodb; [zabbix]>source /tmp/innodb.sql; [zabbix]>select table_name,engine from information_schema.tables where table_schema='zabbix'; +------------+--------+ | table_name | engine | +------------+--------+ | t1 | InnoDB | | t2 | InnoDB | | t3 | InnoDB | +------------+--------+ 3 rows in set (0.00 sec)
4.3、案例: 表空间恢复
背景及解决方式:
真实的案例---开发用户 jira(bug追踪) 、 confluence(内部知识库) ------>LNMT环境,两个库 ------------------- 联想服务器(IBM) 磁盘500G 没有raid centos 6.8 mysql 5.6.33 innodb引擎 独立表空间 备份没有,日志也没开 ------------------ 编译→制作rpm /usr/bin/mysql /var/lib/mysql confulence jira 所有软件和数据都在"/" ------------ 断电了,启动完成后“/” 只读 fsck 重启 启动mysql启动不了。 结果:confulence库在 , jira库不见了 ------------ 求助: 这种情况怎么恢复? 我问: 有备份没 求助: 连二进制日志都没有,没有备份,没有主从 我说: 没招了,jira需要硬盘恢复了。 求助: 1、jira问题拉倒中关村了 2、能不能暂时把confulence库先打开用着 将生产库confulence,拷贝到1:1虚拟机上/var/lib/mysql,直接访问时访问不了的 问:有没有工具能直接读取ibd 我说:我查查,最后发现没有 CREATE TABLE `t1` ( `stuid` int(11) NOT NULL, `stuname` varchar(20) NOT NULL, `stusex` char(1) NOT NULL, `cardid` varchar(20) NOT NULL, `birthday` datetime DEFAULT NULL, `entertime` datetime DEFAULT NULL, `address` varchar(100) DEFAULT NULL, PRIMARY KEY (`stuid`), KEY `idx_name` (`stuname`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ; 我想出一个办法来: create table xxx alter table confulence.t1 discard tablespace; alter table confulence.t1 import tablespace; 虚拟机测试可行。 CREATE TABLE `city_new` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `Name` char(35) NOT NULL DEFAULT '', `CountryCode` char(3) NOT NULL DEFAULT '', `District` char(20) NOT NULL DEFAULT '', `Population` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`ID`), KEY `CountryCode` (`CountryCode`), KEY `idx_popu` (`Population`) ) ENGINE=InnoDB AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1; 面临的问题,confulence库中一共有107张表。 1、创建107和和原来一模一样的表。 他有2016年的历史库,我让他去他同时电脑上 mysqldump备份confulence库 mysqldump -uroot -ppassw0rd -B confulence --no-data >test.sql 拿到你的测试库,进行恢复 到这步为止,表结构有了。 2、表空间删除。 select concat('alter table ',table_schema,'.'table_name,' discard tablespace;') from information_schema.tables where table_schema='confluence' into outfile '/tmp/discad.sql'; source /tmp/discard.sql 执行过程中发现,有20-30个表无法成功。主外键关系 很绝望,一个表一个表分析表结构,很痛苦。 set foreign_key_checks=0 跳过外键检查。 把有问题的表表空间也删掉了。 3、拷贝生产中confulence库下的所有表的ibd文件拷贝到准备好的环境中 select concat('alter table ',table_schema,'.'table_name,' import tablespace;') from information_schema.tables where table_schema='confluence' into outfile '/tmp/discad.sql'; 4、验证数据 表都可以访问了,数据挽回到了出现问题时刻的状态(2-8)
实验模拟:==>相当于数据表的迁移
此实验需要获取源数据库的结构(创建语句)
1.通过历史备份获取
2.和开发一起来完成
1.获取原表的建表语句 ##关键 [world]>show create table city; CREATE TABLE `city` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `Name` char(35) NOT NULL DEFAULT '', `CountryCode` char(3) NOT NULL DEFAULT '', `District` char(20) NOT NULL DEFAULT '', `Population` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`ID`), KEY `CountryCode` (`CountryCode`) ) ENGINE=InnoDB AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1 2.搭建一个1:1的测试数据库(本实验采用多实例) [root@db01 ~]# systemctl start mysqld3307.service 或者采用如下启动: [root@db01 ~]# /app/mysql/bin/mysqld --user=mysql --defaults-file=/data/3307/my.cnf [root@db01 ~]# mysqls -S /data/3307/mysql.sock 3.在测试库中,恢复表结构 [(none)]>create database world; [(none)]>use world [world]>CREATE TABLE `city` ( -> `ID` int(11) NOT NULL AUTO_INCREMENT, -> `Name` char(35) NOT NULL DEFAULT '', -> `CountryCode` char(3) NOT NULL DEFAULT '', -> `District` char(20) NOT NULL DEFAULT '', -> `Population` int(11) NOT NULL DEFAULT '0', -> PRIMARY KEY (`ID`), -> KEY `CountryCode` (`CountryCode`) -> ) ENGINE=InnoDB AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1 -> ; Query OK, 0 rows affected (0.30 sec) 4.删除测试库的ibd文件 [world]>alter table city discard tablespace; 5.使用源库的ibd导入 [root@db01 ~]# cp /data/mysql/world/city.ibd /data/3307/data/world/ [world]>alter table city import tablespace; ERROR 1812 (HY000): Tablespace is missing for table `world`.`city`. [root@db01 world]# pwd /data/3307/data/world [root@db01 world]# chown -R mysql.mysql * #一定要重新授权 [world]>alter table city import tablespace; #再次导入便不会在报错了 6.查看恢复的数据 [world]>select * from city; #恢复成功
4.4、事务ACID实现原理
4.4.1、redo log工作图解
#redo log工作流程: 1)当修改数据的时候,如:update table t1 set num=2 where num=1; 2) 首先将数据从磁盘加载到内存中 3)内存中有一个redo buffer page 4)数据是在内存中将1修改为2 5)redo buffer page中记录的是将1改为2的过程 6)当执行commit时,把redo buffer page中的修改过程写入到磁盘中的redo log #如果此时服务器断电: 1)启动mysql过程中,读取redo log(此时mysql启动很慢) 2)首先将数据页中的原数据1加载到内存中 3)将redo log中的修改过程加载到内存中 4)在内存中将数据修改(1改成2) 5)写入磁盘 ------------------------------------------------------------------------ 重做日志,是事务日志的一种,实现的是持久化的作用 redo的日志文件:iblogfile0 iblogfile1 redo的buffer: 数据页的变化信息+数据页当时的LSN号 redo的刷新策略: commit; 刷新当前事务的redo buffer到磁盘 还会顺便将一部分redo buffer中没有提交的事务日志也刷新到磁盘 LSN: 日志序列号 存放位置: redo 日志文件(重做日志,前滚日志):ib_logfile0 ~ 1 数据文件中: t1.ibd 日志缓冲区:log buffer 数据缓冲区:data buffer MySQL : 在启动时,必须保证redo日志文件和数据文件LSN必须一致, 如果不一致就会触发CSR,最终保证一致 redo 日志文件是轮询使用的 -------------------------------------------------------------- 相关概念: redo undo CSR:自动故障恢复过程 LSN :日志序列号 TXID:事务ID CKPT(Checkpoint): https://www.cnblogs.com/chenpingzhao/p/5107480.html
4.4.2、undo log图解
4.4.3、redo log与undo log协同工作
情况一:手动commit之后断电
情况二:数据已经写到redo log,但是没有commit è要检查commit标签