Innodb的主要特性
- DML操作遵循ACID模型,事务支持commit,rollback和crash
recovery,从而保护用户数据 - 基于行锁以及类似oracle的一致性读,提升了多用户的并发和性能
- 基于主键对查询进行优化
- 为了维护数据的完整性,InnoDB也支持外键。如果使用了外键,insert,update以及delete操作都会得到检查,以确保不会导致多表之间的数据不一致。
- 可以将InnoDB的表和Mysql其它存储引擎的表混合使用,甚至在同一个sql语句当中。比如:可以使用join操作将InnoDB表和MEMORY中的表进行合并。
- InnoDB被设计成,在处理存在大量数据列的时候,可以更好地利用CPU,并使性能最高
- 最大存储大小:64T
- MVCC:支持(MVCC多版本并发控制介绍:
http://www.cnblogs.com/gaojian/p/3295951.html
) - 事务:支持
- 锁粒度:行
- 地理空间数据类型:支持
- 地理空间索引:支持
- B-tree索引:支持
- T-tree索引:不支持
- Hash索引:不支持
- Full Text Search索引:支持
- 聚合索引:支持
- 数据缓存:支持
- 索引缓存:支持
- 数据压缩:支持
- 数据加密:支持
- 集群数据库:不支持
- 复制数据库:支持
- 外键:支持
- 备份/实时恢复:支持
- 查询缓存:支持
- 数据字段的更新统计:支持
- InnoDB存储引擎维护了它自己的buffer
pool,用来在内存中缓存数据和索引。默认innodb_file_per_table是打开的,那么每个新的InnoDB表和索引都存储在独立的文件中
默认的Mysql存储引擎
- InnoDB支持crash
recovery,以便在数据库出现故障的时候,可以直接进行重启即可,数据不会丢失。在恢复的时候,可以提交已经commit的请求,回滚任何还未commit的请求。 - InnoDB的buffer
pool缓存了表和索引数据,经常使用的数据直接在内存中进行处理。这个缓存用于多个信息类型,并加快处理速度。 - 如果我们把相关数据拆分在多个表中,可以使用外键来保证参考完整性。
使用Innodb表的好处
- Crash Recovery:从灾难中恢复功能。
- InnoDB的buffer
pool 缓存了访问过的表和索引数据。经常使用的数据直接从内存中进行处理。这个缓存用于许多类型的信息,以便加快处理速度。 - 外键的支持:将关联数据拆分到多张表中的时候,可以使用外键来确保参照完整性。Update或Delete数据,在其它表中的关联数据也会自动被更新或删除。如果尝试在secondary表中插入在primary表中没有相应数据的记录,这些记录自动会被剔除。
- 如果数据在内存或磁盘中被破坏了,一个checksum的机制可以检测到这一点并在你使用数据之前进行告警。
- 如果我们为每张表设计了合适的primary key,那么哪些关联了这些primary
key的字段会自动被优化。在WHRER、ORDER
BY,GROUP BY,JOIN子句中使用主键,能大大加快操作速度。 - 对于insert,update和delete操作,Innodb自动使用chang
buffering机制进行优化。InnoDB不仅允许并发读写来访问同一张表,它还缓存磁盘I/O改变的数据。 - 性能的优化不仅仅局限于运行时间长的大表查询,对于从一张表中相同的一些记录经常被访问,一个称为Adaptive
Hash Index机制会被使用,从而使得这些查询更快,因为它们成为了hash表了。 - 可以对表和相关联的索引进行压缩
- 可以创建和删除索引,而不会对性能和可用性带来很大的影响。
- 清空file_per_table表非常快,而且能够释放磁盘空间,以便操作系统进行复用,而不只是释放system
tablespace的空间,导致只有InnoDB能够复用它。 - InnoDB对于BLOB和long
text字段的布局更加高效,因为它采用了DYNAMIC行格式。 - 可以通过查看
INFORMATION_SCHEMA中的表来查看InnoDB内部的工作情况。 - 可以通过查询
Performance Schema表来监控InnoDB的性能细节。 - InnoDB可以使用FULLTEXT的索引类型来执行全文(full-text)搜索
- InnoDB对于只读或大部分为读的任务具备更好的性能。在autocommit模式下,自动对InnoDB查询进行优化;而且我们可以显示设置事务为只读的,START
TRANSACTION READ ONLY;以便提升性能。
Innodb表的最佳实践
- 使用最经常查询的字段作为表的主键,如果没有显示的主键,应该设置一个auto-increment
字段。 - 如果需要从多张表中获取数据,多张表之间使用某个ID进行关联,这个时候需要使用join。为了提升join性能,可以在join列上面建立外键,并且这些字段的类型在这些表中都是一样的。增加外键,可以确保参照字段都被索引了,这样可以提升性能。外键可以对delete或update传递所有受影响的表上,并且在父表没有对应ID的情况下,可以阻止在子表插入数据。
- 关闭autocommit。一秒钟commit几百次会严重影响性能。
- 把相关的DML操作打包到一个事务当中(用START
TRANSACTION和
COMMIT包含他们)
。你不希望commit太频繁,但是你也不希望执行大量的INSERT,UPDATE或DELETE语句,经过了几个小时都不发生commit; - 不使用LOCK TABLES语句;InnoDB可以处理对同一张表同时进行读和写的多个会话,而不会影响稳定性和性能。为了获取多行记录的排它写,可以使用SELECT
... FOR UPDATE语句,它只会锁住表中需要更新的记录。 - 开启innodb_file_per_table选项,这样不同表的数据和索引,存放在不同的文件里面。
- 根据表中的数据和访问方式,评估下对表数据进行压缩是否有好处(使用
CREATE TABLE
语句,指定ROW_FORMAT=COMPRESSED)。 - 使用--sql_mode=NO_ENGINE_SUBSTITUTION选项来启动server,从而防止在
CREATE TABLE中 ENGINE=子句指定了其它的存储引擎。 - SHOW ENGINES命令,可以查看InnoDB是否为默认的存储引擎;当然也可以查询表INFORMATION_SCHEMA
ENGINES。
InnoDB概念和架构
Mysql和ACID模型
- MySQL中的InnoDB存储引擎实现了ACID模型。
- A:atomicity,一般和InnoDB的事务相关。和MySQL相关的特性包括:1)Autocommit设置
2)COMIT语句
3)ROLLBACK语句
4)来自于INFORMATION_SCHEMA的操作性数据 - C:consistency,一般和InnoDB保护数据不受崩溃破坏的内部处理相关。和MySQL相关的特性包括:1)InnoDB的双写缓存(doubewrite
buffer) 2)InnoDB的崩溃恢复(Crash
recovery)。 - I:Isolation,也一般和InnoDB的事务相关,特别是用在每个事务上的隔离级别。和MySQL相关的特性包括:1)Autocommit设置
2)SET ISOLATION LEVEL
语句 3)InnoDB
locking的底层细节。在性能调优时,我们可以通过INFORMATION_SCHEMA中的表看到很多细节。 - D:Durability,一般和硬件配置交互的MySQL软件特性相关。由于CPU,网络,存储设备的性能各异,这个部分要提供具体的指导建议是最困难的。和MySQL相关的特性包括:1)InnoDB的双写缓存,使用
innodb_doublewrite配置选项来关闭和打开。
2) innodb_flush_log_at_trx_commit配置选项
3) sync_binlog选项
4)innodb_file_per_table选项
5)存储设备(磁盘,磁阵,SSD)的写缓存
6)带蓄电池后备电源的存储缓存 7)运行MySQL的操作系统,特别是对fsync()函数的支持
8)UPS的使用
9)备份的策略,比如:备份的频率、类型以及备份的保留期
InnoDB的MVCC
- InnoDB是一个多版本存储引擎:它对更新行的历史记录进行保留,以支持事务特性,比如:并发访问和回滚。这些历史记录保留在rollback
segment当中,InnoDB使用这些记录来执行回滚操作,也使用它们为一致性读提供数据的早期版本。 - 在内部,InnoDB为每一行记录增加了3个字段。
- 一个6字节的DB_TRX_ID字段,记录最近对本行进行insert或update的事务标识。对于delete在内部被作为一种特殊的update操作来对待,delete时,在行记录中会设置一个的特殊的bit位来表示行被删除。
- 一个7字节的
DB_ROLL_PTR字段,也叫做roll
pointer。
它指向rollback segment中的一条undo日志记录。如果行被更新,undo日志记录
需要在行被更新之前,重新生成内容。 - 一个6字节的DB_ROW_ID字段,当有行插入的时候,它会自动地单调递增。如果InnoDB自动产生了聚合索引,索引会包含ROW
ID。否则,DB_ROW_ID不会出现在任何索引上面。
- 在rollback segment中的undo日志被分为insert和update两部分。insert部分只有在rollback时有用,可以在事务提交后进行删除。update部分除了用在rollback上,还用于一致性读上面;它们只有在没有事务需要使用它来构造一致性读所需要的记录早期版本的时候,才会被删除。
- 我们要经常commit事务(即使事务只发起了一致性)。否则,InnoDB无法释放Undo
Log里面的数据,rollback日志可能会变得很大。 - 在rollback segment中Undo
log记录一般比insert或update的行记录要小。我们可以使用这个信息来估算我们需要多大的rollback
segment。 - 在InnoDB的多版本方案中,当你使用delete删除一行记录,MySQL不会立即进行物理删除。InnoDB只有在对应行的Undo
日志失效的时候才会执行真正的物理删除操作。这个操作称之为purge(净化)。这个过程非常快,通常在执行delete语句之后就执行完了。如果某张表以同样的速率进行着小批量的插入和删除操作,purge线程有可能跟不上记录的删除,表最终会因为很多dead记录,变得越来越大,导致很多操作都和磁盘相关,变得越来越慢。在这种情况下,我们可以通过调节innodb_max_purge_lag系统变量来进行调优。这个参数表示有多少
delete或update操作被标记为deleted的行没有被purge,如果超过innodb_max_purge_lag这个值,则delete、insert或update操作会被挂起。
多版本和二级索引
- InnoDB的多版本控制,对待二级索引不同于聚合索引。【InnoDB按照主键进行聚合,如果没有定义主键,InnoDB会试着使用唯一的非空索引来代替。如果没有这种索引,InnoDB就会定义隐藏的主键然后在上面进行聚合。】聚合索引的记录是就地更新的,并且其中隐藏的指向undo
log的系统列可以用来构造记录的早期版本。而二级索引既不包括隐藏的系统列,也不会就地更新。 - 当一个二级索引列被更新,旧的二级索引记录被标记为
delete,新的记录被插入,标记为delete的记录最终会被清除(purge)。当更新的事务删除了二级索引记录或更新了二级索引页面,InnoDB将会通过聚合索引来查询记录。在聚合索引中,记录的
DB_TRX_ID 被检查,如果发现当前事务初始化之后,记录被更新了,那么它将通过undo log得到正确的记录版本。 - 当更新的事务删除了二级索引记录或更新了二级索引页面,那么 covering
index技术不会使用(
http://blog.sina.com.cn/s/blog_6757ea5801010qjv.html
)。InnoDB将会通过聚合索引来查询记录,而不是直接从covering
index结构中直接返回。 - 如果开启了index condition pushdown (ICP)优化,(
http://mdba.cn/?p=315
)WHERE条件部分能够只通过索引中的字段进行过滤,MySQL
Server仍会将这部分WHERE条件下推到存储引擎,由它使用索引来过滤数据。如果没有记录被找到,就可以避免查询聚合索引。但是如果查询到了记录(甚至查询到的是标记为delete的记录),InnoDB也会通过聚合索引来查询记录。
InnoDB Redo
日志
- redo日志是一个基于磁盘的数据结构,主要用于从系统崩溃重启时,从不完整的事务中恢复数据。一般情况下,请求通过sql或底层API来修改InnoDB表数据,redo日志对这些请求进行编码。由于异常关闭导致没有完成数据文件更新的操作会在启动的时候(接收外部连接之前)自动重新执行。
- 默认的,redo日志在磁盘上是两个文件:ib_logfile0和ib_logfile1。MySQL
循环写入redo日志文件,日志文件中的数据是影响记录的编码。日志文件中的记录使用LSN来标识。(
http://tech.uc.cn/?p=716
) - 重做日志的磁盘部署使用下面的选项来配置:
- innodb_log_file_size:定义每一个重做日志文件的大小。默认的重做日志文件大小为48M,所有日志文件的总大小不能超过512G
- innodb_log_files_in_group:日志文件的个数,默认为2个。
- innodb_log_group_home_dir:日志文件的存放目录,默认为datadir。
- 像其它的ACID数据库引擎一样,InnoDB会在事务提交之前,将redo日志属性到磁盘上。
InnoDB使用组提交(group
commit)功能将多个这样的flush请求组合在一起进行提交,从而避免每commit一次就flush一次。
InnoDB
Undo
日志
- Undo
日志(或回滚段)是一块存储区域,它持有被当前活跃事务修改的数据拷贝。如果另外一个事务需要看到原始数据(作为一致性读的一部分),未修改的数据从这块存储区域读出来。默认的,这块存储区域是系统表空间的一部分。然而,从Mysql的5.6.3之后,Undo
日志可以使用独立的Undo
表空间。 - InnoDB支持128个Undo
日志文件。对于Mysql
5.7.2,128个文件中的32个预留给临时表事务(non-redo
undo logs)。对于更新临时表的事务,会分配两个undo logs,一个是redo-enabled
undo log,一个是non-redo
undo logs。对于只读事务,只分配non-redo
undo logs,因为对于只读事务,只允许修改临时表。 - 剩下的96个undo
logs文件,每个可以支持1023个并发数据修改的事务,也就是说整个undo
logs可以支持近似96K的并发修改的事务。这里的96k是假定事务不会修改临时表。如果所有事务都会修改临时表,那么总的限制近似为32K。
InnoDB
临时表的Undo日志
- Mysql
5.7.2
引入了一个新的Undo
日志类型,用于普通的或压缩的临时表及其相关对象上面。这个Undo
日志不是一个redo log,因为临时表在异常退出做恢复的时候,Mysql不会对其做恢复操作,因此临时表不需要redo
log。但是临时表的undo log可以在Mysql在运行的时候,对临时表进行回滚。这个特殊的non-redo
undo logs可以避免对redo log进行I/O操作,从而提升性能。临时表的undo
log在临时表空间里面。默认的临时表空间文件为ibtmp1,默认在data
dir下面,会在重启的时候进行重建。用户可以通过修改参数innodb_temp_data_file_path来设置临时表空间文件的存放位置。 - 有了这个改变之后,32个回滚段被预留给临时表的Undo
logs,用于修改临时表及其相关对象的事务上面。这样哪些修改数据而产生回滚记录的事务,可以使用的回滚段最大数目,从128降到96,从而也减少了最大可以处理的并发数据修改事务的数目,从128k降到96k。
InnoDB
表和索引结构
- Mysql使用.frm文件来存储数据字典信息。不像其它Mysql存储引擎,InnoDB在它自己内部表空间的数据字典中存放表的相关信息。当Myql
drop一张表的时候,除了删除.frm文件之外,还会从InnoDB的数据字典中删除相关的信息。 - 聚合索引和二级索引:每一个InnoDB表都有一个特殊的索引,叫做聚合索引,每行的数据就存放在里面。一般情况下,聚合索引和主键是一样的。为了从查询,插入和其它数据库操作上获得最佳性能,我们必须要理解Mysql是如何使用聚合索引的,从而对每张表的一般查询和DML操作进行优化:
- 如果我们在表上面定义了主键,Mysql使用它作为聚合索引。因此要为我们创建的每张表定义一个主键。如果没有逻辑上唯一并且非空的列(或多个列),那么我们要增加要给
auto-increment列(它的值是自动生产的)。 - 如果我们没有定义主键,那么Mysql会找到表中第一个唯一非空索引作为聚合索引
- 如果上面两个条件都不满足,Mysql会在内部自动生成一个隐藏的聚合索引,索引包含一个列,其中存放row
ID的值。表中插入的行将会按照这个隐藏列进行排序。这个ID占用6个字节,它会随着新插入的行自动进行增长,因此按照这个ID排序的行是严格遵循插入顺序的。
- 如果我们在表上面定义了主键,Mysql使用它作为聚合索引。因此要为我们创建的每张表定义一个主键。如果没有逻辑上唯一并且非空的列(或多个列),那么我们要增加要给
- 聚合索引是如何提升查询速度的:通过聚合索引来访问记录是非常快的,因为索引直接可以找到数据存放的页。如果一张表很大,聚合索引通常可以节省一次磁盘I/O操作(对于那些和数据存放在不同页上的索引,一般需要两次磁盘I/O操作)
- 聚合索引和二级索引的关系:聚合索引之外的其它索引都是二级索引。在InnoDB中,二级索引的每一条记录既包含了行的主键列,也包含了组成二级索引的列。InnoDB使用二级索引中的主键列来查询聚合索引中的数据。如果主键很大,那么二级索引也会增大,因此要控制主键的大小。
InnoDB FULLTEXT
索引
- FULLTEXT索引用于在CHAR、VARCHAR或TEXT列上面,使用它可以忽略那些包含stopwords的words,以便提升查询和DML操作(包括这些字段的时候)的性能。
- FULLTEXT索引可以定义为CREATE
TABLE 语句的一部分,或者建立表之后,通过ALTER
TABLE
或CREATE INDEX来增加。Full-text检索使用
MATCH() ... AGAINST语法来进行。 - FULLTEXT索引设计:它采用反转的索引设计,反转索引存放一些列的单词,对于每个单词,又会有一个该单词出现过的文档列表。为了支持近似查询,每个单词的位置信息也存储了(一个字节的offset)。
- 对于每个FULLTEXT索引,一些列的索引表被创建,比如:
CREATE TABLEopening_lines (
id INTUNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
opening_lineTEXT(500),
authorVARCHAR(200),
titleVARCHAR(200),
FULLTEXT idx(opening_line)
)ENGINE=InnoDB;
mysql>SELECT table_id, name, space from INFORMATION_SCHEMA.INNODB_SYS_TABLES
WHERE nameLIKE ‘test/%‘;
+----------+----------------------------------------------------+-------+
| table_id |name | space |
+----------+----------------------------------------------------+-------+
| 333 |test/FTS_0000000000000147_00000000000001c9_INDEX_1 | 289 |
| 334 |test/FTS_0000000000000147_00000000000001c9_INDEX_2 | 290 |
| 335 |test/FTS_0000000000000147_00000000000001c9_INDEX_3 | 291 |
| 336 |test/FTS_0000000000000147_00000000000001c9_INDEX_4 | 292 |
| 337 |test/FTS_0000000000000147_00000000000001c9_INDEX_5 | 293 |
| 338 |test/FTS_0000000000000147_00000000000001c9_INDEX_6 | 294 |
| 330 |test/FTS_0000000000000147_BEING_DELETED | 286 |
| 331 |test/FTS_0000000000000147_BEING_DELETED_CACHE | 287 |
| 332 |test/FTS_0000000000000147_CONFIG | 288 |
| 328 |test/FTS_0000000000000147_DELETED | 284 |
| 329 |test/FTS_0000000000000147_DELETED_CACHE | 285 |
| 327 |test/opening_lines | 283 |
+----------+----------------------------------------------------+-------+
1)最开始的6张表存储反转索引,我们也称之为辅助索引表。当输入的文档拆分为token之后,每个单词(也称之为‘token’)和位置信息、文档ID(DOC_ID)一起被插入到索引表当中。所有单词被一起排序,然后根据对应字符集中单词首字母的排序权重,将它们分解到这6个不同的索引表当中。
2)反转索引被分解到6张辅助索引表上面,主要用来支持并行地进行索引创建。默认是2个线程来完成输入文本的token化,然后排序并插入单词及其相关数据到索引表里面。线程的数目可以使用innodb_ft_sort_pll_degree来进行配置。当在大型表上面创建FULLTEXT索引的时候,我们就应该考虑增加这个选项的值。
3)辅助索引表的名字以FTS_开头,用INDEX_*结尾。辅助索引表和被索引表的关联是通过辅助索引表的名字中记录被索引表的ID值(16进制)来实现。比如:上面的被索引表的id为327,所以索引表的名字中有0x147.
4)代表FULLTEXT索引的index_id也被放在了辅助索引表的名字当中。比如:上面的0x1c9就是FULLTEXT索引的ID。
注意:如果被索引表是
file-per-table模式创建的,那么辅助索引表存放在它们自己的表空间中。
- 在上面例子中的其它索引表,用来处理删除和存放FULLTEXT索引的内部状态。
- FTS_*_DELETED
和FTS_*_DELETED_CACHE:包括哪些已经被删除但是还没有真正从FULLTEXT索引中删除的文档ID。FTS_*_DELETED_CACHE是表FTS_*_DELETED的内存版本。 - FTS_*_BEING_DELETED
和FTS_*_BEING_DELETED_CACHE:包括那些数据正在从FULLTEXT索引中删除的文档ID。TS_*_BEING_DELETED_CACHE是FTS_*_BEING_DELETED
的内存版本。 - FTS_*_CONFIG:存放FULLTEXT索引内部状态的信息。最最重要的是,它存放了
FTS_SYNCED_DOC_ID,用来标识哪些已经完成解析并刷入到磁盘上的文档。在奔溃恢复的时候,FTS_SYNCED_DOC_ID用来标识哪些还没有刷新到磁盘上面的文档,因此,我们可以对这些文档重新进行解析,并加入到FULLTEXT索引的cache里面。可以通过查询
INFORMATION_SCHEMA.INNODB_FT_CONFIG表来查看相关信息。
- FTS_*_DELETED
FULLTEXT Cache
- 当一个文档被插入,需要对它进行token化,然后将每个单词以及相关数据插入到FULLTEXT索引里面。即使是对小文档,这个过程都会导致大量的小单词插入到辅助索引表中,从而导致并发访问这些表的性能下降。为了避免这个问题,InnoDB使用FULLTEXT索引缓存来为最近插入的行进行临时缓存。这个内存中的缓存结构保持插入记录直到缓存满了,然后批量地将它们刷新到磁盘上(也就是辅助索引表上)。可以通过查询INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE表来查看最近插入行的被token化的数据。
- FULLTEXT索引缓存和批量刷新磁盘,可以有效减少对辅助索引表的更新频率,从而减轻并发访问的问题。同时,在缓存中,还可以对同一个单词的记录进行合并,从而尽可能地减小辅助索引表的大小。
- 选项innodb_ft_cache_size
用来配置FULLTEXT索引的缓存大小,这个缓存大小直接影响刷新数据的频率。还可以使用innodb_ft_total_cache_size选项来配置所有FULLTEXT索引使用的缓存大小的最大值。
其它FULLTEXT索引后续使用了再看,见15.2.6.3
InnoDB索引的物理结构
- 除了空间数据索引,其它InnoDB索引都是B-Tree。空间数据索引使用R-Tree,它特别用于索引多维数据。Index记录都是存放在B-Tree和R-Tree的叶子节点上面。索引页的默认大小为16k。
- 当有新记录插入到InnoDB的聚合索引上面的时候,InnoDB尽力预留页面的1/16,用于未来的插入和更新。如果索引记录按照顺序插入(升序或降序),索引页面的使用率约为15/16。如果记录是随机插入的,那么页面的利用率是1/2到15/16之间。
- 对于MySQL
5.7.5,当建立或重建B-tree索引的时候,会执行一个批量加载操作。这种建立索引的方式被称为排序索引创建。innodb_fill_factor
定义了在排序索引创建过程中,B-tree页面的填充百分比,页面中未被填充的空间预留给索引增长使用。排序索引创建不支持空间索引。对于MySQL
5.7.8,
将innodb_fill_factor
设置为100,在聚合索引页面中将会预留1/16的空间用于未来索引增长。 - 如果InnoDB的索引页面的填充百分比,降到比
MERGE_THRESHOLD(默认为50%)还低。InnoDB会尽力从索引树种释放这个页面(当然页面中的现有数据会进行迁移)。配置MERGE_THRESHOLD既适用于B-tree索引,也适用于R-tree索引。 - 我们可以在创建实例之前,通过选项innodb_page_size来配置InnoDB表空间的pagesize。如果一个实例的页面大小被配置,那么我们就不能修改它。MySQL支持64k、32k、16k、8k以及4k的pagesize。其中,32k和64k的页面大小的支持,是从5.7.6版本开始的。
- 使用某种pagesize大小的Mysql
实例,不能使用pagesize大小不一样的数据文件或日志文件。
Change Buffer
- Change Buffer是一种特殊的数据结构,它用来缓存,那些受影响但不在buffer
pool的二级索引页面的修改。在读操作的过程中,如果有页面(其修改被缓存到Change
Buffer当中)被加载到buffer
pool,它会和Change
Buffer对应页面的修改进行合并(这些修改一般由DML操作引起,比如:INSERT、DELETE、UPDATE)。 - 和聚合索引不一样的是,二级索引通常不是唯一的,而且插入到二级索引的顺序一般是随机的。同样的,删除和更新操作影响的二级索引页面极有可能不是紧邻的页面。当有的操作将受影响的页面读取到buffer
pool的时候,将这些页面同缓存的修改进行合并,可以减少一次对二级索引页面的读取。 - 当系统很空闲或在友好shutdown的时候,purge操作会被启动,将更新的索引页面写入到磁盘上。purge操作一般是将多个索引修改一次性写入磁盘,而不是对每个修改都写一次磁盘。
- 如果有大量的二级索引被更新,影响的行业非常多,那么,Change
Buffer的合并可能需要几个小时。在合并期间,磁盘I/O显著上升,可能极大影响查询操作的性能。Change
Buffer的合并也可能会在事务提交后持续发生。事实上,Change
Buffer的合并还可能在Mysql停止后进行重启的时候发生。 - 在内存当中,Change
Buffer占用了部分InnoDB
buffer
pool的内存。在磁盘上,Change
Buffer是系统表空间的一部分。因此,即使系统重启了,Change
Buffer还可以在内存中缓存(读取磁盘中的Change
Buffer)。 - Change
Buffer缓存什么样的数据类型,可以通过
innodb_change_buffering配置选项来控制。该选项是一个Enumeration,可以填充的值为:
值 |
说明 |
none |
不缓存任何操作 |
inserts |
缓存insert操作 |
deletes |
缓存delete 标记操作,严格来说,就是将索引记录标记为在后续Purge过程中需要进行删除的操作。 |
changes |
inserts + deletes |
purges |
缓存在后台发生的物理删除操作 |
all |
默认配置,inserts + deletes + purges |
- 对Change
Buffer进行监控:- InnoDB标准监控输出包括Change
Buffer的状态信息。可以使用SHOW
ENGINE INNODB STATUS命令来查看监控数据:其中Change
Buffer的状态信息显示在“
INSERT BUFFER AND ADAPTIVE HASH INDEX”下面,比如:
- InnoDB标准监控输出包括Change
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
insert 0, delete mark 0, delete 0
discarded operations:
insert 0, delete mark 0, delete 0
Hash table size 4425293, used cells 32, node heaphas 1 buffer(s)
13577.57 hash searches/s, 202.47 non-hash searches/s
- INFORMATION_SCHEMA.INNODB_METRICS表提供了InnoDB标准监控中提供的绝大部分信息,并且还提供了很多额外的信息,为了查看hange
Buffer的状态信息,使用下面的语句:
mysql> SELECT NAME, COMMENT FROMINFORMATION_SCHEMA.INNODB_METRICS WHERE NAME LIKE ‘%ibuf%‘\G
- 表INFORMATION_SCHEMA.INNODB_BUFFER_PAGE提供了在buffer
pool中每个页面的元数据,包括:Change
Buffer的索引和Change
Buffer的位页面。Change
Buffer的索引页面使用PAGE_TYPE.IBUF_INDEX类型进行标识;Change
Buffer的位图页面使用PAGE_TYPE.IBUF_BITMAP类型进行标识。(注意:查询INNODB_BUFFER_PAGE表将会消耗比较大的性能,最好不要再现网进行操作) - Performance
Schema为高级性能监控提供了监控Change
Buffer锁等待的手段。
mysql> SELECT * FROMperformance_schema.setup_instruments
WHERE NAME LIKE ‘%wait/synch/mutex/innodb/ibuf%‘;
+-------------------------------------------------------+---------+-------+
| NAME | ENABLED | TIMED |
+-------------------------------------------------------+---------+-------+
| wait/synch/mutex/innodb/ibuf_bitmap_mutex | YES |YES |
| wait/synch/mutex/innodb/ibuf_mutex | YES | YES |
|wait/synch/mutex/innodb/ibuf_pessimistic_insert_mutex | YES | YES |
+-------------------------------------------------------+---------+-------+
Adaptive
Hash
Indexes
- 自适应hash索引,使得InnoDB的行为就像是一个内存数据库,它拥有一定的工作负荷和充足的内存,但是不会牺牲任何事物特性和可靠性。这个特性可以使用选项
innodb_adaptive_hash_index来开启,通过在服务器启动时,增加
--skip-innodb_adaptive_hash_index参数来关闭。 - 基于对查询操作的观察,MySQL会使用key的前缀来创建hansh索引。这个前缀的长度是不固定的,也许B-Tree中的值只有一部分会出现在hansh索引中。hansh索引是按需创建的,MySQL只会对那些经常访问的页面创建hansh索引。
- 如果一张表几乎完全符合在主内存中,hash索引可以通过直接元素查找、将index的值变成指针来加快查询的速度。InnoDB有一种机制来监控索引的查询。如果InnoDB认为查询可以从Hash索引中受益,InnoDB会自动创建Hash索引。
- 如果有一定的工作负荷,Hash索引对查询提升的速度远超过对监控index查询和维护hash索引数据结构的开销。但是,有时候,如果负载特别高,Hash索引中使用的读写锁会急剧线程的竞争的竞争,比如:多并发Join。使用LIKE和%进行的查询也不太能从自适应HASH索引中得到速度的提升。在工作负荷很大,导致自适应Hash索引不再需要的时候,关闭自适应Hash索引特性,可以减小不必要的性能瓶颈。但是由于预测自适应Hash索引是否会引起性能下降很困难,我们通常需要通过模拟现网负载来进行测试。
- 在MySQL
5.7.8中,自适应Hash索引被分片了。每个分片使用一个锁来进行保护,在之前,整个自适应Hash索引使用一把锁来进行保护。进行分片后可以大大降低线程间的竞争。分片特性,使用innodb_adaptive_hash_index_parts来进行配置,默认值为8,最大可以配置为512. - Hash索引是基于表中的B-Tree索引来创建的。InnoDB可以在B-Tree索引key的任意长度的前缀上创建Hash索引,这个长度依赖于InnoDB观察到的表查询模式。Hash索引一般只对那些经常查询的页面进行索引。
- 我们可以通过监控SHOW ENGINE INNODB STATUS输出的SEMPHORES部分,来查看Hash索引的锁竞争情况。如果看到有很多线程等待在btr0sea.c中创建的读写锁,那么就有必要关闭自适应Hash索引了。
物理行结构
- InnoDB表的物理行结构依赖于表创建时的行格式。如果表创建的时候没有给定行格式,就会使用默认的行格式。在MySQL
5.7.6及其之前的版本,InnoDB使用
Antelope文件格式,并默认采用COMPACT行格式。在MySQL
5.7.7中,innodb_file_format
的默认值为Barracuda,在MySQL
5.7.9中,行格式通过 innodb_default_row_format选项来进行配置,它的默认值为
DYNAMIC。为了和老版本的MySQL
进行兼容,REDUNDANT格式同样还可以进行使用。 - 为了检查当前使用的行格式,可以使用SHOW
TABLE
STATUS。比如:
mysql> SHOW TABLE STATUS IN test1\G
*************************** 1. row***************************
Name: t1
Engine: InnoDB
Version: 10
Row_format: Compact
Rows: 0
Avg_row_length: 0
Data_length: 16384
Max_data_length: 0
Index_length: 16384
Data_free: 0
Auto_increment: 1
Create_time: 2014-10-31 16:02:01
Update_time: NULL
Check_time: NULL
Collation: latin1_swedish_ci
Checksum: NULL
Create_options:
Comment:
- 当然还可以通过查询表INFORMATION_SCHEMA.INNODB_SYS_TABLES.来获取行格式:
mysql> SELECT NAME, ROW_FORMAT FROMINFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE NAME=‘test1/t1‘;
+----------+------------+
| NAME | ROW_FORMAT |
+----------+------------+
| test1/t1 | Compact |
+----------+------------+
- COMPACT行格式可以减少20%的存储量,但是会牺牲一定的CPU性能。如果数据库负荷受限于缓存的命中率和磁盘速度,COMPACT格式可能会更快。如果数据库负荷受限于CPU速度,COMPACT格式可能会降低整体性能。
- InnoDB表中的行格式采用REDUNDANT,它有以下特征:
- 每个索引记录包括6个字节的头部。头部用于将连续的记录连接在一起,同时也用于行级锁。
- 在聚合索引中,包含了用户定义的所有列。此外,还有6个字节的事务ID以及7个字节的回滚指针。
- 如果表没有定义主键,每个聚合索引中还包括6个字节的row
ID字段 - 每个二级索引记录也包括了在聚合索引中的主键字段(只有主键字段不在二级索引中才会被包含)。
- 记录中包含指向每个字段的指针。如果记录中的字段总长度小于128字节,指针是1个字节;否则,就是2个字节。这些指针组成的数组,我们称之为记录目录。这些指针指向的区域,我们称之为记录的数据部分。
- InnoDB在内部使用固定长度格式来存储固定长度的字符列,比如:CHAR(10)。InnoDB不会从VARCHAR列中去除空白符。
- 对于SQL中的NULL值,在记录目录中会预留1个或2个字节。除了这些,一个SQL的NULL值,如果存储的是可变长度的列,则不会占用空间;如果是固定长度的列,那么会预留固定长度的空间。为NULL预留空间,使得更新记录时,从NULL转为非NULL,不会导致索引页的分片。