mysql脏读、不可重复读、幻读

  1. 脏读(侧重点在读取了未提交的数据)

指一个事务读取到了另一个事务未提交的数据,造成了与数据库中的数据不一致的情况。

比如事务A修改了一条数据,但没有提交,此时事务B却读取了该条数据,事务A由于出错发生了回滚,这时事务B就形成了脏读。

 

    2. 不可重复读(虚读)(侧重点在读取了已经提交的修改的数据,数据本身对比)

指一个线程中的事务读取到了另外一个线程中的事务已提交的update的数据。即一个事务A两次或多次读取数据,在此期间,事务B读取同一数据,并修改了此数据,然后提交了,造成了事务A两次或多次读取数据出现了数据不一致的情况。

不可重复读可能造成数据丢失:

第一种丢失更新:

第一类丢失更新就是两个事务同时更新一个数据,一个事务更新完毕并提交后,另一个事务回滚,造成提交的更新丢失。

 

事务A|事务B
-|-
开始事务|开始事务
读取N=5|读取N=5
修改N=8|修改N=9
 |提交,结束事务
回滚|
结束事务(N=5)|N=5,N本应为提交的9

 

第二类丢失更新:

第二类丢失更新就是两个事务同时更新一个数据,先更新的事务提交的数据会被后更新的事务提交的数据覆盖,即先更新的事务提交的数据丢失。

 

事务A|事务B

-|-

开始事务|开始事务

读取M=8|读取M=8

更新M=1|

提交事务,M=1,结束|

 |更新M=7

   |提交事务,M=7,事务A的更新丢失,结束

 

  3. 幻读(侧重点在读取到了新增或删除的数据,数据条数对比)

指一个线程中的事务读取到了另外一个线程中事务已提交的insert的数据。即一个事务A两次或多次读取数据,在此期间,事务B新增了N条数据,然后提交了,造成了事务A两次或多次读取数据出现了数据条数不一致的情况。

 

 

 

4.隔离级别-------级别越高,数据越安全,但性能越低

    读数据一致性及允许的并   发副作用

隔离级别

读数据一致性

脏读

不可重复读

幻读

未提交读(Read uncommitted)

最低级别,只能保证不读取物理上损坏的数据

可能

可能

可能

已提交读(Read committed)

语句级

不可能

可能

可能

可重复读(Repeatable read)

事务级

不可能

不可能

可能

可串行化(Serializable)

最高级别,事务级

不可能

不可能

不可能

 

MySQL 默认的级别是:Repeatable read 可重复读,其他主流数据库,如Oracle;SQLServer默认级别为:Read committed

 

     1) 未提交读:写事务阻止其他写事务,避免了更新遗失。但是没有阻止其他读事务。

存在的问题:脏读。即读取到不一致的数据,因为另一个事务可能还没提交最终数据,这个读事务就读取了中途的数据,这个数据可能是不正确的。

解决办法:已提交读

     2) 已提交读:写事务会阻止其他读写事务。读事务不会阻止其他任何事务。

存在的问题:不可重复读。即在一次事务之间,进行了两次读取,但是结果不一致,可能第一次id为1的人叫“张三”,第二次读id为1的人就叫“王五”了。因为读取操作不会阻止其他事务。

解决办法:可重复读

     3) 可重复读

读事务会阻止其他写事务,但是不会阻止其他读事务。

存在的问题:幻读。可重复读阻止的写事务包括update和delete(只给存在的行加上了锁),但是不包括insert(新行不存在,所以没有办法加锁),所以一个事务第一次读取可能读取到了10条记录,但是第二次可能读取到11条,这就是幻读。

解决办法:串行化

     4) 可串行化

可避免幻读。读加共享锁,写加排他锁。这样读取事务可以并发,但是读写,写写事务之间都是互斥的,基本上就是一个个执行事务,所以叫串行化。

 

5. 封锁协议

封锁协议就是在用X锁或S锁时制定的一些规则,比如锁的持续时间,锁的加锁时间等。不同的封锁协议对应不同的隔离级别。事务的隔离级别一共有4种,由低到高分别是Read uncommitted、Read committed、Repeatable read、Serializable,分别对应的相应的封锁协议等级。

     1) 一级封锁协议

一级封锁协议对应的是Read uncommitted隔离级别,Read uncommitted读未提交,一个事务可以读取另一个事务未提交的数据,这是最低的级别。一级封锁协议本质上是在事务修改数据之前加上X锁,直到事务结束后才释放,事务结束包括正常结束(commit)与非正常结束(rollback)。

 

      2) 二级封锁协议

二级封锁协议本质上在一级协议的基础上(在修改数据时加X锁),在读数据时加上S锁,读完后立即释放S锁,可以避免脏读。但有可能出现不可重复读与幻读。二级封锁协议对应的是Read committed与Repeatable Read隔离级别。

 

      3) 三级封锁协议

三级封锁协议,在一级封锁协议的基础上(修改时加X锁),读数据时加上S锁(与二级类似),但是直到事务结束后才释放S锁,可以避免幻读,脏读与不可重复读。三级封锁协议对应的隔离级别是Serializable。

 

 

6. 锁机制

        1) 共享/排它锁(Shared and Exclusive Locks)

共享锁(Share Locks,记为S锁),读取数据时加S锁,共享锁之间不互斥,即读读可以并行。

排他锁(exclusive Locks,记为X锁),修改数据时加X锁,排他锁与任何锁互斥,即写读,写写不可以并行。

一旦写数据的任务没有完成,数据是不能被其他任务读取的,这对并发度有较大的影响。对应到数据库,可以理解为,写事务没有提交,读相关数据的select也会被阻塞,这里的select是指加了锁的,普通的select仍然可以读到数据(快照读)。

 

        2) 意向锁(Intention Locks)

InnoDB为了支持多粒度锁机制(multiple granularity locking),即允许行级锁与表级锁共存,而引入了意向锁(intention locks)。意向锁是指,未来的某个时刻,事务可能要加共享/排它锁了,先提前声明一个意向。

意向锁是一个表级别的锁(table-level locking);

意向锁又分为:

意向共享锁(intention shared lock, IS),它预示着,事务有意向对表中的某些行加共享S锁;

意向排它锁(intention exclusive lock, IX),它预示着,事务有意向对表中的某些行加排它X锁;

   加锁的语法为:

select ... lock in share mode;  要设置IS锁;

select ... for update;       要设置IX锁;

事务要获得某些行的S/X锁,必须先获得表对应的IS/IX锁,意向锁仅仅表明意向,意向锁之间相互兼容,兼容互斥表如下:

 

IS

IX

IS

IX

 

 

 

 

虽然意向锁之间互相兼容,但是它与共享锁/排它锁互斥,其兼容互斥表如下:

 

S

X

IS

IX

 

 

 

 

排它锁是很强的锁,不与其他类型的锁兼容。这其实很好理解,修改和删除某一行的时候,必须获得强锁,禁止这一行上的其他并发,以保障数据的一致性。

        3) 记录锁(Record Locks)-----锁定某行

对单条索引记录进行加锁,锁住的是索引记录而非记录本身,即使表中没有任何索引,MySQL会自动创建一个隐式的row_id作为聚集索引来进行加锁。记录锁就是为某行记录加锁,它封锁该行的索引记录。查询条件必须为主键列或唯一索引,否则锁会变成临建锁。同时查询语句为精准匹配(=),不能为>、<、like等,否则也会退化成临建锁。事务隔离级别是RR。

 

        4) 间隙锁(Gap Locks) -----锁定某个区间

间隙锁,它封锁索引记录中的一段间隔范围,或者第一条索引记录之前的范围,又或者最后一条索引记录之后的范围。

存储引擎是InnoDB,事务隔离级别是RR。

间隙锁的主要目的,就是为了防止其他事务在间隔中插入数据,以导致“不可重复读”。如果把事务的隔离级别降级为已提交读(Read Committed, RC),间隙锁则会自动失效。例如:

Select * from test where id between 1 and 8 for update;

SQL语句会封锁区间(1,8),以阻止其他事务插入id位于该区间的记录。

 

        5) 临键锁(Next-key Locks) -----锁定左开右闭的一段区间

临键锁,是记录锁与间隙锁的组合,它的封锁范围,既包含索引记录,又包含索引区间。

临键锁的主要目的,也是为了避免幻读。

如果把事务的隔离级别降级为RC,临键锁就也会失效。

每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事务持有该数据行的临键锁时,会锁住一段左开右闭区间的数据。需要强调的一点是,InnoDB 中行级锁是基于索引实现的,临键锁只与非唯一索引列有关,在唯一索引列(包括主键列)上不存在临键锁。

假设有如下表:
MySql,InnoDB,Repeatable-Read:table(id PK, age KEY, name)

id

age

name

1

10

Lee

3

24

Soraka

5

32

Zed

7

45

Talon

该表中 age 列潜在的临键锁有:
(-∞, 10],
(10, 24],
(24, 32],
(32, 45],
(45, +∞],

 

在根据非唯一索引 对记录行进行 UPDATE \ FOR UPDATE \ LOCK IN SHARE MODE 操作时,InnoDB 会获取该记录行的 临键锁 ,并同时获取该记录行下一个区间的间隙锁。

 

InnoDB 中的行锁的实现依赖于索引,一旦某个加锁操作没有使用到索引,那么该锁就会退化为表锁。

记录锁存在于包括主键索引在内的唯一索引中,锁定单条索引记录。

间隙锁存在于非唯一索引中,锁定开区间范围内的一段间隔,它是基于临键锁实现的。

临键锁存在于非唯一索引中,该类型的每条记录的索引上都存在这种锁,它是一种特殊的间隙锁,锁定一段左开右闭的索引区间。

 

      6) 插入意向锁(Insert Intention Locks)

插入意向锁,是间隙锁(Gap Locks)的一种(所以,也是实施在索引上的),它是专门针对insert操作的。多个事务,在同一个索引,同一个范围区间插入记录时,如果插入的位置不冲突,不会阻塞彼此。

目的是提高插入并发。

       7) 自增锁(Auto-inc Locks)

自增锁是一种特殊的表级别锁(table-level lock),专门针对事务插入AUTO_INCREMENT类型的列。如果一个事务正在往表中插入记录,所有其他事务的插入必须等待,以便第一个事务插入的行,是连续的主键值。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

上善亦若水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值