一、InnoDB概叙
1、后台线程
Innodb后台线程主要有:master主线程、多个IO 线程(书上写的是4个,但我看我本地的是read thread、write thread是各4个,应该是版本问题。而且书上写的通过innodb_file_io_threads
控制我也没有查到,书上应该是5.1版本)、一个锁监控线程、1个错误监控线程。
1)、InnoDB存储引擎 FILE I/O
通过show engine innodb status\G
查看:
可以看到FILE I/O
线程有:一个insert buffer thread
插入缓存线程、log thread
日志记录线程、read thread
4个读线程、write thread
4个写线程。
2)、master thread
master thread的线程优先级是最高的,其内部是几个(循环loop)操作,在这个线程中会分为几个操作:例如每一秒钟操作、每10分钟操作(这个时间会延迟,但InnoDB会尽量保证这个频率)。
1、每秒一次的操作:
日志缓冲刷新到磁盘,即便事务没有提交(总是)
合并插入缓冲 (可能)
至多刷新100个缓冲池中的脏页到磁盘(可能)。(书的版本是5.1版,所以数字可能在新版本会有变化?)
如果没有活动,则切到backgroup loop
。
刷新脏页:InnoDB会通过判断当前缓冲池中脏页的比例是否超过配置参数innodb_max_dirty_pages_pct
,如果超过就会执行磁盘同步IO操作。
可以看到这里默认是75%(书上写的是90%)。还有就是这里也每秒都进行了磁盘IO同步,但为什么不直接找到写到对应数据页中,这是因为日志文件是直接追加的,并不需要找到对应页的位置,以及其他附带的索引文件的操作等。而如果是直接写数据页,就需要进行索引的操作,同时需要去找到应该插入的对应的页,这些操作会花费更多的资源。
2、每10秒操作
刷新100个脏页到磁盘(可能)
合并至多5个插入缓冲(总是)
将日志缓冲刷新到磁盘(总是)
删除无用的Undo页(总是)
产生一个检查点(总是)
刷新100个或者10个脏页到磁盘(总是)
在以上过程中,InnoDB存储引擎会判断过去10秒之内磁盘操作是否小于200次,如果是,InnoDB就判断当前有足够的磁盘能力,所以其就会将100个脏页刷新到磁盘。然后执行full purge
,即删除无用的脏页。
3、backgroup loop
这个循环执行是如果当前没有用户活动(数据库空闲),或者关闭时就会切换到这个循环。这个循环的操作:
删除无用的Undo页(总是)
合并20个插入缓冲(总是)
跳回到主线程(总是)
如果没有事情,InnoDB会切换到suspend_loop
,将master thread挂起,等待事情的发送。可以通过前面的查看InnoDB的状态的语句查看循环运行情况:
2、内存
InnoDB存储引擎内存的主要组成:缓冲池(buffer pool)、重做日志缓冲池(redo log buffer)、额外的内存池(additional memory pool)。
1)、buffer pool
缓冲池是占最大块内存的部分、用来存放各种数据的缓存。Innodb存储引擎的工作方式总是将数据库文件按页(每页16K)读取到缓冲池,然后按最近最少使用(LRU)的算法来保留再缓冲池的缓存数据。
如果数据库文件需要修改,首先是修改缓存池中的页(发生修改后,存在缓冲池中的该页即称为脏页,因为其已经与数据文件不一致了),然后按照一定的频率将缓冲池中的脏页刷新到对应的数据文件。同样可以通过前面的show engine innodb status\G
。
buffer pool size
:表明共有多少缓冲帧(buffer frame),每个缓冲帧为16K。
Free bufffers
:表明当前空闲的缓冲帧。
Database pages
:表明已经使用的缓冲帧。
Modified reads
:表示当前的脏页的数量(也就是被修改的页)
2)、Innodb存储引擎内存结构
缓冲池中缓存的数据页类型:索引页、自适应哈希索引、数据页、undo页、插入缓冲(insert buffer)、InnoDB存储的锁信息(lock info)、数据字典信息(data dictionary)。
日志缓冲:日志缓冲(redo 重做日志缓冲),将重做日志信息先放到这个缓冲区,然后按一定频率刷新到重做日志文件,一般情况是每一秒就会将重做日志缓冲刷新到日志文件。
3)buffer pool 的参数
show variables like 'innodb_buffer_pool_size';
缓冲池的大小。
3、关键特性
1)、插入缓冲
插入缓冲是相对于索引来说的,是插入非唯一索引到索引页,而不是对插入数据到数据页。
主键是行唯一的标识符,在应用程序中行记录的插入顺序是按照主键递增的顺序插入的。因此插入聚集索引一般是顺序的,不需要裁判的随机读取。但非唯一的辅助索引就不同了,因为其需要离散地访问非聚集索引也,插入的性能就变低了。
所以InnoDB就设计了对非聚集索引的插入或更新操作,不是每次直接将将其插入到索引页。而是先将其先放到插入缓冲区中,然后按一定的频率(上面有讲)执行插入缓冲与非聚集索引页叶子节点的合并操作。有了缓存就可以整合,例如如果多个索引数据是同一个索引页。就能一次更新到磁盘了。
按上面的描叙就需要两个对应的条件才能使用插入缓冲:
索引是辅助索引
索引不是唯一的(如果是唯一的,也就需要离散去遍历判断了,这样插入缓冲就失去意义了)
可以通过前面的查看InnoDB语句查看插入缓冲的使用情况
seg size
:当前插入缓冲的大小(单位为16KB),free list len
:表示空闲列表的长度,size
:表示已经合并的数量。
2)、两次写
两次写是对于写磁盘数据页来说的,简单理解就是增加一个备份页。例如当数据库在写一个页,但还没有写完就宕机了,我们称为部分写失效(partial page write)。
不过我们不是还有redo重做日志吗?书上是说的:重做日志记录的是对页的物理操作,例如偏移量800,写aaa
记录,但如果这个页本身就是损坏的,所以再进行重写也没有意义。所以我们就需要一个备份页,当写入失效时,我们就可以通过副本也来还原该页再进行重做,也就是doublewrite。
doublewrite由两部分:一个就是内存中的Doublewrite Buffer
,另外就是物理磁盘上共享表空间中连续的128页,即两个区(extent)。当缓冲池的脏页刷新时,并不直接写磁盘,而是通过memcpy
函数先将脏页拷贝到内存中,之后马上通过fsync
函数同步磁盘,应为doublewrite页是连续的,所以这个过程是顺序写,开销并不大。在完成doublewrite后,在将数据写到各自的表空间中(如果有设置每个表有独立的表空间)。
可以通过show global status like 'innodb_dblwr%'
来查看
第一个是表示:写的页的数量,第二个是表示:实际写入的次数。
3)、自适应哈希
InnoDB索引引擎会监控对表上索引的查找,如果观察建立哈希索引可以带来速度的提升,则会自己建立哈希索引,所以称为自适应的。自适应哈希索引通过缓冲池的B+数建立,而且不需要将整个表都建立哈希索引,所以会很快。
可以通过前面查看InnoDB引擎状态的语句在INSERT BUFFER AND ADAPTIVE HASH INDEX
这段,查看其使用。