MySQL阶段三笔记:事务、隔离级别、锁(不全是原创)

目录

一、事务

二、日志系统:redo log、bin log、undo log

三、小考试

四、MVCC(扈老师说比较难,让我反复看)

五、简单删除语句加锁分析(内容引自微信公众号)

六、锁 


一、事务

  1. 概念:

    要么都执行,要么都不执行;

    Oracle数据库来说,默认的事务隔离级别是READ COMMITTED,是不满足隔离性的要求的

    ACID

    • 原子性(Atomicity):要么都执行,要么都不执行
    • 一致性(consistency):事务执行前后状态一致,保证数据库完整性约束;不在乎中间过程;分为强一致性、弱一致性和最终一致性
    • 隔离性(isolation):事务和事务之间互不影响
    • 持久性(durability):事务一旦提交,就是永久的分类
  2. 分类:扁平事务、带有保存点的扁平事务、链事务、嵌套事务、分布式事务
    • 扁平事务:在一个事务里面做的所有操作
      BEGIN WORK  
      Operation 1  
      Operation 2  
      Operation 3  
      ...  
      Operation N  
      COMMIT WORK 
      
      
      
      例如:
      begin work;  
      select * from user;  
      update user set name = 'sihai' where id = 1;  
      commit work; 
    • 带有保存点的扁平事务:一个事务里面可以设置多个保存点,可以按照保存点回滚,比如有保存点1,2,3,4;则可以回滚到1,2,3,4任意一个保存点
      begin work;  
      select * from user;  
      savepoint t1;  
      update user set name = 'sihai' where id = 1;  
      savepoint t2;  
      select * from user;  
      savepoint t3;  
      update user set name = 'sihai' where id = 1;  
      savepoint t4; 
      commit work; 

    • 链事务:链事务使多个事务能保持原子性,下一个事务能获取当前事务的结果;如果回滚,则只能回滚到最近的保存点?链事务也需要有保存点吗,直接按事务回滚,只回滚最近的事务不行吗?有保存点的作用是为了把多个链事务放到一个保存点方便一起会回滚吗;如下,只能回滚到保存点4
      begin work;  
      select * from user;  
      savepoint t1;  
      update user set name = 'sihai' where id = 1;  
      savepoint t2;  
      select * from user;  
      savepoint t3;  
      update user set name = 'sihai' where id = 1;  
      savepoint t4; 
      commit work; 

      **链事务在执行commit后就会释放当前事务所持有的所有锁,而带有保存点的扁平事务不会影响所持有的锁????

    • 嵌套事务:事务1可以嵌套子事务2,子事务3,子事务2里面还可以嵌套子事务4,子事务5,且子事务提交后,要等根事务提交,才能真正提交;根事务回滚,则下属的所有子事务都回滚

    • 分布式事务:

      分布式事务通常是指在一个分布式环境下运行的扁平事务(只能是扁平事务吗?不能是链事务或者嵌套事务吗),因此需要根据数据所在位置访问网络中的不同节点。      CAP理论:Consistency、Availability、Partition tolerance 三个单词的缩写,分别表示一致性、可用性、分区容忍性

      在不同的物理地址,通过网络访问,执行不同的事务,这就是分布式事务

  3. 使用:                                                                                                                           COMMIT和COMMIT WORK语句不同之处在于COMMIT WORK用来控制事务结束后的行为是CHAIN还是RELEASE,如果是CHAIN,那么事务就是链事务

    completion_type=2时,上一事务commit后,马上与服务器断开链接,再进行新的事务,需要重新建立连接

  4. 隔离级别:PS今天fu老师给讲了隔离级别:RU、RR、RC、还有一个不常用,单次没背下来,对照上图应该是serializable,直接锁死
  5. 事务隔离是为了解决:脏读、幻读、不可重复读
  •  READ UNCOMMITTED
  •  READ COMMITTED
  •  REPEATABLE READ
  •  SERIALIZABLE

PS:阶段三第二篇为啥是日志系统,我以为都是事务呢,今天的我不识好歹了,但是因为我的错,导致了fu老师的错~今天不是100分,拉倒了

二、日志系统:redo log、bin log、undo log

PS:fu老师找的第二篇文章贼棒,后面全程wawawa的读,这篇讲的真是好!!!!

MySQL日志系统:redo log、binlog、undo log 区别与作用_JobShow裁员加班实况-微信小程序-CSDN博客_binlog redolog undolog区别

MySQL中的六种日志文件_淡淡的倔强的博客-CSDN博客_mysql日志文件


redo log是InnoDB存储引擎层的日志,binlog是MySQL Server层记录的日志

innodb事务日志包括redo log和undo log。redo log是重做日志,提供前滚操作,undo log是回滚日志,提供回滚操作

redo log是物理日志,记录数据页的修改数据

bin log和undo log是逻辑日志,记录修改sql语句

  1. redo log:重做日志文件,用于记录事务操作的变化,记录的是数据修改之后的值,不管事务是否提交都会记录下来???????事务没提交也会记录吗;保存的是更新记录;redo log日志的大小是固定的,即记录满了以后就从头循环写,有过期时间的说法吗???(是不是因为可以循环写,所以不用设置过期时间)用来恢复提交后???的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置)。数据

    *****什么时候产生

    事务开始之后就产生redo log,redo log的落盘并不是随着事务的提交才写入的,而是在事务的执行过程中,便开始写入redo log文件中。?????如果事务没提交就出现问题了,redo log是怎么修改的,用undo log来修改吗-------是用uodo log来回滚,哈哈

    *****什么时候释放

    当对应事务的脏页写入到磁盘之后,redo log的使命也就完成了,重做日志占用的空间就可以重用(被覆盖)。

    然后会通过以下三种方式将innodb日志缓冲区的日志刷新到磁盘

    1、Master Thread 每秒一次执行刷新Innodb_log_buffer到重做日志文件。 
    2、每个事务提交时会将重做日志刷新到重做日志文件。 
    3、当重做日志缓存可用空间 少于一半时,重做日志缓存被刷新到重做日志文件

    由此可以看出,重做日志通过不止一种方式写入到磁盘,尤其是对于第一种方式,Innodb_log_buffer到重做日志文件是Master Thread线程的定时任务

    因此重做日志的写盘,并不一定是随着事务的提交才写入重做日志文件的,而是随着事务的开始,逐步开始的。

    另外引用《MySQL技术内幕 Innodb 存储引擎》(page37)上的原话:

    即使某个事务还没有提交,Innodb存储引擎仍然每秒会将重做日志缓存刷新到重做日志文件。

    这一点是必须要知道的,因为这可以很好地解释再大的事务的提交(commit)的时间也是很短暂的。

  2. bin log属于MySQL Server层面的,又称为归档日志,属于逻辑日志,是以二进制的形式记录的是这个语句的原始逻辑,依靠binlog是没有crash-safe能力的,sql语句

    逻辑格式的日志,可以简单认为就是执行过的事务中的sql语句

    *******但又不完全是sql语句这么简单,而是包括了执行的sql语句(增删改)反向的信息,也就意味着delete对应着delete本身和其反向的insert;update对应着update执行前后的版本的信息;insert对应着delete和insert本身的信息********。

  3. undo log:是回滚日志,提供回滚操作,不是redo log的逆向过程,其实它们都算是用来恢复的日志

    什么时候产生

    事务开始之前,将当前是的版本生成undo log,undo 也会产生 redo 来保证undo log的可靠性

    什么时候释放

    当事务提交之后,undo log并不能立马被删除,而是放入待清理的链表,由purge线程判断是否由其他事务在使用undo段中表的上一个事务之前的版本信息,决定是否可以清理undo log的日志空间。

  • redo log和binlog区别

  • redo log是属于innoDB层面,binlog属于MySQL Server层面的,这样在数据库用别的存储引擎时可以达到一致性的要求。
  • redo log是物理日志,记录该数据页更新的内容;binlog是逻辑日志,记录的是这个更新语句的原始逻辑
  • redo log是循环写,日志空间大小固定;binlog是追加写,是指一份写到一定大小的时候会更换下一个文件,不会覆盖。
  • binlog可以作为恢复数据使用,主从复制搭建,redo log作为异常宕机或者介质故障后的数据恢复使用。

一条更新语句执行的顺序

update T set c=c+1 where ID=2;

  • 执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
  • 执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。  ????为啥是一行新数据,是比如原来那行数据1在bin log 中是更新的sql,比如这个sql中值=c;新一行数据2就是:update T set c=c+1 where ID=2,且值=C+1  然后bin log中既有数据1,又有数据2吗,还是说commit后,只有数据2,没有数据1了????---------对应截图,证明我说的是对的,哈哈
  • 引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
  • 执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
  • 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。

redo日志、undo日志:

存储引擎也会为redo undo日志开辟内存缓存空间,log buffer。磁盘上的日志文件称为log file,是顺序追加的,性能非常高,注:磁盘的顺序写性能比内存的写性能差不了多少。

undo日志用于记录事务开始前的状态,用于事务失败时的回滚操作;redo日志记录事务执行后的状态,用来恢复未写入data file的已成功事务更新的数据。例如某一事务的事务序号为T1,其对数据X进行修改,设X的原值是5,修改后的值为15,那么Undo日志为<T1, X, 5>,Redo日志为<T1, X, 15>。

梳理下事务执行的各个阶段:

(1)写undo日志到log buffer;

(2)执行事务,并写redo日志到log buffer;

(3)如果innodb_flush_log_at_trx_commit=1,则将redo日志写到log file,并刷新落盘。

(4)提交事务。

可能有同学会问,为什么没有写data file,事务就提交了?

在数据库的世界里,数据从来都不重要,日志才是最重要的,有了日志就有了一切。

因为data buffer中的数据会在合适的时间 由存储引擎写入到data file,如果在写入之前,数据库宕机了,根据落盘的redo日志,完全可以将事务更改的数据恢复。好了,看出日志的重要性了吧。先持久化日志的策略叫做Write Ahead Log,即预写日志。

三、小考试

1、事务的本质?锁、日志

2、子事务的应用?一个大事务中的小事务,不需要关心其他事务是否提交或者回滚,自己处理完就可以提交

3、加事务的原则?单个事务内,尽量处理尽可能少的,关系最近的数据库操作,减少其他非数据库操作在其中

4、怎样避免死锁?按照顺序加锁

5、RR和RC的优缺点、区别?

RR是可重复读,在一个事务内,数据在任何时候读的内容都是一致的,优点:避免幻读;缺点:可能产生幻读

RC是读提交,读取的是提交后的数据,优点:避免脏读;缺点:不可重复读、幻读

mysql默认RR

PS:扈老师,我错错了,完了,只能记住这么多,其他内容没记住,小纸纸没带回来,后面补充

四、MVCC(扈老师说比较难,让我反复看)

多版本并发控制:写写阻塞、读写、读读、写读都不阻塞,实现可重复读;同一份数据临时保存多个版本的一种方式,进而实现并发控制

1、快照,学名叫做一致性视图,这也是可重复读和不可重复读的关键,可重复读是事务开始的时候生成一个当前事务全局性的快照,而读提交则是每次执行语句的时候都重新生成一次快照

快照读:读取的是记录中的可见版本(可能是历史版本),不用加锁

对于一个快照来说,它能够读到那些版本数据,要遵循以下规则:

  1. 当前事务内的更新,可以读到;
  2. 版本未提交,不能读到;
  3. 版本已提交,但是却在快照创建后提交的,不能读到;
  4. 版本已提交,且是在快照创建前提交的,可以读到;

2、当前读(特殊的select操作、insert、delete和update):读取的是记录中最新版本,并且当前读返回的记录都会加上锁,这样保证了了其他事务不会再并发修改这条记录

MVCC能解决什么问题,好处是?

数据库并发场景有三种,分别为:

  • 读-读:不存在任何问题,也不需要并发控制
  • 读-写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读
  • 写-写:有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失

备注:第1类丢失更新:事务A撤销时,把已经提交的事务B的更新数据覆盖了;第2类丢失更新:事务A覆盖事务B已经提交的数据,造成事务B所做的操作丢失

MVCC带来的好处是?
多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。 所以MVCC可以为数据库解决以下问题

  • 在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能
  • 同时还可以解决脏读,幻读,不可重复读等事务隔离问题,但不能解决更新丢失问题

两阶段锁:加锁、解锁

五、简单删除语句加锁分析(内容引自微信公众号)

https://mp.weixin.qq.com/s/-LGrg-wZqAaGRPZAHICujQ

# table T (id int, name varchar(20))
delete from T where id = 10;

组合一:id主键 + RC  只给这条主键数据加行锁

图片

组合二:id唯一索引 + RC:给当前唯一索引加锁,并给对应的聚簇索引加行锁

图片

组合三:id不唯一索引+RC:给多条数据加X锁,并且给对应的多条聚簇索引加X锁

图片

组合四:id无索引+RC:直接锁表,走聚簇索引,进行全表扫描,但是mysql进行优化,走聚簇索引时,如果当前索引不满足条件,则释放锁,调用unlock row方法,这样做,保证了最后满足条件的记录加上锁,但是每条记录的加锁操作是不能省略的,同时优化违背了2PL原则。

图片

组合五:id主键+RR:主键数据直接加一条行锁

组合六:id唯一索引+RR:唯一索引加一条行锁,对应的聚簇索引也加一条行锁

组合七:id不唯一索引+RR

为了解决幻读,先给数据相同的索引加上行锁,并给聚簇索引加上行锁,然后再给数据相同的索引前后间隙加上间隙锁

图片

结论:在RR隔离级别下,id列上有非唯一索引,对于上述的SQL语句;首先,通过id索引定位到第一条满足条件的记录,给记录加上X锁,并且给Gap加上Gap锁,然后在主键聚簇索引上满足相同条件的记录加上X锁,然后返回;之后读取下一条记录重复进行。直至第一条出现不满足条件的记录,此时,不需要给记录加上X锁,但是需要给Gap加上Gap锁吗,最后返回结果。

组合八:id无索引+RR:全表扫描,每行都加行锁,并加上间隙锁

图片

六、锁 

1、共享锁:读锁(S锁):共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到最新数据。

        如果只有一个事务,用了共享锁,则该事务能进行删改操作;如果多个事务取到同一个共享锁,则都不能进行删改动作

如果有多个事务拿到了共享锁,则所有事务都不能对数据进行 UPDATE DETELE 等操作。?????

举例里面,拿到同一个共享锁的,明明事务2的更新操作只是阻塞,阻塞后更新成功了,怎么说没有更新成功呢,是不是sleep10这个10秒的过程中,才有共享锁,超过10秒,共享锁已经释放了,所以可以更新

lock in share mode

例子:两个事务:

拿同一个共享锁没问题,都不阻塞,但是更新操作要等,共享锁释放才能更新

/* 开启事务1 */
BEGIN;
/* 查询ID为1的数据并加上共享锁 */
SELECT * FROM `test` WHERE `id` = 1 LOCK IN SHARE MODE;
/* 延迟10秒执行 */
SELECT SLEEP(10);
/* 尝试修改 id = 1 的数据 */
UPDATE `test` SET `name` = '小新1' WHERE `id` = 1;
/* 延迟30秒执行 */
SELECT SLEEP(30);
/* 提交事务1 */
COMMIT;

/* 开启事务2 */
BEGIN;
/* 查询ID为1的数据,并且也加上共享锁 */
SELECT * FROM `test` WHERE `id` = 1 LOCK IN SHARE MODE;
/* 尝试修改 id = 1 的数据 */
UPDATE `test` SET `name` = '小新3' WHERE `id` = 1;
/* 提交事务2 */
COMMIT;

 

2、排他锁:写锁(X锁):排它锁不能与其它锁并存,而且只有一个事务能拿到某一数据行的排它锁,其余事务不能再获取该数据行的所有锁一个事务A获取排他锁后,其他事务的任何对数据修改的操作都是阻塞的状态,都要等事务A的排他锁释放后才能执行

加排他锁是串行的

for update,在一个事务中,begin和commit中

例如:两个事务

事务1加了排他锁后,事务2中加排他锁的执行要等事务1提交后才行

事务1加了排他锁后,事务2中更新操作的执行要等事务1提交后才行

/* 开启事务1 */
BEGIN;
/* 查询ID为1的数据并加上排他锁 */
SELECT * FROM `test` WHERE `id` = 1 FOR UPDATE;
/* 延迟10秒执行 */
SELECT SLEEP(10);
/* 尝试修改 id = 1 的数据 */
UPDATE `test` SET `name` = '小新6' WHERE `id` = 1;

/* 延迟15秒执行 */
SELECT SLEEP(15);
/* 提交事务1 */
COMMIT;

/* 开启事务2 */
BEGIN;
/* 普通查询id = 1的数据 */
SELECT * FROM `test` WHERE `id` = 1;
/* 事务2也给id = 1的数据加上排它锁 */
SELECT * FROM `test` WHERE `id` = 1 FOR UPDATE;
/* 提交事务2 */
COMMIT;

/* 开启事务2 */
BEGIN;
/* 尝试修改 id = 1 的数据 */
UPDATE `test` SET `name` = '小张' WHERE `id` = 1;
/* 提交事务2 */
COMMIT;

 

 

共享/排它锁对于性能的影响

  1. 因为排它锁只允许一个事务获取,所以如果是业务繁忙的情况下,一旦有某个业务不能及时的释放锁,则会导致其它事务的锁等待、锁等待超时、死锁等问题;
  2. 虽然共享锁可以给多个事务共享,但一旦有多个事务同时拥有共享锁,则所有事务都不能对数据进行 UPDATE DETELE 等操作,也会导致其它事务的锁等待、锁等待超时、死锁等问题;
  3. 都会影响数据库的并发能力。

3、Gap锁和Next-Key锁:

加锁原则:范围查找,zuo

在InnoDB中完整行锁包含三部分:

记录锁(Record Lock):记录锁锁定索引中的一条记录。行锁

间隙锁(Gap Lock):间隙锁要么锁住索引记录中间的值,要么锁住第一个索引记录前面的值或最后一个索引记录后面的值。针对相同的记录上锁;左开右开?

范围锁(Next-Key Lock):Next-Key锁时索引记录上的记录锁和在记录之前的间隙锁的组合;左开右闭

  • 索引上的等值查询--给唯一索引加锁的时候,next-key lock升级为行锁
  • 索引上的等值查询--向右遍历时最后一个值不满足查询需求时,next-key lock 退化为间隙锁

不同于写锁相互之间是互斥的原则,间隙锁之间不是互斥的;

事务A和事务B都取到同一个间隙锁,并且执行了写锁,就会产生死锁

间隙锁案例,好难啊

作者:小亮__
链接:https://www.jianshu.com/p/32904ee07e56
来源:简书

加锁规则有以下特性,我们会在后面的案例中逐一解释:

  • 1.加锁的基本单位是(next-key lock),他是前开后闭原则
  • 2.插叙过程中访问的对象会增加锁
  • 3.索引上的等值查询--给唯一索引加锁的时候,next-key lock升级为行锁
  • 4.索引上的等值查询--向右遍历时最后一个值不满足查询需求时,next-key lock 退化为间隙锁
  • 5.唯一索引上的范围查询会访问到不满足条件的第一个值为止


 

 哦哦,等值查询,左开右闭的范围锁才会退化成左闭右闭,范围查询不会退化,所以上图,范围锁:[10,15],所以事务3也会阻塞

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值