《MySQL技术内幕——InnoDB存储引擎》读书笔记(二)——InnoDB存储引擎

一、InnoDB存储引擎概述

从MySQL5.5版本开始是默认的表存储引擎,该存储引擎是第一个完整支持ACID事务的MySQL存储引擎,其特点是行锁设计、支持MVCC、支持外键

、提供一致性非锁定读,同事被设计用来最有效地利用以及使用内存和CPU。


二、InnoDB体系架构

1、后台线程

InnoDB存储引擎是多线程的模型,因此后台有多个不同的后台线程,负责处理不同任务。

(1)Master  Thread

非常核心的后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新,

合并插入缓冲(insert buffer)、undo页的回收等。

(2)IO  Thread

在InnoDB存储引擎中大量使用了 AIO(Async  IO  异步的) 来处理写IO请求,这样可以极大提高数据库的性能。

而IO Thread的工作主要负责这些IO请求的回调(call back).

基本是: write 、read 、 insert buffer 、log IO  thread

查看InnoDB的版本: show  variables  like 'innodb_version'    

查看MySQL的版本:   select version()

查看read、write的线程数量:show  variables  like  'innodb_%io_threads'

查看IO Thread情况:show  engine  innodb  status   (读的线程id总是小于写的线程)


(3)Purge  Thread

事务被提交后,其所使用的undolog可能不再需要,因此需要 PurgeThread来回收已经使用并分配的undo页。

1.1版本以前,这个操作由Master Thread来完成,1.1版本以后该操作可以独立到单独的线程中进行,可以提高性能。

通过在配置文件中添加该命令启动独立的Purge Thread: 

innodb_purge_threads=1

1.2版本以后,支持多个 PurgeThread     通过命令查看数量:show variables like 'innodb_purg_threads'

(4)Page Cleaner Thread

1.2版本以后引入的,作用是将脏页刷新操作都放入单独的线程中完成,目的是为了减轻原Master Thread 的工作

及对于用户查询线程的阻塞,进一步提高InnoDB存储引擎的性能。


2、内存

(1)缓冲池

InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理,由于CPU和磁盘速度之间的鸿沟,基于磁盘的数据库系统

通常使用缓冲池技术来提高数据库的整体性能。


缓冲池就是一块内存区域,通过内存的速度来弥补磁盘速度较慢对数据库性能的影响。

数据库中进行读取页操作: 

1、将从磁盘读取的页存放在缓冲池中(将页FIX在缓冲池中)

2、下次读同样的页时,判断该页是否在缓冲池中,如果在则称为被命中,否则,进行第1

数据库中页的修改操作:

1、修改缓冲池中的页

2、通过Checkpoint的机制刷新回磁盘

缓冲池的大小通过来 innodb_buffer_pool_size 设置:show variables like 'innodb_buffer_pool_size'\G;


缓冲池中缓存的而数据页类型有:索引页,数据也,undo页,插入缓冲(insert buffer),自适应哈希索引(adaptive hash index),

InnoDB存储的锁信息(lock info)、数据字典信息(data dicttionary)等。

除了缓冲池(innodb_buffer_pool)还有重做日志缓冲(redo log_buffer)和额外内存池(innodb_addtional_mem_pool_size)


从InnoDB 1.0以后,允许有多个缓冲池实例,每个页根据哈希值平均分配到不同缓冲池实例中。

可以通过innodb_buffer_pool_instances在配置文件中进行配置:show variables like 'innodb_buffer_pool_instances'


MySQL5.6以后 ,可以通过information_schema 架构下的表 innodb_buffer_pool_stats 观察缓冲的状态

use information_schema;

select pool_id,pool_size,free_buffers,database_pages from innodb_buffer_pool_stats;


(1)缓冲池中内存的管理——LRU list,Free list,Flush list


1、LRU list

通常来说,数据库中的缓冲池是通过LRU(Lastest Recent Used,最近最少使用)算法进行管理的。

即最频繁使用的页在LRU列表的前端,而最少使用的页在LRU列表的尾端,当缓冲池不能存放新读取的页时,将首先

释放LRU列表中尾端的页。

在InnoDB存储引擎中,缓冲池中页的大小默认为16KB,最新访问的页并不是直接放入到LRU列表的首部,而是放入

LRU列表的midpoint位置。

这个算法在InnoDB存储引擎下称为  midpoint insertion strategy,默认midpoint是在LRU列表长度的5/8处,midpoint位置可由

参数  innodb_old_blocks_pct 控制,默认值为37,表示新读取的页插入到LRU列表尾端的37%的位置。

mysql> show variables like 'innodb_old_blocks_pct';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| innodb_old_blocks_pct  | 37    |


这样做的目的:

防止出现偶尔使用的大数据量的读操作,把缓冲池中大量的甚至全部的页释放掉,由于这样的操作是偶尔进行的,所以读

到的页并不是活跃的热点数据,如果放在LRU列表的开头可能会把真正的热点数据清除掉,使下一次热点数据的读取需要访问磁盘。


可以通过调小innodb_old_blocks_pct ,如: set global  innodb_old_blocks_pct=20 减少热点页被刷出的概率

InnoDB存储引擎引入了另一个参数来进一步管理LRU列表:innodb_old_blocks_time

用于表示页读取到mid位置后需要等待多久才会被加入到LRU列表的热端。

show variables like 'innodb_old_blocks_time'   


2、Free list

LRU列表管理的是已经读取的页。

当数据库刚启动时,LRU列表是空的,即没有任何的页,这时的页都存放在Free列表中。

当需要从缓冲池中分页时:

1、从Free列表中查找是否有可用的空闲页,若有则将该页从Free列表删除,放入到LRU列表中

2、否则,根据LRU算法,淘汰LRU列表末尾的页,将该内存空间分配给新的页


当页从LRU列表的old部分加入到new部分,称为:page made young   移动到前端的次数

因为innodb_old_blocks_time的设置,导致页没有冲old部分移动到new部分的操作称为:page not made young

buffer pool hit rate  缓冲池的命中率,小于95%。需要观察是否由于全表扫描引起的LRU列表被污染的问题


InnoDB 1.2以后,可以通过information_schema 架构下的表 innodb_buffer_pool_stats 观察缓冲的状态

use information_schema;

select pool_id,pool_size,free_buffers,database_pages from innodb_buffer_pool_stats;

通过表innodb_buffer_page_lru 观察每个LRU列表中每个页的具体信息:

select * from innodb_buffer_page_lru limit 1


InnoDB 1.0以后,开始支持压缩页的功能,即将原本16KB的页压缩为1KB、2KB、4KB和8KB。

对于非16KB的页,是通过unzip_LRU列表进行管理的。

可以通过innodb_buffer_page_lru来观察unzip_LRU列表中的页。


3、Flush list

在LRU列表中的页被修改后,该页称为脏页(dirty page),即缓冲池中的页和磁盘上的页的数据产生了不一致。

这时,数据库会通过checkpoint机制将脏页刷新回磁盘,而Flush列表中的页即为脏页列表。

注意脏页既存在于LRU列表中,也存在于Flush列表中。


LRU列表用来管理缓冲池中页的可用性,FLush列表用来管理将页刷新回磁盘,二者互不影响。

因为脏页同样存在于LRU列表中,故用户可以通过源数据表  innodb_buffer_page_lru 来查,需要加入 oldest_modification 大于 0 的查询条件:

select * from innodb_buffer_page_lru where oldest_modification > 0


通过  show engine innodb status 可以查看缓冲池中这些列表的信息,但不是实时的。


(3)重做日志缓冲


InnoDB存储引擎内存区域除了有缓冲池外,还有重做日志缓冲———— redo log buffer.

InnoDB存储引擎首先把重做日志信息放入这个缓冲区,然后按一定频率将其刷新到重做日志文件。

一般情况下每一秒钟会将重做日志缓冲刷新到日志文件,因此用户只需要保证每秒产生的事务量在这个缓冲大小之内即可。

show variables like 'innodb_log_buffer_size' \G          默认为8M


将重做日志缓冲刷新到外部磁盘的重做日志文件中:

(1) Master Thread 每一秒将重做日志缓冲刷新到重做日志文件;

(2)每个事务提交时会将重做日志缓冲刷新到重做日志文件;

(3)当重做日志缓冲池剩余空间小于1/2 时。


(4)额外的内存池

在InnoDB存储引擎中,对内存的管理是通过一种称为内存堆(heap)的方式进行的。

在对一些数据结构本身进行分配时,需要从额外的内存池中进行申请。当该区域的内存不够时,会从缓冲池中进行申请。

例如:

分配了缓冲池(innodb_buffer_pool),但是每个缓冲池中的帧缓冲(frame buffer)还有对应的缓冲控制对象(buffer control block),这些对象

(buffer control block),这些对象记录了一些诸如LRU、锁、等待等信息,而这个对象的内存需要从额外内存池中申请。


因此,在申请了很大的InnoDB缓冲池时,也应考虑相应地增加这个值。


3、Checkpoint 技术


缓冲池的设计目的是为了协调CPU速度与磁盘速度的鸿沟,为了避免在缓冲池将新版本数据刷新到磁盘时发生宕机,从而使数据无法恢复,

当前事务数据系统普遍采用 Write Ahead Log 策略,即当事务提交时,先写重做日志,再修改页。

这样就可以通过重做日志来完成数据的恢复,达到事务ACID中D(Durability 持久性)的要求、


如此来说要恢复数据需要两个前提条件:

1、缓冲池可以缓存数据库中所有的数据;

2、重做日志可以无限增大。


即便是上述两个条件都满足,那么还有一个情况:宕机后数据库的恢复时间。 (如果是运行了几个月甚至几年的数据库)


因此Checkpoint(检查点)技术的目的是解决以下几个问题:

1、缩短数据库的恢复时间;

2、缓冲池不够用时,将脏页刷新到磁盘;

3、重做日志不可用时,刷新脏页。


因此,当数据库发生宕机,需要恢复数据时,只需要对Checkpoint后的重做日志进行恢复,因为Checkpoint之前的页都已经刷新回磁盘。


此外,当缓冲池不够用时,根据LRU算法会溢出最近最少使用的页,若此页为脏页,那么需要强制执行Checkpoint,将脏页也就是页的最新版本刷回磁盘。


重做日志出现不可用的情况:

1、当前事务数据库系统对重做日志的设计都是循环使用的;

2、重做日志可以被重用的部分是指数据库恢复操作不需要这部分的重做日志,因此这部分就可以被覆盖重用;

3、若此时重做日志还需要使用,那么必须强制产生Checkpoint,将缓冲池中的页至少刷新到当前重做日志的位置。


对于InnoDB存储引擎,通过LSN(Log Sequence Number) 来标记版本。

LSN是8字节的数字,其单位是字节,每个页有LSN,重做日志也有LSN,Checkpoint也有LSN。

可以通过 show engine innodb status ;  查看


Checkpoint发生的时间、条件及脏页的选择等都非常复杂,无外乎是将缓冲池中的脏页刷回到磁盘,不同之处在于每次刷新多少页到磁盘,

每次从哪里取脏页,以及什么时间触发Checkpoint。














                                 

你可能感兴趣的:(mysql数据库)