Mysql

1.Page--页

 

       为了避免一条一条读取磁盘数据,InnoDB采取了Page(页)的方式作为磁盘和内存之间交互的基本单位

        一页的大小一般是16KB

        那么我们来看看Page的结构

         以上就是Page的基本结构,可以看出我们目前最关心的User Record(我们insert的记录)在page中是什么方式存储的       

        首先我们会申请一个Page的空间内存,那么我们User Record肯定为空,然后所有的都是Free Space,以下就是默认申请的一个Page空间

         比如如果我们Page刚好塞满了记录,那么我们Free Space自然就没有,所有的都在User Record中存储,见下图

       从以上可以总结出,接下来我们就引出了User Record中记录的概念,我们再来看看Record又有哪些结构呢

2.Record

 以上就是Record中基本结构,那我们演示下insert记录之后,UserRecord是怎么记录的呢

        

 以上就是insert记录的时候,page页存储记录的方式(默认主键正序排序方式),那么如果删除其中一条记录会有什么变化呢

直接将需要删除的记录delete_flag置位1,next_record置位下一位record,同时将最大的n_owner-1

以上来看,我们往Page(16KB)里面插入record的时候,可能插入的record已经上万,而且记录都是单向指针的方式,那么我们如果查询的记录刚好就是最后一条,那我们是不是需要从第一条记录遍历查询到最后一条再返回?基于这个问题,Page Directory就出现了

3.Page Directory

主要是为了帮助我们查询的时候能够迅速定位到记录的位置

 基于Page Directory之后,记录存储的数据格式,一般每组的记录1-8条,那么我们再来模拟分组2已经满了的情况

 现在看来我们已经有个对个slot,这样查找的时候,我们就可以通过page_directory二分查找定位slot,再进入没个分组中,循环遍历查找效率就快多了(每组中的记录一般1-8条)

InnoDB基于Page查找记录的方式:

  1. 通过二分法确定槽
  2. 通过记录的next_record属性遍历该槽所在的组中的各个记录

那么接下来又引出新的问题,我们Page(16KB)大小,总有存满的时候,这个时候就会申请新的Page,那么我们现在就有了多个Page,那么我们又要怎么来定位到具体的记录呢,接下来引出Index索引的概念来解决以上问题

4.Index

以上总结到我们的记录都是存储在Page上且记录都是min_rec_flag叶子节点,那我们是不是就可以使用非叶子结点来存储索引来指向具体的Page(叶子节点)

 以上正常来讲就是多个Page存储记录,看看InnoDB有哪些方式来解决呢

4.1主键索引

主要是建立非叶子节点记录(索引)来指向page号,同时非叶子节点的记录不需要存储太多的数据(page中最小的主键和page号),所以来看基于主键查询,我们已经可以快递定位到记录的位置

4.2二级索引 -- 非主键

 以上我们给another_id创建了索引(默认正序排序),那么我们基于another_id查询就可以快递定位到记录中的主键,同时我们就可以再通过主键索引可以快速定位到我们所需要的记录

4.3联合索引

 通过another_id和name建立联合索引,这种情况InnoDB会先左原则排序,通过another_id和name来定位到唯一的主键记录

如果我们二级索引的值不唯一的话,那么innoDB就无法定位唯一的主键记录,那么我们就需要保证记录的唯一性

 我们以上就是通过索引值+主键值+page号来定位记录的唯一性

5.Buffer Pool(缓冲池)

        目的:  由于数据页存储在表空间(磁盘文件),我们每次操作都需要涉及到一次IO操作,为了减少IO操作,我们会将数据页存储到Buffer Pool(缓冲池)中

        基本结构:

 Mysql启动的时候,自动为Buffer Pool申请一片连续的内存空间(链表结构),默认128Mb

磁盘数据加载到Buffer Pool流程:

        1.从free链表中获取一个空的控制块

        2.通过控制块,找到对应的缓冲页,把对应的数据信息加载到缓冲页

        3.把控制块从free链表中移除,并且count-1

5.1free链表

        以上总结到磁盘数据加载到Buffer Pool需要通过free链表,那么free链表

        目标: 存储Buffer Pool中所有的空的控制块以及数量
        

基本结构: 

问题:

那么我们知道Buffer Pool中存储的都是缓存数据,如果我们对数据页进行修改操作,是不是导致Buffer Pool和磁盘数据不一致,那么接下来flush链表登场了

5.2flush链表

        目的: 存储Buffer Pool中缓冲页修改后对应的控制块

        基本结构:

 每次修改缓冲页数据的时候,Mysql会将对应的代码块加载到flush链表中,并且count+1

问题:

那么我们都知道Buffer Pool只要是为了避免IO操作,从而在缓冲中加载数据来提升效率,那么我们缓冲只有128Mb,如果每次查询的数据在缓冲中都不存在,这个时候Buffer Pool岂不是没有任何意义,所以我们Buffer Pool需要存储一些常用的数据页,这个就可以尽量避免这个问题了,这个时候LRU出现了

5.3BRU链表

        目的: 存储最近最少被使用的缓冲页对应的代码块,如果一条数据很少被访问,那么我们就可以将该数据刷新到磁盘中

        基本结构:

 young区域: 最近访问的数据页

 old区域: 最近很少被访问的代码块

问题:

        1.当你顺序的访问了一个区中大于 innndb_read_ahead_threshold=56个数据页时,Mysql会自动的把下一个区所有的数据页加载到LRU链表

        2.当Buffer Pool中存储着一个区中13个连续的数据页时,你再去这个区里面读取,MySQL就会将这个区里面所有的数据页都加载进Buffer Pool中的LRU链表

        3.全表扫描,如果数据很多的话,那么这些数据页会全部加载到LRU链表,并且将常用的数据全部挤出去,这样LRU链表可能就不是常用的数据了

            1.2问题解决:

          预读主要涉及问题就是,每次加载的时候都会加载一堆我们不需要的数据,那么Mysql会自动将这些数据加载到Old区域(冷数据区),对于整个young区域(热数据区)是没有影响的 ==  分区方式解决

             3.问题解决

        如果全表扫描数据的话,将一大批数据加载到old区域时,然后在不到1s内你又访问了它,那在这段时间内被访问的缓存页并不会被提升为热数据。 这个1s由参数innodb_old_blocks_time控制。

Buffer Pool刷新方式

        1.从flush链表(Buffer Pool中被修改的数据页)刷新一部分数据到磁盘(定时)

        2.从BRU链表的Old区域(冷数据区)中刷新一部分数据到磁盘(定时)

刷新完之后,我们Buffer Pool缓存的基本都是最新且常用的数据页

  6.undo log

        目的: 为了保证事务的原子性,但是有些情况会造成异常事务回滚,所以Mysql为了解决事务一致性问题而记录的日志,称之为undo log

        6.1事务id(数据页中record的trx_id)

                事务id的分配

                        1.只读的事务(不可见)

(临时表  ==> Mysq在处理事务的时候,需要建立临时表来处理,最后再将临时表删除)

                        2.读写的事务

                        第一次对表记录记录增删改之后,才会分配事务id,事务id默认0

                事务的开启

                        1.只读事务

                       start transaction read

                        2.读写事务

                        start transaction write read = begin

6.2 insert操作对应的undo日志

        

end of record: 本条undo log页面结束地址(下一条开始地址)

end of record: 本条undo log页面开始地址

undo type: 日志类型

        TRX_UNDO_INSERT_REC   新增操作Undo 日志类型

        TRX_UNDO_DEL_MARK_REC  删除操作Undo 日志类型

        TRX_UNDO_UPD_EXIST_REC  更新操作Undo 日志类型

undo no: 日志编号(即表的主键)

table id: 表id(数据库表的唯一标识)

由于insert操作如果需要回滚的操作的话,就是把insert的记录根据主键删除即可,所以insert操作的时候,undo日志不需要存储太多的数据

6.3Delete操作对应的Undo日志

trx_id: 之前删除记录的事务id

roll_pointer: 指向之前记录的undo_log

6.4Update操作对应的Undo日志

 n_updated: 被更新的列的数量

以上是主键未更新的情况

     主键更新:

        1.通过delete mark删除之前的数据 

        2.根据更新后的记录,插入到主键索引中

       

7.事务

7.1ACID

        Atomicity原子性: 某个操作,要么全执行,要么全回滚

        Consistency一致性: 数据库数据符合一致性
        Isolation隔离性: 多个事务访问相同数据,执行顺序有一定的规律,彼此不干涉

        Durability: 对数据所做的修改都应该保存到磁盘

事务状态:

脏读: 读取到另一个未提交的事务修改后的数据

不可重复读: 修改了另一个未提交事务读取的数据

幻读: 如果一个事务根据某些搜索条件查询了一些记录,但该事务未提交,另一个事务写入了符合搜索条件的数据,此时就发生了幻读

隔离级别:

8.MVCC(Muti-Version Concurrency Control)

        多版本并发控制.利用记录的版本链和Read View,来控制并发事务访问相同记录时的行为

 8.1版本链

        在每次更新记录的时候,会将旧值放到undo log中,随着更新日志的增多,所有的版本都会被roll_pointer属性连接成一个链表,随之称为版本链

       流程图:

 

8.2 Read View

        定义:  一致性视图,用来判断版本链中哪个版本是当前事务可见的

        m_ids: 在生成Read View的时候,当前系统中活跃的读写事务的事务id列表

        min_trx_id: 在生成Read View的时候,当前系统中活跃的读写事务中最小的事务id,也就是m_ids的最小值

        max_trx_id: 在生成Read View的时候,系统应该分配下一个事务的事务id

        creator_trx_id: 生成该Read View的事务的事务id

如何通过Read View来判断记录的某个版本是否可见呢

        1.如果trx_id = creator_trx_id,说明当前事务正在访问自己修改过的记录,所以该版本可以被当前事务访问

        2.如果trx_id < min_trx_id,说明访问的事务在当前事务生成Read View之前已经提交了,所以该版本可以被当前事务访问 

        3.如果trx_id >= max_trx_id,说明访问的事务是在当前事务生成生成Read View之后才开启,所以该版本不可以被当前事务访问

        4.如果trx_id in m_ids,说明创建Read View时,该版本的事务还是活跃的,该版本不可访问

        5.如果trx_id not in m_ids,说明创建Read View时,该版本的事务已经被提交了,该版本可以访问

   如果某个版本的数据对当前事务不可见,那就顺着版本链找到下一个版本的数据,并且继续执行以上步骤来判断记录的可见性,直到版本链中的最后一个版本

        Read View生成时机

Read COMMITED 和 REPEATABLE READ隔离级别之间一个非常大的区别就是----- 他们生成Read View的时机不同

        READ COMMITED:  在一个事务中,每次读取记录都会生成一个Read View

以上图可以看出,READ COMMITED隔离级别解决脏读(读未提交)的问题 

        REPEATABLE READ:  在一个事务中,只在第一次读取记录的时候生成一个Read View

 以上可以看出REPEATABLE READ 隔离级别可以解决不可重复读(修改了另一个未提交事务读取的数据)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值