目录
简介
MySQL的体系结构可以分为连接层和服务层;连接层主要是各种外部的语言连接接口,服务层则是MySQL的核心;其中服务层又包括各种连接池、管理组、SQL接口、查询分析器、优化器、缓冲组件、存储引擎、物理文件;
本文主要介绍了存储引擎Innodb的一些相关特性;
1、innodb体系结构是怎样的?
innodb引擎由存储引擎内存池和多个后台线程组成;存储的数据以页为单位;
- 内存池分为多个部分,需要完成的功能有维护所有后台线程的访问、缓存磁盘上的数据方便快速读取、重做日志缓冲等;
- 后台线程主要作用是负责刷新内存池中的数据、保证缓冲池中的内存缓存是最近的数据。将已修改的数据文件刷新到磁盘文件,同时保证在数据库发生异常的情况下InnoDB能恢复到正常运行状态
2、innodb的线程
innodb引擎的线程一般指其后台线程组,常见包括主线程、io线程、purge Thread 、Page Cleaner Thread;
- 主线程主要负责缓冲池中的数据异步刷新到磁盘,保证数据的一致性;
- io线程主要负责多个io请求的回调处理。包括write、read、insert buffer、log、io thread;这些线程以AIO方式处理io请求。可以通过各种innodb参数来设置;
- purge thread用于挥手一些日志信息,主要是已分配的ubdolog;
- Page Cleaner Thread主要用于脏页面的刷新工作,即对于一些错误信息的更新;
3、如何理解innodb的内存
innodb引擎基于磁盘进行一个信息的存储。但是磁盘的访问效率低,远不及内存;所以引入了缓冲池来提高数据访问性能。此外,内存除了需要有缓冲池,还有其管理工具,重做日志缓冲、额外空闲内存区;
-
缓冲池主要是实现一个缓存的功能,即二次访问相同数据时的快速读取;它的大小直接影响整个引擎的性能;当然缓冲池中存储的不仅仅只有插入的数据,事实上,缓冲池中缓存的数据页类型有:索引页、数据页、undo页、插入缓冲(insert buffer)、自适应哈希索引(adaptive hash index)、InnoDB存储的锁信息(lock info)、数据字典信息(data dictionary)等
可以有多个缓冲池实例,数据通过不同的hash值存储到不同缓冲池。 -
缓冲池管理区主要包括管理已读取页面的LRU列表,脏页列表Flush列表;
当数据库刚启动时,LRU列表是空的,即没有任何的页。这时页都存放在Free列表中。当需要从缓冲池中分页时,首先从Free列表中查找是否有可用的空闲页,若有则将该页从Free列表中删除,放入到LRU列表中。否则,根据LRU算法,淘汰LRU列表末尾的页,将该内存空间分配给新的页。在LRU列表中的页被修改后,称该页为脏页(dirty page),即缓冲池中的页和磁盘上的页的数据产生了不一致。这时数据库会通过CHECKPOINT机制将脏页刷新回磁盘,而Flush列表中的页即为脏页列表。需要注意的是,脏页既存在于LRU列表中,也存在于Flush列表中。 -
重做日志缓冲区,redo log buffer。InnoDB存储引擎首先将重做日志信息先放入到这个缓冲区,然后按一定频率将其刷新到重做日志文件。这个频率是:1、Master Thread每一秒将重做日志缓冲刷新到重做日志文件;2、每个事务提交时会将重做日志缓冲刷新到重做日志文件;3、当重做日志缓冲池剩余空间小于1/2时,重做日志缓冲刷新到重做日志文件。
-
额外的内存池,在InnoDB存储引擎中,对内存的管理是通过一种称为内存堆(heap)的方式进行的。在对一些数据结构本身的内存进行分配时,需要从额外的内存池中进行申请,当该区域的内存不够时,会从缓冲池中进行申请。
4、innodb如何保证缓冲池和磁盘上的数据一致
事务数据库的一般策略;WtiteAheadLog策略:当事务提交时,先写重做日志(redolog),在修改页;通过重做日志来保持数据持久性;同时重做日志也可以实时将数据写入到缓冲池;
此外还有一个Checkpoint检查点机制用于提供容错,在日志中插入检查点;解决以下问题
- 缓冲池的容量不足以存储当前重做日志中的数据信息:根据LRU算法列出最少使用的页,若此页为脏页,那么需要强制执行Checkpoint,将脏页也就是页的新版本刷回磁盘。
- 数据库发生问题宕机:数据库不需要重做所有的日志,Checkpoint之前的页都已经刷新回磁盘。故数据库只需对Checkpoint后的重做日志进行恢复。这样就大大缩短了恢复的时间。
3.重做日志大小不足以继续运作:将检查点后的数据脏页刷新;
5、innodb的一些关键特性
插入缓冲
Insert Buffer并非是缓冲池的一部分,它其实也是一个物理页表;主要用于解决非聚集索引插入效率的问题;
B+树中插入值是需要比较大小,如果我们插入的主键索引或者其他辅助索引不是自增而是随机数,那么可能出现的一个问题是两条相近的记录存储后的索引不在同一页;离散的访问这些索引表页就导致了数据索引的插入变慢;
InsertBuffer解决的办法:对于这些非聚集索引的插入或更新操作,不是每一次直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,若在,则直接插入;若不在,则先放入到一个Insert Buffer对象中;这样的一个直接作用就是合并了多次的重复索引的插入操作,提高非唯一索引插入性能;
插入缓冲使用需要满足的条件:这个索引是辅助索引,他不是唯一的;
插入缓冲的组成:额,它也是一颗b+树;叶节点存放的是查询的search key(键值),searchKey组成space占用4字节。marker占用1字节,它是用来兼容老版本的Insert Buffer。offset表示页所在的偏移量,占用4字节;在这个b+树中,索引页按照页表id排序好;遇到重入的辅助索引则可以直接查询到它存储的位置。
两次写
两次写主要用于解决存储数据页的可靠性;常见的一个场景是:当数据插入到一半,系统崩了,怎么保证之后的重写数据;此时数据表中这一页的数据只有一半,如果使用重做日志,无法处理之前的那一半数据,因为重做日志上记录的时插入数据物理偏移量,反而会在这一半数据之后的物理地址上进行插入数据。
两次写则是为了解决这个问题,在重做日志前,可以先建立一个页面,当写入失败时,通过这个页面来还原数据。
doublewrite由两部分组成,一部分是内存中的doublewrite buffer,大小为2MB,另一部分是物理磁盘上共享表空间中连续的128个页,即2个区(extent),大小同样为2MB。在对缓冲池的脏页进行刷新时,并不直接写磁盘,而是会通过memcpy函数将脏页先复制到内存中的doublewrite buffer,之后通过doublewrite buffer再分两次,每次1MB顺序地写入共享表空间的物理磁盘上,然后马上调用fsync函数,同步磁盘,
自适应hash索引
innodb中的索引为B+树索引,b+索引相对于hash索引的一个劣势是单行查找。如果有索引页的使用特别频繁,会考虑建立部分自适应哈希索引(Adaptive Hash Index,AHI)来提高查询效率;
InnoDB存储引擎会自动根据访问的频率和模式来自动地为某些热点页建立哈希索引。从而提高查询效率;建立的要求是这个索引页的连续访问模式是相同的,
异步IO
为了提高磁盘操作性能,采用异步IO(Asynchronous IO,AIO)的方式来处理磁盘操作。
异步io即在执行一组SQL查询操作时,其中一个SQL执行不需要等待其他SQL执行完毕才进行执行操作;可以在发出一个IO请求后立即再发出另一个IO请求,当全部IO请求发送完毕后,等待所有IO操作的完成,
刷新邻接页
Flush Neighbor Page(刷新邻接页):当刷新一个脏页时,InnoDB存储引擎会检测该页所在区(extent)的所有页,如果是脏页,那么一起进行刷新。