大家好,我是考100分的小小码 ,祝大家学习进步,加薪顺利呀。今天说一说一文彻底掌握MySQL事务(四大特性,四种隔离级别,七种传播行为)[通俗易懂],希望您对编程的造诣更进一步.
小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
![一文彻底掌握MySQL事务(四大特性,四种隔离级别,七种传播行为)[通俗易懂]插图1 一文彻底掌握MySQL事务(四大特性,四种隔离级别,七种传播行为)[通俗易懂]](https://daima100.com/wp-content/themes/justnews/themer/assets/images/lazy.png)
事务四大特性
原子性(Atomicity)
原子性指整个事务是不可分割的工作单位。在事务中所有数据库操作都执行成功,才算整个事务成功。事务中任何一个sql语句执行失败,已经执行成功的sql语句也必须被撤销,数据库状态回到执行事务前的状态。
一致性(Consistency)
事务在执行之前和执行之后都必须处于一致性状态。在事务开始之前和事务结束之后,数据库的完整性约束没有被破坏。
例如转账操作,A有200元,B有100元,A转账100元给B,在转账前A,B总共有300元,转账后两者总和应该还是300元,这就是事务一致性。
隔离性(Isolation)
隔离性也叫做并发控制,可串行化、锁等。事务的隔离性要求每个读写事务对象对其他事务的操作对象能相互分离,即该事务提交前对其他事务都不可见,通常这使用锁来实现。
当多个事务同时执行一个操作时,为了保证各个事务之前不会被互相干扰,就要对多个并发事务进行隔离
持久性(Durability)
持久性是指一个事务一旦被提交,那么对数据库中的数据的改变时永久性的,即便是数据库系统出现故障,也不会丢失提交事务的操作。
事务的实现
事务的隔离性是由锁来实现的,原子性,一致性,持久性都是通过数据库的 redo log 和 undo log来完成的。redo log是重做日志,用来保证事务的一致性和持有性,undo log用来保证事务的原子性。
redo log (重做日志)
概念
redo log是由两部分组成,一个是内存中的重做日志缓冲,这个是容易丢失的,第二个是重做日志文件(redo log file),这个是持久的。
当事务提交的时候,会先将该事务的所有日志写入到重做日志中。为了确保每次日志都写入重做日志文件,每次重做日志缓冲写入日志文件后,innoDB引擎都需要调用一次fsync操作,这个fsync操作是为了确保重做日志写入磁盘。
log block
重做日志都是以512个字节进行存储,这说明重做日志缓存、重做日志文件都是以块(block)的方式进行保存的,称之为重做日志块(redo log block),每块大小是512字节。
如果一个页的重做日志数量大于512字节,那就需要分割为多个重做日志进行存储。由于重做日志块大小和磁盘扇区大小一样,都是512字节,因此重做日志写入可以保证原子性,不需要doublewrite技术。
log buffer会根据一定规则将内存中的log block刷新到磁盘,具体规则如下:
- 事务提交的时候
- 当 log buffer中有一半的内存空间已经被使用的时候
- log checkpoint的时候
重做日志格式
inndoDB引擎中存储管理是基于页的,所以重做日志格式也是基于页的。其头部格式如下图
![一文彻底掌握MySQL事务(四大特性,四种隔离级别,七种传播行为)[通俗易懂]插图3 一文彻底掌握MySQL事务(四大特性,四种隔离级别,七种传播行为)[通俗易懂]](https://daima100.com/wp-content/themes/justnews/themer/assets/images/lazy.png)
- redo_log_type:重做日志类型
- space:表空间id
- page_no:页的偏移量
- redo log body:重做日志具体内容
LSN
代表的是日志序列号,表示重做日志写入的总量,checkpoint的位置,页的版本
例如当前重做日志的LSN为1000,有一个事务T1写入100字节的重做日志,那么LSN就会变为1100.
脏页
内存脏页,内存中发送了修改,没写入到磁盘之前,会把内存页称为脏页。
checkpoint
检查点,就是将脏页刷新到磁盘的动作。
undo (回滚日志)
基本概念
有时事务需要回滚操作,就需要用到undo.对数据库进行修改时,存储引擎不但会产生redo,也会产生undo,如果用户执行的事务或语句由于某种原因失败了,又或者用户执行了rollback语句请求回滚,就可以利用undo信息将数据回滚到修改之前的样子。
undo存放在数据库内部的一个特殊段(segment)中,这个段称为undo段。
undo是逻辑日志,因此只是将数据库逻辑的恢复到原来的样子。
当用户执行了一个insert语句,在用户rollback,会将插入的事务进行回滚,回滚实际上做的是与先前相反的工作。每个insert,innoDB存储引擎会完成一个delete.每个delete回滚时会执行一个insert.
undo log会产生redo log,因为undo log也需要持久性的保护。
undo格式
在innoDB存储引擎中,undo log分为:
insert undo log
update undo log
insert undo log 是指在insert操作中产生的undo log.因为Insert操作只对事务本身可见,对其他事务不可见,所以可以在事务提交后删除。不需要purge操作。
update undo log 记录的是 delete和update操作产生的undo log,该 undo log可能需要提供mvcc机制,因此不能在事务提交的时候进行删除。提交需要放到undo log链表,然后等待purge线程进行最后的删除。
purge
delete和update操作可能并不直接删除原有的数据。
例如 delete from t where a=1;
表t 上列a 有聚簇索引,列b有辅助索引,对于delete操作,仅会把主键列等于1的记录delete flag设置为1,记录并没有删除,即记录还是存在于B+树中。真正删除这行记录的操作其实被延时执行了,会通过purege操作中完成。
这样设计时因为inndodb存储引擎支持MVCC,所以记录不能在事务提交时立即处理。这时可能其他事务在引用这行,所以innoDB存储引擎需要保存记录之前的版本。是否可以删除该条记录通过purge来进行判断,只有改行记录已不被其他任何事务引用,就可以进行真正的delete操作。
group commit
若事务是非只读事务,则每次事务提交需要一次fsync操作,以此保证重做日志都已经写入磁盘。为了提高fsync的效率,数据库使用了group commit功能,即一次fsync可以刷新多个事务写入日志文件。
对于innodb引擎,事务提交会进行两阶段操作:
- 修改内存事务对应信息,并且将日志写入重做日志缓冲。
- 调用fsync将确保日志都从重做日志缓冲写入磁盘。
步骤2因为需要与磁盘打交道,所以会较慢,group commit可以将多个事务的重做日志通过一次fsync刷新到磁盘,这样就减少了磁盘压力,提高了数据库性能。
binlog (二进制文件)
binlog是用来进行 POINT-IN-TIME(PIT)的恢复及主从复制环境的建立。从表面看和重做日志很相似,都是记录了对于数据库操作的日志。但本质上有着很大不同。
- 重做日志在InnoDB存储引擎产生,而二进制日志是在MySQL数据库的上层产生,并且二进制文件不仅针对InnoDB存储引擎,MySQL数据库任何存储引擎对于数据库的更改都会产生二进制日志。
- 两种文件记录内容形式不同,二进制日志是一种逻辑日志,记录的是sql语句。重做日志记录的是物理格式日志,记录的是对于每个页的修改。
- 两种日志写入磁盘时间点不同,二进制日志只在事务提交完成后进行一次写入。而重做日志在事务进行中不断被写入。
- 二进制日志对于每个事务,仅包含对应事务的一个日志。重做日志会对应多个日志条目,并且事务的重做日志写入是并发的,并非是在事务提交时写入。
脏读,不可重复读,幻读
脏读
脏读就是别的事务读取了另一个事务未提交的数据,当A事务对X进行了修改,此时还没提交,而B事务对进行了修改的X进行了读取,并进行了其他操作,而后A事务回滚了,这样B事务就出现了脏读
不可重复读
是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
幻读
是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
幻读和不可重复读的区别:
不可重复读的重点是修改 : 同样的条件, 你读取过的数据,再次读取出来发现值不一样了
幻读的重点在于新增或者删除 同样的条件, 第 1 次和第 2 次读出来的记录数不一样
事务的四种隔离级别
Read Uncommitted(读取未提交内容)
所有事务都可以看到其他未提交事务的执行结果,该级别会引发脏读的问题
Read Committed(读取已提交内容)
满足了一个事务只能看见已经提交的事务所做出的改变;但是会出现不可重复读的问题,在同一个事务执行完全相同的查询可能出现不一样的结果
Repeatable Read(可重复读)
这是MySQL默认的隔离级别同一个事务执行相同的查询会看到同样的数据,但是此操作级别可能会出现幻读
Serializable(可串行化)
这是最高的隔离级别,通强制事务排序,使之不可能出现冲突。它会在每个读中加入共享锁。但是这个级别可能导致大量的超时和锁竞争
事务的七大传播行为
PROPAGATION_REQUIRED
支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS
支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY
支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW
新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER
以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则新建一个事务
MySQL多版本并发控制(MVCC)
就是多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问。
在 innodb存储引擎中,为了保证并发访问,在读已提交和可重复读的事务隔离级别下,如果一个事务对某行进行修改,
每次的修改会在undo段中进行记录,每次记录都会有个版本号,这种称之为版本链,当这个事务还没提交事务,另一个事务来读取该行数据,不会阻塞,而是根据版本链读取修改前的最新的记录数据。
版本链(ReadView)
- trx_id
这个id用来存储每次对某条聚簇索引记录进行修改时的事务id.
- roll_pointer
每次对哪条聚簇索引记录有修改的时候,都会把老版本写入undo日志中,这个roll_pointer就是存了一个指针,指向这条聚簇索引记录的上一个版本的位置。
多版本控制下读已提交和可重复读下的区别
例如 一个行之前的值是1,事务A对这个行进行了修改,变成了2,但没有提交,此时事务B对该行进行读取,在读已提交和可重复读隔离级别下,会找到这条记录之前的最新版本记录,就是1,但如果事务A提交了事务,这时在读已提交隔离级别下,读到的数据就是2了,但是可重复读读到的还是1,这是因为读已提交级别下每次查询都会生成一个独立的ReadView,而可重复读级别下第一次读的时候生成一个ReadView,之后读都是复用之前的ReadView。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/11535.html