mysql原理分析

1.mysql数据结构

mysql使用的是B+树的数据结构,我们接来下首先说一下为什么使用B+树而不用B树或者红黑树。

我们都知道红黑树是一个二叉树,当数据量大起来时,这个树就会变得很高,节点很多,所以他的IO次数也会相应的变多,还有就是树的每个节点,存放的数据很少,通过计算本来树的每一层大概需要分配16KB的数据。而红黑树所存的数据远远小于16KB,造成空间的浪费

所以我们想要优化就要从两个方面进行,一方面是需要将树的高度降低,也就是说将每一层的节点变多,一方面尽可能的利用16KB的大小。

这时我们就想到了B树。

 上边就是一个我们以三阶的B树为例,三阶的B树,每一个节点最多存储两个数据,一但达到3个树的结构就会发生变化,此时我们很好奇数据是怎么储存的呢

它实际上就是这样的一个格式,其中p1,p2,p3就是它指针,子级的地址,10和17就相当于是mysql的索引,而data则是除了索引外的数据。

我们通过这个储存的方式我们再看一下B树是怎么查询的呢

我们以取到12为例,

1.把根节点 1 加载到内存(IO操作)

2.比较12在10-17中那个位置,获得P2 (内存操作)

3.通过P2的指针,把3加载到内存 (IO操作)

4.比较12在11-15中那个位置,获得P2 (内存操作)

5.通过P2的指针,把8加载到内存(IO操作)

6.在关键字列表中找到12 (内存操作)

通过这6步,我们可以看出来相较于红黑树,节点减少了就减少了IO的次数,提升了速度。

但是树结构的每一层是有大小的是有限的,如果data中的数据过大就会导致每一层能存储的节点变少,从而导致树又变得很高。

问题又回到了远点,对于B树,而言一定data中的数据变大就会导致树的高度变高,增加了IO的次数。mysql对于提出的解决办法就是使用B+树。

B+ 树

B+树和B树很像,不同的是B+树的非叶子节点只存储key值,不再存储data,所有的data都存储在叶子节点,而且叶子节点之间有一个双向的链表指针且最后一个叶子节点和第一个叶子节点之间也有一个链表指针。

这个B+树上一般有两个指针,一个是指向根节点,一个是指向最小的叶子结点,通过这样的方式,分页和范围查询的时候可以很轻松的完成,而通过索引查询时因为层数很少也可以极快的完成查找。

mysql的ACID

A(原子性):事务中的一组操作要么全部成功,要么全部失败,一但中间发生错误就会回滚到执行之前的情况,就像没有发生这些操作一样。

C(一致性):一致性就是数据库的完整性,比如在转账过程中A向B转账,A扣钱了,但是B没有加钱,这时数据库就是不完整的,实际上一致性也可以说是包含在原子性中。

I (隔离性):隔离性就是在同一时间只能有一个事物操作同一数据,不同事物之间不会相互影响。就是A正在取钱,那么B就不能同时向A转钱,

D(持久性):当一组事务完成后就会存储到磁盘,就算数据库宕机数据也不会消失。

Mysql事务的隔离级别

读取未提交:就是一个事务可以读取在另一个事务中的修改,即使是这个事务没执行完成,假如A,B两个事务同时执行,A修改了一条数据,B中读取了A的修改并基于A的修改进行了计算,但是A的事务又执行失败了,A回滚了,那么此时B中的计算结果就是错误的。

读取已提交:只能读取已经提交的事务的结果。假如A事务中有多次相同读取的操作,但是再读取的过程中事务B插入了一条数据,那么A的多次读取操作结果就会不同。

可重复读:可重复读的意思就是在一个事务中对同样数据的多次读取的结果是相同的,可重复读的问题在于可能会出现幻读,也就是说在AB事务,在事务内都新增了一条数据,此时两条新增的数据他们的主键是相同的,B事务先结束,数据落表之后A事务在落表的时候就会发现已经有这条数据了,就像出现了幻觉一样,这就是幻读问题。

串行化:串行化就是一个一个的执行,只有前边一个执行完成后,下一个才会执行,不会出现上边的三个问题,但是执行的效率就会大大的降低。

mysql 的默认的隔离级别就是可重复读,我们这里就着重说一个可重复读会造成的问题:幻读

幻读问题实际上mysql已经帮助我们解决了

mysql使用的是间隙锁和行锁组合的方式。当insert发生时,只会使用行锁锁住当前行,这样其他事务就不能再新增相同主键的记录,但是发生范围修改时,如果其他事务在此范围内新增了一条,那种新增的这条记录也会被修改,这就会有问题,所以就出现了间隙锁,间隙锁会锁住这个范围内的间隙,所有的写操作都会被阻塞在这,这样就保证了不会传幻读的问题。

MVCC

MVCC:多版本并发控制,实际上就是多版本数据实现并发访问的技术。

MVCC的作用是使不同的事务看到他们应该看到的数据版本,比如一条数据正在被修改,那么其他的事务不必等到他完成,而是看到他的历史版本即可,而且根据隔离性的不同,他的历史版本的粒度也是不同的,可重复读的粒度就是每一次事务的开始时的数据,已提交就是每一次select的时候。

它的实现方式是通过Undo log 和 readView ,readView是用来控制读取那个undo log中的历史版本。

Innodb

Innodb主要是分为三大块,缓冲池,重做缓冲池,额外内存池

缓冲池

内存和磁盘之间读写速度差距过大,为了提高速度,innodb引入了缓冲池。当有查询进来的时候,会现在缓存中查找,如果找到了就会直接返回,如果没找到,才会在到磁盘中查找,并将查到的数据放到缓存中。

在mysql第一次启动的时候会申请一段连续的Buffer  pool,默认大小是128M,然后创建一个free 链表,buffer pool中的数据也是按照数据页的形式进行储存的,大小也是16k,在最开始buffer pool的数据也都是空闲的,为了管理这些空闲页,buffer pool会为这些空闲页矿建一些描述信息(包括该页所属的表空间编号,页号,缓存页在buffer pool中的地址,链表的节点信息等等),之后会将这些描述信息全部都加载到free链表中。这里说一下为什么要有free链表,因为buffer pool中的数据页大小和mysql磁盘上的数据页大小相同,所以每一次从磁盘放入缓存只需要把磁盘的数据页付给缓存中的数据页即可,这时就会有一个问题,磁盘怎么知道那些数据页是空闲的,这时就出现了一个free链表来统一管理这个空闲页,当从磁盘查出数据后,只需要到free链表中拿到空闲数据页即可。

查询的流程:首先会再缓存中查找是否存在,如果不存在则到磁盘查询,查询到数据后,会在free链表中找一个空闲页,并把数据页赋空闲页,然后写入描述信息,再把描述信息从free中移除。

这时又有个问题,我们怎么知道缓存中是否有我们需要的数据页?

Innodb的解决办法是创建一个数据页缓存hash表,

他的key就是表空间号+数据页号,value就是数据页。每一个数据页写入缓存后就会存入hash表中,下次要使用的时候直接在hash表中查询即可。

现在又有了新的问题,假如我们这个mysql运行一段时间后,free中的空闲页用完了,这时innodb是怎么处理的呢?

innodb采用的是LRU算法(最近最少使用),也就是innodb维护了一个LRU链表,将这个链表分为热数据区和冷数据区

 通过上边的两个图我们可以看到LRU链表的操作流程,但是LRU的问题也是非常的明显,第一如果进行了全表扫描就会缓存大换血,第二点因为是按照页的方式进行读取,所以即使没有用到的数据也会被掉到最前方,第三就是每一次发生查询都会把数据页移动到热数据区的头部,这样其实就影响到了其他访问的性能。

为了解决这几个问题,innodb对LRU链表进行了优化,在查询首次进入的时候并不会直接将数据移动到热数据区的头部而是放到冷数据区的头部,如果再1s之类再次访问,就会把他放到热数据区的头部,这样的方式就可以保证不影响热数据区的性能,解决了预读的问题。然后innodb规定只有热数据区1/4之后的区域发生查询才会移动到头部,这样就解决了每一次都需要移动的问题,保证热数据区的头部大部分的时候都是热数据。

写操作

当发生写操作时,innodb也是先修改缓冲池中的内容,因为如果一次写操作就写一次磁盘的话就性能方面速度还是过慢了,所以innodb的做法就是先写入缓存当中,等到积累到一定程度后再一次性写入磁盘,这样就大大的节省了IO交互。

但是如果再写入时mysql突然宕机了我们该怎么办呢?

我们知道如果数据页还在缓存中,mysql宕机我们可以使用redo Log 恢复,但是在写入的过程中发生了宕机,属于部分丢失的情况,此时数据页的完成性就被破坏了,使用redo Log是没有办法恢复的。

为了解决这一问题,innodb使用了一个两次写的方式,innodb为了完成两次写创建了一个double  write 的空间,默认大小是2M。他的完整流程就是首先写请求进入的时候会先直接修改buffer pool中的缓存页,并在redo Log中记录,等到被修改的页到达一定程度后,会启动一个用户线程,double write为128个连续的页,前边的120页用来写脏,而后边八个则是为了把脏页放入之前的120个页中,这样做的原因是为了,做到异步,当我们把页放入后8个以后,我们就不需要在关注double write了,因为刷脏是用户线程发起的,如果长时间停留会影响到用户的使用。

当double write  buffer 中的数据存满的时候,就会触发批量刷脏,首先会将doule weite中的数据分两次每次1M的方式写到本地的共享表空间,然后再使用fsync函数将double write buffer中的数据存入本地磁盘。一但在写入本地磁盘宕机时,直接将共享表空间中的数据页覆盖本地磁盘的数据即可恢复数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值