0
点赞
收藏
分享

微信扫一扫

锁系列一之概述

追风骚年 2022-01-20 阅读 72

一、MySQL并发事务访问相同记录

1、读-读情况

读-读 情况,即并发事务相继 读取相同的记录 。读取操作本身不会对记录有任何影响,并不会引起什么问题,所以允许这种情况的发生。

2、写-写情况

即并发事务相继对相同的记录做出改动

在这种情况下会发生 脏写 的问题,任何一种隔离级别都不允许这种问题的发生。所以在多个未提交事务相继对一条记录做改动时,需要让它们 排队执行 ,这个排队的过程其实是通过 锁 来实现的。这个所谓的锁其实是一个 内存中的结构 ,在事务执行前本来是没有锁的,也就是说一开始是没有 锁结构 和记录进行关联的

当一个事务想对记录做改动时,首先会看看内存中有没有与这条记录关联的 锁结构 ,当没有的时候就会在内存中生成一个 锁结构 与之关联。比如,事务 T1 要对这条记录做改动,就需要生成一个 锁结构与之关联

锁结构中的信息比较多,先主要关注如下两个信息

(1)trx信息

代表这个锁结构是哪一个事务生成的

(2)is_waiting

代表当前事务是否在等待

 

当事务T1改动了记录后,就生成了一个锁结构与记录关联,因为没有别的事务为该记录加锁,所以is_waiting属性就是false,此时获取锁成功。

在事务T1提交前,另一个事务T2也想对该记录做改动。那么先看看有没有锁结构与该记录关联,发现有一个锁结构与之关联后,然后生成了一个锁结构与该记录关联,不过锁结构的is_waiting属性为true,表示当前事务需要等待,此时为加锁失败。

在事务T1提交后,就会把该事务生成的锁结构释放掉,然后看有没有别的事务在等待获取锁,发现了事务T2还在等待获取锁,所以把事务T2对应锁结构的is_waiting设置为false,然后把事务对应的线程唤醒,让他继续执行,此时事务T2获取到锁。

 (1)不加锁
        就是不需要在内存中生成对应的 锁结构 ,可以直接执行操作。
(2)获取锁成功,或者加锁成功
        在内存中生成了对应的 锁结构 ,而且锁结构的 is_waiting 属性为 false ,也就是事务可以继续执行操作。
(3)获取锁失败,或者加锁失败,或者没有获取到锁
        在内存中生成了对应的 锁结构 ,不过锁结构的 is_waiting 属性为 true ,也就是事务需要等待,不可以继续执行操作。

3、 读-写或写-读情况

读-写 或 写-读 ,即一个事务进行读取操作,另一个进行改动操作。这种情况下可能发生 脏读 、 不可重复读 、 幻读 的问题。

4、并发问题的解决方案

解决 脏读 、 不可重复读 、 幻读的两种方案

(1)读操作利用多版本并发控制( MVCC ,下章讲解),写操作进行 加锁 

普通的SELECT语句在READ COMMITTED和REPEATABLE READ隔离级别下会使用到MVCC读取记录。

在 READ COMMITTED 隔离级别下,一个事务在执行过程中每次执行SELECT操作时都会生成一个ReadView,ReadView的存在本身就保证了 事务不可以读取到未提交的事务所做的更改 ,也就是避免了脏读现象;

在 REPEATABLE READ 隔离级别下,一个事务在执行过程中只有 第一次执行SELECT操作 才会生成一个ReadView,之后的SELECT操作都 复用 这个ReadView,这样也就避免了不可重复读和幻读的问题。

(2)读、写操作都采用 加锁 的方式

注意

(1)采用 MVCC 方式的话, 读-写 操作彼此并不冲突, 性能更高

(2)采用 加锁 方式的话, 读-写 操作彼此需要 排队执行 ,影响性能

二、锁

1、 从数据操作的类型划分:读锁和写锁

2、从数据操作的粒度划分:表级锁、页级锁、行锁

 

举报

相关推荐

0 条评论