MySql事务隔离级别

隔离级别介绍

事务4个特性,ACID。A原子性,C一致性,I隔离性,D持久性。
事务的隔离级别,我们从低往高讲,然后再解释高级的隔离级别如何解决低级别的问题。

read-uncommitted

首先最低级的是读未提交,(read-uncommitted)

读未提交的意思就是我在事务A中可以读取到事务B未提交的变更值。这样会导致的问题是,如果B最终未提交该事务,回滚了,那么A读取到B变更的值就是一个错误的值。 我们把这种现象叫做脏读。
在这里插入图片描述
为了解决这个问题,我们必须要读取已经提交的修改才行,不能读那些还没有提交的随时可能被回滚的脏数据。于是升级为读已提交(read-committed)

read-committed

读已提交就是会仅读取那些已经提交的变更,已经提交的变更都是有效的变更。所以不会出现读无效的脏数据的情况。但是随之而来的是另外一个问题,就是不可重复读的问题。意思很简单,就是A事务第一次读取变量X的值为100,第二次读取X的值变为200,这中途B事务提交了修改将X变为200.这种情况下,A事务对B事务有所感知,受到了B事务的影响,两次读到的值不一样,我们称之为不可重复读问题。

repeatable-read

repeatable-read,可重复读。为了解决读已经提交级别中的不可重复读问题,我们升级到了可重复读级别。然后存在的问题就是幻读了,幻读指的是通过Insert后新增了记录,两次相同的查询条件而查询到的记录数不一样。可重复读并不代表快照读操作完全不会受到其他已提交事务的影响,快照的建立是在第一次执行查询操作时开始。

serializable

所有操作都上排它锁。串行化执行。一般不会使用。

隔离级别例子简单介绍

锁介绍

在加锁的时候,不仅在索引上加了锁,还会在聚簇索引上加锁。

排它锁和共享锁

排他锁(eXclusive)也称X锁,可以通过select … for update,insert,delete操作加锁。
共享锁 (Share)也称S锁,可以通过 SELECT … LOCK IN SHARE MODE来加锁。
顾名思义,X锁和X,S都互斥,S锁和S锁不互斥。

意向锁(Intention Locks)

意向锁,字面上看来应该是表示打算加锁的一个前置操作。
由于存在行锁和表锁,意向锁的用途就是用来感知表锁和行锁之前的冲突问题。

Intention locks do not block anything except full table requests (for example, LOCK TABLES … WRITE). The main purpose of intention locks is to show that someone is locking a row, or going to lock a row in the table.
我们看看Mysql官方文档的说明。意向锁不阻塞任何东西除了表锁。意向锁的主要目的是为了展示这里有一个行锁或者有人打算在这个表上建立一个行锁。

那么再来看意向锁在什么情况下会建立。

在获取行共享之前,必须获取意向共享锁(IS)
在获取行排他锁之前,必须获取意向排他锁(IX)
那么也就是说,在上行锁之前,我们就获取了意向锁,当需要进行表锁之前,需要判断这个意向锁是否存在,来决定是否可以加表锁。

下面是官网给出的冲突。可见意向锁和意向锁之间不会冲突,X锁和所有锁冲突,IX锁与所有非意向锁冲突,IS锁与所有非X锁不冲突。
在这里插入图片描述

记录锁Record行锁

SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE。给该记录加了行的排他锁。加锁以后,哪怕这个记录不存在,也会给该索引加上锁,其他事务无法在该索引上执行插入或删除操作。

Gap锁(间隙锁)

gap锁指的是锁了一定范围,尽管这个范围内没有记录,仍然给这一段索引上锁。它存在于可重复读的隔离级别中,可以用来解决幻读问题。gap锁解决的是当前读的幻读问题,而不是快照读的幻读问题,快照读的幻读问题采用快照或者叫mvcc并发版本控制机制解决的,在InnoDB引擎下是基于undo log。

gap锁不仅会锁查询条件范围内的索引,还会将查询条件对应的记录的间隙加锁。

Next Key 锁

官网上表示这是一个行锁加上GAP锁。
对于唯一主键,如果针对这个索引记录执行select … for update 时会加行锁。但是对于非唯一主键来说,除了加行锁以外,还会在其索引记录的两侧范围加上gap锁。例如:

在这里插入图片描述
myid是key,但是不是唯一索引。当我执行select * from test_gap_lock where myid = 10 for update;时,不仅myId=10的记录上了锁,(5,10],(10,90)这两个区间都被上了gap锁,所以在另外一个事务中执行insert into test_gap_lock values(1111111,‘44’, 11);时会被阻塞。update,delete都会,但是insert语句不会加gap锁,只会加行锁record lock;

插入意向锁(insert intention lock)

在执行插入前先加上insert intention lock,这样当插入操作不会导致 duplicate-key错误时,就可以插入。

如果多个事务在同一个唯一索引上执行插入操作时,触发了 duplicate-key 错误时,其他事务都会获取该索引的S锁,当插入操作回滚时,这种情况会导致死锁。INSERT … ON DUPLICATE KEY UPDATE 这种Insert代替普通的insert时,在发生duplicate-key 错误时会获取X锁而不是S锁,可以解决这种冲突问题。

导致死锁

当两个或两个以上的事务,采用不同的顺序来抢多个锁,触发。
在这里插入图片描述

快照读与当前读(MVCC (Multi-Version Concurrency Control)

快照读(Consistent Nonlocking Reads)

在这里插入图片描述
每行记录有一个6字节的DB_TRX_ID,存储了最近更新此记录的事务ID。还有8字节的DB_ROLL_PTR指针,指向事务的undo log。 事务ID在执行了第一次select时(不管是读的哪个表)产生,是主键增长的。对于可重复读级别,当发现记录的事务ID大于当前事务ID,则会根据undo log获取小于等于当前事务ID的记录版本。对于读已提交级别,则一直会读取最新已提交数据。多版本管理不会存储多条记录,而是根据undo log来实现多版本共存的,不会产生额外的资源占用。

当前读( Locking Reads)

凡是需要获取共享锁和排他锁的操作,都不是读的快照,而是读的实时数据。包括update和Delete语句。如下图所示,查询到t1表中I=2有一条记录,但是update的时候没有,说明update语句的查询操作不是读取的快照,另外一个事务把I改为了不等于2的值。
在这里插入图片描述

隔离级别实现原理

可重复读

可重复读是通过快照的机制来实现的。当事务中的执行了第一条SQL时,这个快照就建立了。这里的读指的是快照读,区别于当前读。对于当前读,一定取的是最新值。

可重复读解决幻读问题

Mysql通过next-key锁的机制来解决了幻读问题。对于快照读,一定是没有幻读的,应该它读取的是当前事务的快照,不会受到其他事务的影响。然而如果是当前读,那么在读已提交(RC)隔离级别下,没有next-key锁机制的情况下,会出现什么情况呢。

比如现在有两条记录,key分别90和120,当用SELECT查询[90,120]这个区间时,会有两条记录,另外一个事务插入了key为101的记录,再执行相同的SELECT语句时,会出现三条记录。101这条记录就是幻读的记录。

而通过gap锁,不仅锁了记录,还锁了区间,这个区间不能再插入和更改,所以不会出现幻读。

参考文章:
http://hedengcheng.com/?p=771

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值