InnoDB特性介绍
MySQL区别于其他数据库的最为重要的特点就是其插件式
的表存储引擎
。而在众多存储引擎中,InnoDB
是最为常用
的存储引擎。从MySQL5.5.8
版本开始,InnoDB
存储引擎是默认的存储引擎
。
InnoDB存储引擎支持事务
,其设计目标主要面向在线事务处理
(OLTP)的应用。其特点是行锁设计
、支持外键
,并支持非锁定读
,即默认读操作不会产生锁
。
InnoDB通过使用多版本并发控制(MVCC)
来获取高并发性
,并且实现了SQL标准的4种隔离级别
,默认为Repeatable Read
级别。同时,使用一种被称为next-key-locking
的策略来避免幻读
现象的产生。除此之 外,InnoDB存储引擎还提供了插入缓冲
(insert buffer)、双写
(double write)、自适应哈希索引
(adaptive hash index)、预读
(read ahead)等高性能和高可用的功能。
InnoDB内存结构
上图详细显示了InnoDB存储引擎的体系架构,从图中可见,InnoDB存储引擎由内存池
,后台线程
和磁盘文件
三大部分组成。接下来我们就来简单了解一下内存相关的概念和原理。
缓冲池
InnoDB存储引擎是基于磁盘存储
的,并将其中的记录
按照页
的方式进行管理
。但是由于CPU速度和磁盘速度之间的鸿沟,基于磁盘的数据库系统通常使用缓冲池
记录来提高数据库的的整体性能
。
在数据库中进行读取操作
,首先将从磁盘中读到的页放在缓冲池中
,下次再读相同的页
中时,首先判断
该页是否在缓冲池
中。若在缓冲池中,称该页在缓冲池中被命中,直接读取该页
。否则,读取磁盘上的页
。
对于数据库中页的修改操作
,则首先修改在缓冲池中的页
,然后再以一定的频率刷新到磁盘上
。页从缓冲池刷新回磁盘的操作
并不是在每次页发生更新时触发,而是通过一种称为CheckPoint的机制
刷新回磁盘。
所以,缓冲池
的大小直接影响
着数据库的整体性能
,可以通过配置参数innodb_buffer_pool_size
来设置。
具体来看,缓冲池中缓存的数据页类型有:
-
数据页(data page)
-
索引页(index page)
-
插入缓冲(insert buffer)
在InnoDB引擎上进行插入操作时,一般需要按照主键顺序进行插入,这样才能获得较高的插入性能。当一张表中存在非聚簇
的且不唯一
的索引时,在插入时,数据页的存放还是按照主键进行顺序存放,但是对于非聚簇索引叶节点的插入不再是顺序的了,这时就需要离散的访问非聚簇索引页,由于随机读取的存在导致插入操作性能下降。InnoDB为此设计了Insert Buffer来进行插入优化。对于
非聚簇索引
的插入
或者更新
操作,不是每一次都直接插入到索引页中,而是先判断插入的非聚集索引是否在缓冲池中,若在,则直接插入
;若不在,则先放入到一个Insert Buffer中
。看似数据库这个非聚集的索引已经查到叶节点,而实际没有,这时存放在另外一个位置。然后再以一定的频率和情况
进行Insert Buffer和非聚簇索引页子节点的合并
操作。这时通常能够将多个
插入合并到一个操作
中,这样就大大提高了对于非聚簇索引的插入性能
。 -
自适应哈希索引(adaptive hash index)
InnoDB会根据访问的频率
和模式
(where),为热点页建立哈希索引,来提高查询效率
。InnoDB存储引擎会监控对表上各个索引页的查询,如果观察到建立哈希索引可以带来速度上的提升,则建立哈希索引,所以叫做自适应哈希索引。自适应哈希索引是
通过缓冲池的B+树页
构建而来,因此建立速度很快
,而且不需要对整张数据表建立哈希索引
。其有一个要求
,即对这个页的连续访问模式必须是一样
的,也就是说其查询的条件(WHERE)必须完全一样,而且必须是连续的
。 -
锁信息(lock info)
InnoDB存储引擎会在行级别上对表数据进行上锁。不过InnoDB也会在数据库内部其他很多地方使用锁,从而允许对多种不同资源提供并发访问。数据库系统使用锁是为了支持对共享资源进行并发访问,提供数据的完整性和一致性。 -
数据字典信息(data dictionary)
InnoDB有自己的表缓存,可以称为表定义缓存或者数据字典
。当InnoDB打开一张表,就增加一个对应的对象到数据字典。数据字典是对数据库中的数据、
库对象
、表对象
等的元信息的集合
。在MySQL中,数据字典信息内容就包括表结构、数据库名或表名、字段的数据类型、视图、索引、表字段信息、存储过程、触发器等内容。MySQL INFORMATION_SCHEMA库提供了对数据局元数据、统计信息、以及有关MySQL server的访问信息(例如:数据库名或表名,字段的数据类型和访问权限等)。该库中保存的信息也可以称为MySQL的数据字典。
双写(double write)
如果说Insert Buffer给InnoDB存储引擎带来了性能上的提升,那么Double Write
带给InnoDB存储引擎的是数据页
的可靠性
。
如上图所示,Double Write由两部分组成
,一部分是内存中的double write buffer
,大小为2MB
,另一部分是物理磁盘
上共享表空间连续的128个页
,大小也为2MB
。在对缓冲池的脏页
进行刷新时
,并不直接写磁盘,而是通过memcpy
函数将脏页先复制
到内存中的double write buffer区域
,之后通过doublewrite buffer再分两次,每次1MB顺序地写入共享表空间的物理磁盘上
,然后马上调用fsync
函数,同步磁盘
,避免操作系统缓冲写带来的问题。在完成doublewrite页的写入后,再
将double wirite buffer中的页写入各个表空间文件
中。
MySQL的数据页
默认是16k
,对数据页的校验也是按16k计算的
。而操作系统
的数据页默认是2k或者4k
,IO操作是按系统页
为单位就行读写的。这就可能出现一种情况,数据库对一个16k的数据页修改后,操作系统开始进行写磁盘,每次写4k,结果刚写完第一个4k,数据库挂了。这时候数据库重启时,校验数据页
,发现有数据页不完整
,就起不来了,即使通过歪门邪道使数据库起来,也会有一页的数据丢失
。
简单来说,修改后的脏页放到double write buffer区,这个区占用2M内存空间,buffer空间满或其他条件触发,使double write buffer存的脏页先写到共享表空间,之后在写入数据文件。这个时候如果写了不完整的页,可以用共享表空间中完整的页加以覆盖
,数据页完整了,数据库也就可以拉起了,之后的各种恢复就看redo log的
了。提到redo log了有人可能会说,不完整的数据页用redo log恢复呗,搞个double write多此一举吗。这时候不得不介绍一下redo log的记录日志格式了——redo log
是按数据块的方式记录日志
的,差不多类似于脏页直接放到redo log中,但又不完全相同,不然redo log得多占空间啊。它是根据偏移量来记录修改
了,比如test数据文件的第1025个数据块的100字节的偏移位置,数据修改为了‘new data’。一个16k的数据块不可能哪儿哪儿都修改,有可能仅仅修改里面的一个字节,而redo log的工作作风就是:修改哪儿记录哪儿。这样,数据页不完整了,找redo log没用
。