以往已经总结过Mysql底层实现上的一些细节,上一篇的文章也用通俗的语言说了一些自己对Mysql底层存储实现上的认识与理解。这篇文章就在原来的基础上来说一下基于redo、undo日志实现的数据安全存储、以及结合存储机制和版本链等技术对Mysql事务的实现。在文章中主要是分为两部分一个是redo、undo日志的结构、关键属性的相关计算,Mysql事务以及隔离级别实现的原理!
课堂笔记:(3条消息) Mysql二——事务控制背后的redo、undo以及MVCC_任天柳-CSDN博客
思维导图:https://www.processon.com/view/link/622077c207912952a5a52d23
1、redo block 头部信息的含义
一、redo日志结构
redo日志的结构类似于前面说的数据存储过程中的结构,每一条redo日志可以理解为是一个记录,一个页面层面上的原子性操作中可能包含一个或是多个redo日志我们可以将这个单位称之为MTR。这里MTR的概念与存储结构中提出的组的概念是相似的,这里说的相似主要是指——MTR是位于redo日志与redo block之前的一个“单位”。redo block完全可以与页的概念做类比,不过页里的槽、组是不会跨页的,而redo block中的MTR可能是要跨block的,在理解redo日志的读写时,由MTR、block结构而引起的相关计算是需要着重理解的。
上面我们纵向的说了redo机制中的存在的几个结构,纵向来一条redo日志他是分为不同的类型的,对于数据的定位他使用的机制类似于极坐标或是相对位置(指定数据存储的起始位置、与数据的存储长度)从而确定数据是在哪里存储的。在redo日志中通过表空间、页号,来锁定redo对应记录所在的页。type以及页面中的偏移量来确定确定日志的记录位置。上面我们说的是简单日志类型的结构,在真正的数据插入中数据的结构是要更复杂的,也就是说redo日志是有不同的类型的。
redo日志最后也是要刷到磁盘的,相对于log buffer 的磁盘刷新,他们有一个后台的程度进行定时的刷新、以及当空间不够的时候进行刷新,但是log buffer在事务提交的时候也是有一次刷新到磁盘的,刷新成功之后事务则会返回成功。这样的机制在上一篇说存储结构的时候已经说过,他多增加一层从而确保了数据的安全性!磁盘中的存储结构与log buffer 是遥相呼应的,数据库中默认有两个日志文件ib_logfile文件,这个文件默认的多少以及大小都是可以通过相应的参数进行设置的。在往磁盘上写入的过程,会按照磁盘上日志文件的顺序来进行一个循环写入。每个ib_logfile开头都有一个固定的数据结构4个大小为512B的块——(log_file_header、checkpoint1、checkpoint2、没有用的),在log_file_header中有一个属性LOG_HEADER_START_LSN,这个属性在日志刷盘的过程中是有作用的,与他搭配使用是全局变量lsn(是用来表示log buffer中日志刷新的一个情况)。说到这里纵向、横向的结构已经说得差不多了,接下来说下日志同步过程中的关键属性值的变化。
lsn的默认值是8704他是用来记录buffer中已经写入到磁盘的大小,加入我们写入磁盘的第一条数据的大小为100,那么写完之后lsn的大小为(8704+12+100),之后算lsn时候也是需要把block的头尾加上的,在log buffer中与lsn想关联的的还有一个buffer_next_to_writ(下一次写入磁盘时的位置)、buf_free(空闲log_buffer)。上面说的这几个都是buffer中的,磁盘中的起始位置应该是2048,而对应lsn中起始值是8704!
二、undo日志
undo日志针对是数据恢复,所以产生redo日志的是增删改操作,不过针对不同的操作类型日志的结构是不同的。
针对插入过程的undo日志,里面除过end_of_record、start_of_record,之外还有记录日志类型的undo_type、undo_no、以及这个undo日志对应的table_id,那么undo如何锁定到他所对应的数据呢,这里面起作用的是主键各列信息,里面会记录主键的长度以及他所对应数据的id值。在insert类型的undo日志中没有trx_id、row_point,这是因为insert的操作整体是简单的,因为实物提交之后结合redo日志是可以保证数据保存到磁盘中的。上面提到的这两个关键信息后面都会结合版本链以及readView机制来实现对事务的一个控制以及不合理数据的一个回滚。
到这里要理解为什么insert类型的undo日志不需要trx_id、row_point这个属性,希望在后面的阐述中可以说明这个问题
delete、update操作的undo日志中除过上面我们说的几个信息里面还记录了len_of_index_col_info、索引各列信息列表,前面记录的是后面“索引各列信息列表”存储信息的一个长度之和在加上他本身的长度——2。记录各索引列值中会用到存储结构中记录的一些信息,这一点需要理解。 delect、update的操作会与记录中的roll_pointer结合起来形成一个版本链:
? undo日志中的roll_pointer与记录中的这个值是什么关系,他们起着什么样的作用,版本链在底层的结构中是记录的范畴还是说undo日志的范畴?
应该是属于undo日志的范畴,否则的话也不合理呀!!!答案如下:
二、隔离级别的实现
Mysql的事务中有ACID的概念,A指的是原子性——一个操作要么全部操作成功要么全部操作失败进行回滚;C表示的是一致性主要约束的是数据要合规合理列入不能为空不能为负或是男女这些信息;I表示隔离性,主要约束的是并发情况下数据的互补影响;D表示的是持久性,持久性要求能够确保数据存储成功,在Mysql中的redo日志据就是实现持久性的一个关键。
上面AICD可以说是指导持久层的道,在术的层面上还有脏写、脏读、不可重复读、幻读等情况,这些情况都是在多个事务并发的情况下产生的。下面主要数一下产生这些问题的场景。脏读是多个事务对一个数据不受限制的改变;脏读是一个事务读取了另一个事务还没有提交的数据。不可重复度则是在一个事务中两个查询读取到了两个不同的数据,产生这个现象是因为咋读的事务过程中有一个事务完成了对数据的修改并进行了提交。上面我们说的几个问题都是发生在对一条数据的操作上,而幻读是发生在范围的查找上,在读的事务上前后两次都去的都是一定范围上的数据,而在这两次读取的过程中有一个事务对这个范围内的数据进行了修改,所以两次读取到的数据不同。 在实际的业务场景中我们通常使用的是中间的两种隔离级别,中间这两种隔离界别实现的一个原理就是他们在事务中生成readView的时机不同,readView我们可以理解为是快照,他里面存储的信息是——m_ids(当前活跃列表id)、min_trx_id、max_trx_id、creator_trx_id(创建readView的事务id)。在隔离级别中read_commited是每次数据读取都会生成readView,repeatable_read则是在事务中只有第一次select的时候才会生成readView。
epeatable_read则是在事务中只有第一次select的时候才会生成readView。
|