MySQL之Innodb

InnoDB体系架构:

在这里插入图片描述
InnoDB存储引擎有多个内存块,这些内存块组成了一个大的内存池,负责
1.维护所有的进程、线程访问的多个内部数据结构
2.缓存磁盘上的数据,方便快速的读取,同时在对磁盘文件的数据修改之前在此处缓存
3.重做日志缓存

后台线程

后台线程的主要作用是负责刷新缓存池中的数据,保证缓存池中的内存缓存是最近的数据,同时将脏页刷回磁盘。

1.Master Thread
Master Thread顾名思义是非常核心的线程,主要负责将缓存池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插入缓冲、UNDO页的回收等等。

Master具有最高线程优先级,由主循环、后台循环、循环刷新、暂停循环组成,MasterThread会根据数据库状态在4者之间切换。

Loop循环(主循环):分为每秒操作和每10秒操作
每秒操作:
日志缓冲刷新到磁盘,即使事物还未提交。(因此事物的重做日志一直回写到磁盘,所以再大事务的commit也很短)
合并插入缓存(如果每秒IO次数小于5次 则执行)
至多刷新200个脏页回磁盘(当脏页数量超过75%才执行)
每10秒操作:
刷新脏页回磁盘
至多合并5个插入缓存
日志缓冲到磁盘
删除无用的undo页

backing loop(后台循环)
合并插入缓冲
删除无用的undo页

flush loop(刷新循环):

2.IO Thread
在MySQL中采用大量的AIO来处理写请求,这样可以极大的提高数据库的性能,而IO thread则负责这些IO请求的回调处理。默认存在4个wirte、4个read、1个insert buffer IO、1个log IO thread。

3.Purge Thread
事务提交后,其所使用的undolog可能不再需要,需要Purge Thread来回收已经使用的undo页。以前此功能是在Master Thread中执行,后来独立出来,减轻Master Thread的负担。

4.Page Cleaner Thread
将以前Master Thread中的脏页的刷新操作放在单独的线程中来执行,减轻Master Thread的工作对用户查询线程的阻塞。

内存

InnoDB存储引擎的数据是在磁盘上的,并且将其中的记录按照页的方式进行管理。但是CPU和磁盘的速度差别太大,因此使用缓冲池技术来提高数据库整体性能。对数据库做操作,首先要在内存中查看该页是否在缓冲池中,如果在则直接操作,如果不再则需要先将页读到缓冲池中再进行操作。同时,缓冲池中的脏页不是即时的刷回磁盘,为了提高数据库的整体性能采用checkpoint机制刷回。缓冲池的大小将直接影响数据库的性能,64位操作系统最多支持512G内存,而32位最多64G.

在这里插入图片描述
整个内存分为缓冲池、重做日志缓冲、额外内存池。其中缓冲池实例可以允许多个,每个页根据哈希值平均分配到不同的缓冲池实例中,这样可以减少数据库内部资源竞争,增加数据库的并发能力。默认缓冲池为1个。

缓冲池管理
缓冲池中由LRU LIST、 FREELIST 、FLUSH LIST、Unzip List

FREE列:表管理可用空闲页
LRU列表:用来管理已经读到的页
FLUSH:列表管理修改过的页
unziolist管理压缩页:Innodb中页的大小默认为16KB,但是可以将页压缩为1、2、4、8KB。

数据库刚开始启东时,lru和flush都是空的,free是满的。需要读取的时候,lru向free申请可用的空闲页,并将它加入lru中。如果一个页上发生了数据的修改,那么这个页就加入到flush列表中。一个页可能同时存在于LRU和FLUSH中,LRU列表用来管理页的可用性,而FLUSH列表来管理页刷回磁盘,二者互不影响。

缓冲池通过LRU算法来管理,和传统的LRU算法不同,数据库中的LRU加入了midpoint(默认为5/8)。新读到的页,并不是直接放在LRU头部,而是先放在midpoint。把midpoint之前的称为new列表,midpoint之后的称为old列表,数据只有在old区中呆够一定的时间,才会被放入到new区中。new中是最为活跃的数据,可以理解为一个传统的LRU,而old更像是一个过滤区。这样做的原因是为了防止一些大量不那么必要的数据加入到LRU中,使得真正的热点数据被刷出,影响效率。

重做日志缓冲
Innodb先将从做日志信息放在缓冲区中,然后按照一定的频率将其刷回到重做日志文件。一般每秒刷新一次,所以不需要很大,默认8M。三种情况下:
1.Master Thread每一秒将重做日志缓冲刷回重做日志文件
2.每个事物提交的时候都会刷回
3.当重做日志缓冲小于50%时,刷回

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

CheckPoint

当前数据库普遍采用WAL(write head log):当事物提交时,先写重做日志,再修改页。即当一个缓冲池中的页被修改,变为脏页时先将何处改变写入到重做日志中,等到时间充足或者其他情况下再将脏页刷新回磁盘。若脏页还没有刷新回磁盘,数据库宕机,那么根据重做日志也可以恢复数据库的状态。

checkpoint解决如下问题:
缩短数据库恢复时间
缓冲池不够时将脏页刷新到磁盘
重做日志不可用时脏页刷新到磁盘

以下四种情况会出发CheckPoint:
1.Matser Thred CheckPoint:以每秒或者每10秒的速度刷新一定比例的页回磁盘
2.Flush_lru_list CheckPoint:要保证LRU列表差不多有100多个页可以使用,若从LRU尾端移除的页有脏页则需要刷回
3.重做日志不可用的情况下需要刷回
3.脏页数量如果超过了75%,则需要刷回

关键特性

InnoDB存储引擎的关键特性包括:插入缓冲、两次写、自适应哈希索引、异步IO、刷新邻接表

插入缓冲

插入缓冲并不只是一个缓冲,由InsertBuffer、PurgeBuffer、DeleteBuffer、、

Innodb中,主键是行唯一的标识符。当按照主键递增的顺序来插入数据的时候,不需要磁盘的随机读写,写入都是在一块地,所以速度很快。但是当采用非聚集索引,此时会需要离散的读取,磁盘的速度很慢。因此,对于非聚集索引采用了插入缓冲。在插入或者更新的操作时,不是每次都直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,如果在就直接插入,如果不在就先存放在一个InsertBuffer中。以一定的频率将InsertBuffer中的数据和辅助索引叶子节点进行合并,真正的刷到磁盘上。

InsertBuffer必须满足:
1.索引是辅助索引。
2.索引不是唯一的。因为如果是唯一索引,在插入操作要去索引页查找来判断是否是唯一索引,查找时就会出现离散读取,失去了插入缓冲的意义。

PurgeBuffer/DeleteBuffer等是InserBUffer的升级版。InsertBuffer只负责插入和更新,后面又加入了删除。

对一条记录删除分为两步:
1.将记录标记为删除。(DeleteBuffer执行)
2.将记录真正的删除。(PurgeBUffer 执行)
分为两部是因为虽然现在命令删除,但是可能以前还存在一个事物正在持有这行,并不能马上删除,等到所有需要的事物都完成了,这个数据删除不会造成影响,才是真正的删除。

InsertBuffer的数据结构是B+数,在共享表空间中有全局唯一的InsertBufferB+数,负责对所有表的辅助索引进行插入缓冲。同时还有InsertBuffer Bitmap来跟踪16384个辅助索引页,记录辅助索引页的可用空间。当读取一个页到缓冲池时,会去InsertBuffer Bitmap查看该页是否有记录在插入缓冲中,如果有则将该页记录刷新到辅助索引页上。同时当某一页的可用空间小于1/32时,会强制合并。
1.辅助索引页被读取到缓冲池
2.InsertBuffer Bitmap记录到该页无空间可用
3.Master Thread

两次写

如果Innodb在写入某个页到表中,但是写到中间突然宕机了,这种情况被称为部分写失效。这种情况下redo log是无法进行重做的。因为redo log重做之前,需要一个页到副本,当写入失效发生时,先通过页到副本还原页,再进行重做。

doublewrite由两部分组成,内存中的doublewrite buffer 和磁盘中的共享表空间中的连续128个页,二者的大小都是2M。对缓冲池的脏也进行刷新时,并不直接写磁盘,而是先将脏页复制到内存中的doublewrite buffer,doublewrite buffer再分两次,每次1M写入到磁盘中的共享表空间。然后再将doublewrite buffer中的数据刷新到真正的数据文件中。第一次向磁盘的共享表空间因为空间都是连续的,所以速度很快,多写一次并不会花费太多的时间。第二次就是真正的将脏也刷新到数据文件中,此时大概率是离散的。

当数据库发生上述宕机情况是,先从共享表空间中找到该页到副本,将其复制到表空间文件,再使用redo log重做。

写入重做日志文件的操作不是直接写,而是先写到重做日志缓冲,然后从缓冲中向磁盘写入,写入是按照扇区最小的单位512个字节,所以写入操作一定是完成的,因此重做日志不需要两次写。

自适应哈希AHI

Innodb的索引是B+索引,不支持O(1)查找。但是哈希索引是非常快的,支持O(1)查找。如果存储引擎观察到建立哈希索引可以带来性能的提升,那么他就会建立哈希索引,称为自适应哈希索引。innodb会自动的为热点数页建立哈希索引,这是通过缓冲池中的B+索引,并不是对整个表建立索引。AHI要求连续访问的模式必须是一样的,以该模式超过100次或者页通过该模式访问了超过总数的1/16.

异步IO

用户发送SQL后不需要等待结果,数据库会自行回掉(IO thread)。此外,采用AIO还可以进行IOmerge,将多个IO合并为一个。

刷新领接表
当一个脏页刷新时,innodb会检测该页所在区所有的页,如果是脏页那么一起刷新。

InnoDB中的表

索引组织表

在innodb中,表都是按照主键顺序组织存放的,称为索引组织表。在每张表中都有一个主键,如果创建表示没有显示的指定主键,那么innodb会判断表中是否有唯一索引,有就将此唯一索引作为主键,没有就自动创建6字节大小的指针作为主键。

innodb的所有数据都逻辑的被存放在表空间中,表空间由段、区、页组成。如果自己创建idb文件,那么只会存放每张表的数据、索引和插入缓冲bitmap,而其他类型数据如回滚信息、插入缓冲页、二次写缓冲等还是在原先的共享表空间。表由各个段组成,常见的段有数据段、索引段、回滚段。一个区默认大小为1M,可以存放16个页,每个页默认大小16KB。

在这里插入图片描述
innodb可以将某些数据存储在真正数据页之外,一般认为BOLO,LOB这种大对象列类型的存储会放在页面之外。VARCHAR也有可能是行溢出数据。

char和varchar
char存储固定长度的字符 char(n)表示存储n个字符 不足则补齐
varchar存储不固定长度的字节 varchar(n)表示最多n个字节 不补齐 n最大为65532 因为需要3个字节来保存长度

约束
innodb提供了以下约束:
主键约束 Primary Key
唯一约束 Unique Key
外键约束 Foreign Key
非空约束 NOT NULL

视图

视图是一个命名的虚表,默认只存在内存中。视图可以灵活的组织表,方便系统操作,但是对于表的更新还是依靠真正的表来实现,视图只是一个底层表和上层应用的抽象,方便操作。MYSQL本身不支持物化视图。

分区

分区并不是在存储引擎完成,而是在服务层。因此很多的存储引擎都支持分区。分区分为水平根据和垂直分区,MYSQL仅仅支持水平分区,不支持垂直分区。且MYSQL的分布式局部分区索引,不支持全局分区。Mysql支持的分区方法:
1.range分区:行数据基于一定连续区间的列值被放入分区
2.list分区:list分区面向离散的值 而range是连续的, list划分不同的值到不同的区中
3.hash分区:根据哈希函数来分区
4.key分区:根据用户自定义的表达式的值来分区,值不能为负数
5.colums分区:1和2是分局整形数据来划分的,colums根据非整形数据划分

分区对于OLAP很有帮助,因为OLAP往往需要扫描全表来处理,分区可以加快这一速度。但是对于OLTP,每次读取的数据并不是很大,因此分区不一定有性能的提升,甚至可能下降。

索引

Innodb支持B+树索引、全文索引和哈希索引。innodb支持自适应的哈希索引,会自行判断是否需要建立哈希索引。B+树索引是最常见的索引,根据键值对来找到数据,但是B+树索引并不能找到给定键的具体行。而只能找到数据行所在的页,先把页读到内存中,在进行查找。MYSQL支持索提示,显示的告诉优化器采用那个索引。

B+树
B+树是为磁盘或者其他直接存储辅助设备设计的平衡查找树,所有的记录节点都是按照键值的大小顺序的存放在同一层的叶子节点中,所有的叶子节点组成一个链表,所以B+树支持范围操作。B+树索引可以分为聚集所以和非聚集索引。

聚集索引

聚集索引按照每张表中的主键构造一棵B+树,同时叶子节点中存放的极为整张表的行记录数据,叶子节点也称为数据页。聚集索引这个特性决定索引组织表的数据也是索引的一部分,每个叶子节点通过双向链表来连接。由于实际的数据页只能由一颗B+树来链接,因此只能由一个聚集索引。非叶子节点仅保存键值及指向数据页的偏移量,而叶子节点保存了数据行的完整信息。聚集索引对主键查询和范围查找非常的快

聚集索引的存储鼻并不是物理上连续的,而是逻辑上连续的。叶子节点通过双端链表链接,叶子节点按照主键的顺序排序,每个页中的数据也是按照双端链表进行链接。

非聚集索引

辅助索引,也叫非聚集索引,叶子节点并不包含行记录的全部数据。叶子节点除了包含键值之外,还包含了主键值用来表式去哪里可以找到相应的行数据。每张表可以有多个聚集索引,当通过辅助索引来寻找数据时,先通过辅助索引找到主键,再拿这个主键区主键索引找到数据,这个操纵称为回表。因此主键的值不能设置的太长太大,这样辅助索引也会变大。

如果辅助索引中除了主键值,还存储了其他的行的数据,假设所需要的数据辅助索引叶子节点正好保存了,就不在需要拿主键去查找聚集索引,省略了回表的操作,这种索引叫做覆盖索引。覆盖索引因为不包含所有的数据,所以大小要远远小于聚集索引,根据B+树特性可以减少IO操作。

联合索引是对表上的多个列进行索引

索引管理
索引的创建可以通过alter table 或者 create drop index。

MRR优化:
对于范围查询和JION查询,MRR先将查询到的辅助索引值(即主键值)存放再一个缓存中,根据主键的大小进行排序,根据排序后的结果来顺序的访问实际的文件。

通过这样可以减少对键值的查询操作,减少缓冲池中页被替换的次数。因为主键值是顺序的,可能上一次提取出的页会包含很多所需要的行,这样提取一次页就可以。如果随机读取,可能刚刚提取一个页,一会就被缓冲替换掉了,下一次需要的时候还需要重新读取页。

IPC优化:
IPC在进行索引查询时,在取出索引数据的同时还会判断时候可以进行过滤操作。将过滤操作放在了存储引擎层,而不是服务层。

全文检索

全文检索将存储与数据库的任意内容查找出来的技术,它根据数据中的词、段、句等信息进行统计分析来实现。全文检索通常通过倒排索引来实现,倒排索引与B+树一样,也是一种索引结构。倒排索引通过在辅助表中存储单词和单词出现的位置映射来实现。

innodb在数据库的多个地方使用到锁,比如表、缓冲池的LRU等等。innodb支持在行级别上对表加锁,而MYISAM支持表锁。在数据库lock和latch都可以标识锁,但是一个针对事务、一个针对线程。

在这里插入图片描述
innodb支持共享锁和排他锁。同时支持多粒度锁定,如同时表锁和行锁,但是再对细粒度上锁之前,要先对粗粒度上锁。
在这里插入图片描述
一致性非锁定读:
如果事务正在执行deltet或则update操作,这时读取操纵并不会等待锁的释放,而是先去读取一个锁的快照数据。快照数据是该行的之前的版本,此操作通过Undo日志完成。undo用来回滚数据,快照读不需要上锁,因为没有事务要对历史数据进行修改。但是不同的事务隔离级别下,读取的方式不同,并不是所有隔离级别都采用一致性非锁定读。此外,如果隔离级别是读已提交,快照数据是最新的一分,而对于可重复读隔离级别,快照读会读取事务开始之前的数据。

一致性锁定读是在数据行加锁,等待释放完成才可以读取。自增长采用了一致性锁定读。

锁的算法
Record lock:单个行记录上锁。为索引记录上锁
Gap lock:间隙锁 锁定一个范围 但是不包括记录本身
Next-Key lock:锁定一个范围 并且锁定记录本身。对行进行查询时使用此种,为了解决幻读问题。可重复读下默认采用此种

当查询的索引包含唯一属性,next-key lock会降级为record lock,提高系统并发性。
间隙锁时为了组织多个事务将记录插入到同一个范围内,解决幻读问题。
解决死锁问题innodb们没有采用超时等待,而是采用等待图方式。
锁升级是指将当前锁的颗粒度增大,把行锁升级为页锁,页锁升级为表锁。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值