Mysql事务与锁原理

2022/2/6 19:12:36

本文主要是介绍Mysql事务与锁原理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1.事务的四大特性

 ACID:

  A:Atomicity,原子性,要么全部执行,要么全部都不执行。

    C:Consistency,一致性,满足现实世界业务的约束。

  I :Isolation,隔离性,并行事务之间互不影响。

    D:Durability,持久性,事务一旦提交永久保留。

 

2.四大隔离级别

  READ UNCOMMITTED:未提交读,可能发生“脏读、不可重复读和幻读”问题

  READ COMMITTED:RC,已提交读,可能发生“不可重复读和幻读”问题

  REPEATABLE READ:RR,可重复读,可能发生“幻读”问题[mysql 默认]

  SERIALIZABLE:可串行化,上述问题都不可能发生

 

3.什么是脏页

  脏页发生在InnoDB的Buffer Pool(缓冲池)中,当数据发生修改的时候,一般先修改Buffer Pool的缓存页,再由缓存页去把数据刷到磁盘。将修改数据后,并且未刷盘的页,称为脏页。

 

4.持久型原理-redo

  事务的持久性是指事务一旦提交永久保留,即使数据库宕机,也不会丢失;即使服务器宕机,也不会丢失。

  InnoDB的内存管理是通过Buffer Pool来进行刷盘存储数据,如果此时发生宕机就会丢失数据,而redo日志机制就是用来保证mysql宕机时不丢失数据。

  redo日志:也叫重做日志,针对记录修改的日志,在系统崩溃后重启时,按照此日志重新执行,便能够复原没有被持久化的数据页。

  格式如下图:

 

 

 

  日志占用的空间非常小,主要存储的是:存储表空间ID、页号、偏移量以及需要更新的值。

  日志是顺序写入磁盘的,发生连续I/O的几率更高。

  

  redo日志不是一个日志文件,而是多个日志文件,因为大小有限制,需要循环写入,工作原理如下:

 

  图中,0号日志文件已经刷盘,若此时发生异常需要重做,则只需要写入1号和2号日志文件即可。

 

5.事务原子性原理-undo

  何时发生undo?当执行ROLLBACK的SQL语句时,记录需要回滚至之前的状态,那么便需要将记录之前的样子保存下来。这时就需要undo日志 

  undo日志:又叫撤销日志,在记录的修改之前都会记录记录之前状态的快照,记录的快照即为undo日志。

  在行格式中,隐藏列有一个回滚指针(roll_point),指向undo日志:

 

  undo日志链(又叫版本链)工作原理如下:

 

 

   

  当执行ROLLBACK时,找到上一个事务对应的undo日志记录,并执行复原。

 

6.事务的隔离性原理 undo+MVCC

  MVCC:多版本并发控制,借助undo日志构造的版本链,实现对数据库的并发访问。

  主要借助一些辅助信息例如上图中的(trx_id),并通过比对当前事务ID与版本链的数据ID来判断哪个版本的记录对当前事务可见。

  在InnoDB内存中有一种数据结构ReadView,用来判断记录是否对当前事务可见。

 

  ReadView主要构成内容:

   m_ids:未提交的事务ID列表。

   min_trx_id:最小ID,未提交的事务ID列表

   max_trx_id:最大ID,下一个分配事务的id值。

   creator_trx_id:当前事务ID。

 

  何时生成ReadView?

    1.READ COMMITTED RC:每次读取数据前都生成一个ReadView。(需要解决脏读

    2.REPEATABLE READ RR:第一次读取数据时生成一个ReadView,之后复用。(需要解决脏读和不可重复读

  如何判断是否可见?

    trx_id = creator_trx_id:意味着当前事务在访问它自己修改过的记录(可见)

    trx_id < min_trx_id:表明生成该版本的事务在当前事务生成ReadView前已经提交(可见)

    trx_id >= max_trx_id:表明生成该版本的事务在当前事务生成ReadView后才开启(不可见)

    min_trx_id <= trx_id < max_trx_id:判断一下trx_id属性值是不是在m_ids列表中

      如果在,说明创建ReadView时生成该版本的事务还未提交(不可见)

      如果不在,说明创建ReadView时生成该版本的事务已经被提交(可见)

 

7.什么是脏读、不可重复读、幻读

  脏读:

 

 

 

 

Session A和Session B各开启了一个事务,Session B中的事务先将number列为1的记录的name列更新为'关羽',然后Session A中的事务再去查询这条number为1的记录,如果读到列name的值为'关羽',而Session B中的事务稍后进行了回滚,那么Session A中的事务相当于读到了一个不存在的数据。

 

不可重复读:

 

 

 

在Session B中提交了几个隐式事务(注意是隐式事务,意味着语句结束事务就提交了),这些事务都修改了number列为1的记录的列name的值,每次事务提交之后,如果Session A中的事务都可以查看到最新的值。

 

幻读:

 

 

 

Session A中的事务先根据条件number > 0这个条件查询表hero,得到了name列值为'刘备'的记录;之后Session B中提交了一个隐式事务,该事务向表hero中插入了一条新记录;之后Session A中的事务再根据相同的条件number > 0查询表hero,得到的结果集中包含Session B中的事务新插入的那条记录。

 

 

 

8.Mysql的加锁过程

 

从图中可知,Mysql Server与InnoDB是一条条数据交互的,修改前需要先读(这里不是指select,而是mysql根据合适的索引读取到需要修改的数据),读的时候加锁,并且锁是一条条加的。

例子:update user set age = 1 where age = 2,由于age上没有索引,mysql查询时使用的是主键索引,mysql server与innodb是一条条数据交互的,全表扫描时,针对表内每一条数据都加了行锁,直到事务结束时才会释放,进而导致了锁全表。

如何修复脏数据?

  1. 可以分批次对数据进行修改  2.让where条件必须命中索引,若不命中则必须带有limit

 

9.行级锁

行锁作用位置:(根据加锁过程可以看到,先查询出数据,再执行锁定,查数据要选择索引)

   主键索引

   唯一的辅助索引

   普通的辅助索引

  

行锁的3种算法:

  record锁:正经的记录锁,锁当前记录

 

  gap锁:间隙锁,锁当前记录(不含)到下一条记录中间(不含)[左开右开],可以防止幻影行的插入

 

  next-key锁:record锁+gap锁,锁当前记录(不含)到下一条记录中间(含)[左开右闭]

 

 

 

 

10.什么是意向锁

意向锁:Intention Locks

  意向共享锁:Intention Shared Lock,IS锁。当事务准备在某条记录上加S锁时,需要先在表级别加一个IS锁。

  意向独占锁:Intention Exclusive Lock,IX锁。当事务准备在某条记录上加X锁时,需要先在表级别加一个IX锁。

 

意向锁存在的意义:仅仅为了在之后加表级别的S锁和X锁时可以快速判断表中的记录是否被上锁,以避免用遍历的方式来查看表中有没有上锁的记录(不需要程序员手工上锁)。

 

 

 

————————面试题————————

 

1) 为什么Mysql默认的隔离级别为Repeatable Read(RR)?

  在5.1.5版本,RC隔离级别+STATEMENT的主从同步方式的情况下,会产生主从不一致的Bug,在RR隔离级别时不会,所以默认为RR;

2) 为什么项目使用的隔离识别为Read Commited(RC)?

  1.没有必要:绝大部分场景下,不可重复读的问题都可以接受,因为很少会在一个事务里,读取2次相同的数据;且在修改之前,会先执行锁定

  2.提高并发:RC 在加锁的过程中,是不需要添加Gap Lock和 Next-Key Lock 的,只对要修改的记录添加Record Lock。

  3.减少死锁:RR的事务隔离级别会增加Gap Lock和 Next-Key Lock,这就使得锁的粒度变大,那么就会使得死锁的概率增大。

 



这篇关于Mysql事务与锁原理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程