MySql技术内幕-InnoDB存储引擎简要

首先应该认识到一件事:MySQL数据库的核心就在于存储引擎。存储引擎是基于表的。

由于开源,MySQL数据库第三方引擎有很多,可以满足不同的特定需求,大名鼎鼎的InnoDB就是其中之一(最早是,后来被Oracle收购)。

InnoDB存储引擎---高性能,高可用,高拓展:

1. 体系架构

  

 

 

 由图可见,InnoDB存储引擎有多个内存块共同组成一个大的内存池,负责如下工作:

1)维护需要访问的数据结果。

2)缓存磁盘数据,方便快速读取。

3)重做日志。

后台线程主要作用就是刷新内存池中数据,保证缓存的是最近的数据,此外还会将已修改的数据刷新到磁盘,保证数据库异常是能够恢复。

2.内存

1)缓冲池:一块内存区域,用来弥补磁盘速度慢的影响。在数据库读取时,首先将从磁盘读取到的页放在缓冲池,下次读取相同页时直接从缓存中读取。对于页数据修改时,首先修改缓冲池中的,再以一定频率(Checkpoint机制)刷新到磁盘。

因此,缓冲池的大小直接影响数据库整体性能,一般32位操作系统最大3G,但由于内存技术的成熟,64位的系统PC服务器已经能支持512G了,

存储引擎如何管理内存区域:LRU(最近最少使用)算法管理:即最频繁使用的页再LRU列表前端,而最少使用的页在LRU列表的尾端。当缓冲池不能存放新读取到的页时,将首先释放LRU列表中尾端的页。但在InnoDB中,新读取到的页,虽然是最新访问的页,但并不是直接放入到LRU列表的首部,而是放入到LRU列表的midpoint位置。这是因为若直接将读取到的页放入到LRU的首部,那么某些SQL操作可能会使缓冲池中的页被刷新出,从而影响缓冲池的效率

常见的这类操作为索引或数据的扫描操作。这类操作需要访问表中的许多页,甚至是全部的页,而这些页通常来说又仅在这次查询操作中需要,并不是活跃的热点数据。如果页被放入LRU列表的首部,那么非常可能将所需要的热点数据页从LRU列表中移除,而在下一次需要读取该页时,InnoDB存储引擎需要再次访问磁盘。为了解决这个问题,InnoDB存储引擎引入了另一个参数来进一步管理LRU列表,这个参数是innodb_old_blocks_time,用于表示页读取到mid位置后需要等待多久才会被加入到LRU列表的热端。

 在LRU列表中的页被修改后,称该页为脏页(dirty page),即缓冲池中的页和磁盘上的页的数据产生了不一致。这时数据库会通过Checkpoint机制将脏页刷新回磁盘,而Flush列表中的页即为脏页列表。需要注意的是,脏页既存在于LRU列表中,也存在于Flush列表中。LRU列表用来管理缓冲池中页的可用性,Flush列表用来管理将页刷新回磁盘,二者互不影响。

Checkpoint机制:

1)数据库关闭时,刷新全部脏页。

2)剩余空闲页不足时,部分刷新。

3)脏页太多时,部分刷新。

 

2. 该存储引擎支持事务,应该这么说,该存储引擎设计目标主要就是面向在线事务处理,当然还有其他一些特点:

支持行锁:InnoDB 的行锁机制是通过索引来完成的,对于大多数使用索引查询的sql来说,效率很高。

缓冲处理:相比MyISAM引擎只缓存索引,InnoDB不仅缓存索引,还缓存数据。

支持外键:外键约束确保了数据的完整性,虽然说外键会影响数据库性能(主要并发高低问题,对子表进行写入操作(UPDATE/INSERT)的时候,父表就会被加上“共享锁”,这样在对子表高并发进行写入操作的情况下,对父表的写入操作就会由于“共享锁”的存在,而会长时间不能得到更新),但对大部分用户来说,为保证数据完整性,外键控制仍然是成本最低的选择。

默认读取不产生锁(一致性非锁定读)。

关键特性:

1)插入缓冲:InnoDB存储引擎开创性地设计了Insert Buffer,对于非聚集索引的插入或更新操作,不是每一次直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,若在,则直接插入;若不在,则先放入到一个Insert Buffer对象中,好似欺骗。数据库这个非聚集的索引已经插到叶子节点,而实际并没有,只是存放在另一个位置。然后再以一定的频率和情况进行Insert Buffer和辅助索引页子节点的merge(合并)操作,这时通常能将多个插入合并到一个操作中(因为在一个索引页中),这就大大提高了对于非聚集索引插入的性能。

目前Insert Buffer存在一个问题是:在写密集的情况下,插入缓冲会占用过多的缓冲池内存(innodb_buffer_pool),默认最大可以占用到1/2的缓冲池内存。Percona上发布一些patch来修正插入缓冲占用太多缓冲池内存的情况,即对插入缓冲的大小进行控制。

2)两次写:当发生数据库宕机时,可能InnoDB存储引擎正在写入某个页到表中,而这个页只写了一部分,比如16KB的页,只写了前4KB,之后就发生了宕机,这种情况被称为部分写失效(partial page write)。需要一个页的副本,当写入失效发生时,先通过页的副本来还原该页,再进行重做,这就是doublewrite。在对缓冲池的脏页进行刷新时,并不直接写磁盘,而是会通过memcpy函数将脏页先复制到内存中的doublewrite buffer,之后通过doublewrite buffer再分两次,每次1MB顺序地写入共享表空间的物理磁盘上,然后马上调用fsync函数,同步磁盘,避免缓冲写带来的问题。

 3)自适应哈希:InnoDB存储引擎会自动根据访问的频率和模式来自动地为某些热点页建立哈希索引。

4)异步IO:为了提高磁盘操作性能,当前的数据库系统都采用异步IO(Asynchronous IO,AIO)的方式来处理磁盘操作。用户在发出一个IO请求后立即再发出另一个IO请求,当全部IO请求发送完毕后,等待所有IO操作的完成,这就是AIO。

5)刷新邻接页:当刷新一个脏页时,InnoDB存储引擎会检测该页所在区(extent)的所有页,如果是脏页,那么一起进行刷新。这样做的好处显而易见,通过AIO可以将多个IO写入操作合并为一个IO操作,故该工作机制在传统机械磁盘下有着显著的优势。

但是需要考虑到下面两个问题:

❑是不是可能将不怎么脏的页进行了写入,而该页之后又会很快变成脏页?

❑固态硬盘有着较高的IOPS,是否还需要这个特性?

为此,InnoDB存储引擎从1.2.x版本开始提供了参数innodb_flush_neighbors,用来控制是否启用该特性。对于传统机械硬盘建议启用该特性,而对于固态硬盘有着超高IOPS性能的磁盘,则建议将该参数设置为0,即关闭此特性。

 

3. InnoDB存储引擎是将数据放在一个逻辑的表空间,这个表空间就像黑盒一样由InnoDB管理。InnoDB默认采用聚集的方式存储数据,因此每张表都按照逐渐顺序存放,如果没有显示定义主键,InnoDB会在表定义时自动生成ROWID作为主键。

 

  

注:全文摘自《mysql技术内幕》。

 

posted @ 2022-02-17 17:36  lv99  阅读(386)  评论(0编辑  收藏  举报