mysql隔离级别实现原理

隔离级别的实现:

未提交读(RU:read-uncommitted):在RU级别中,事务读到的所有数据都是最新的数据,可能是事务提交后的数据,也可能是事务执行中的数据(可能会被回滚)。

当隔离级别为RU时:

  • 所有的读不加锁,读到的数据都是最新的数据,性能最好。
  • 所有的写加行级锁,写完释放。

提交读(RC:read-committed):使用MVCC技术,在每一行加入隐藏的字段(DB_TRX_ID:修改该行的最后一个事务的id,DB_ROLL_PTR:指向当前行的undo log日志,DB_ROW_ID:行标识,DELETE_BIT:删除标志),它实现了不加锁的读操作。

当隔离级别为RC时:

  • 写操作:加行级锁。事务开始后,会在UNDO日志中写入修改记录,数据行中的隐藏列DATA_POLL_PTR存储指向该行的UNDO记录的指针。
  • 读操作:不加锁。在读取时,如果该行被其它事务锁定,则顺着隐藏列DATA_POLL_PTR指针,找到上一个有效的历史记录(有效的记录:该记录对当前事务可见,且DELETE_BIT=0)。

可重复读(RR:repeatable-read):使用MVCC技术来实现不加锁的读操作。

当隔离级别为RR时:

  • 写操作:加行级锁。事务开始后,会在UNDO日志中写入修改记录,数据行中的隐藏列DATA_POLL_PTR存储指向该行的UNDO记录的指针。
  • 读操作:不加锁。在读取时,如果该行被其它事务锁定,则顺着隐藏列DATA_POLL_PTR指针,找到第一个有效的历史记录(有效的记录:该记录对当前事务可见,且DELETE_BIT=0)。

从上面可以知道:实际上RC和RR级别的操作基本相同,而不同之处在于:行记录对于当前事务的可见性(可见性:即哪个版本的行记录对这个事务是可见的)。RC级别对数据的可见性是该数据的最新记录,RR基本对数据的可见性是事务开始时,该数据的记录。

1) 行记录的可见性(read_view)的实现

在innodb中,创建一个事务的时候,会将当前系统中的活跃事务列表创建一个副本(read_view),里面存储着的都是在当前事务开始时,还没commit的事务,这些事务里的值对当前事务不可见。read_view中有两个关键值 up_limit_id(当前未提交事务的最小版本号-1,在up_limit_id之前的事务都已经提交,在up_limit_id之后的事务可能提交,可能还没提交) 和 low_limit_id(当前系统尚未分配的下一个事务id,也就是目前已出现过的事务id的最大值+1。注意:low_limit_id不是最大的活跃事务的id。)

注意:当前事务和正在commit的事务是不在read_view中的。

2)无论是RC级别还是RR级别,其判断行记录的可见性的逻辑是一样的。

当该事务要读取undo中的行记录时,会将行记录的版本号(DB_TRX_ID)与read_view进行比较:

1. 如果DB_TRX_ID小于up_limit_id,表示该行记录在当前事务开始前就已经提交了,并且DELETE_BIT=0,则该行记录对当前事务是可见的。

2. 如果DB_TRX_ID大于low_limit_id,表示该行记录在所在的事务在本次事务创建后才启动的,所以该行记录的当前值不可见。

3. 如果up_limit_id< = DB_TRX_ID 是否在活跃事务链中,如果在就是不可见,如果不在就是可见的。

4. 如果上面判断都是不可见的,则读取undo中该行记录的上一条行记录,继续进行判断。

而对于RC级别的语句级快照和RR级别的事务级快照的之间的区别,其实是由read_view生成的时机来实现的。RC级别在执行语句时,会先关闭原来的read_view,重新生成新的read_view。而RR级别的read_view则只在事务开始时创建的。所以RU级别每次获取到的都是最新的数据,而RR级别获取到的是事务开始时的数据。

3)值得注意的是: 在上面的可见性判断中,虽然逻辑是一样的,但是实际意义上是有区别的:

在第二步中,对于RC级别来说,low_limit_id是执行语句时已出现的最大事务id+1,可以认为在执行语句时,是不存在事务会比low_limit_id要大,所以大于low_limit_id的事务都是不可见的。而对于RR级别来说,low_limit_id是当前事务开始时已出现的最大事务+1(也可以认为是当前事务的id+1,因为在创建当前事务时,当前事务的id最大),大于low_limit_id的事务表示是在该事务开始后创建的,所以对RR级别是不可见。

在第三步中,对于RC级别来说,只要DB_TRX_ID不在活跃链表中,则无论DB_TRX_ID是否大于事务id,RC都是可见的。而对于RR级别来说,因为low_limit_id就是当前事务id+1,可以认为小于low_limit_id的事务都是在当前事务创建前出现的,所以也只需要简单判断DB_TRX_ID是否在活跃链表中。

串行化(serializable):读写都会加锁。

(四)Mysql中的锁

锁分类

Mysql为了解决事务并发,数据安全问题,使用了锁机制。

按照锁的粒度可以把锁分为表级锁和行级锁:

1) 表级锁

Mysql中粒度最大的一种锁,会锁住当前操作的整张表,并发性能低,但表锁的实现简单,耗费资源少,加锁快,不会出现死锁。

2) 行级锁

Mysql中粒度最小的一种锁,只会锁住当前操作的数据行。行锁极大地提高了Mysql的并发性能,但行锁的开销较大,速度较慢,会出现死锁。

按照锁的性质可以把锁分为共享锁和排它锁:

1) 共享锁(S锁)

其他事务可以读取被共享锁锁住的数据行,不能修改该数据行,并且也只能对该数据行加共享锁,而不能加排它锁。

2) 排它锁(X锁)

当一个事务对数据行加上排他锁,那么该事务可以读取和修改该数据行,而其他事务不允许对该数据行加任何锁。

表锁

Mysql中表锁除了共享锁和排他锁之外,还存在着两种锁:意向共享锁(IS),意向排他锁(IX)。

意向锁的作用是表明该事务想对该表加一个共享/排他锁,但并没有真正把锁加上去。比如,当事务想对一个被排他锁锁住的表加上共享锁/排他锁时,必须先在该表上添加一个意向共享锁/意向排他锁,直到锁住表的排他锁被释放。

事务在给一个数据行加共享锁前必须先取得该表的IS锁,在加排它锁前必须先取得该锁的IX锁。并且意向共享锁可以同时并存多个,但是意向排他锁同时只能有一个存在。

行锁

InnoDB行锁是通过给索引上的索引项加锁来实现的,这意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。

Innodb中的行级锁有以下几种:

1) Record Lock: 对索引项加锁,锁定符合条件的行。其他事务不能修改和删除加锁项;

2) Gap Lock: 对符合条件范围的“间隙”加锁,锁定记录的范围,不包含索引项本身。其他事务不能在锁范围内插入数据。“间隙(GAP)”是指 键值在条件范围内但并不存在的记录。

3) Next-key Lock: 锁定索引项本身和间隙。Record Lock和Gap Lock的结合,Next-key Lock就是我们所说的间隙锁,可解决幻读问题。

举例来说,假如emp表中只有101条记录,其empid的值分别是 1,2,...,100,101,下面的SQL:

Select * from  emp where empid > 100 for update;

是一个范围条件的检索,InnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁。

在使用范围条件检索并锁定记录时,InnoDB这种加锁机制会阻塞符合条件范围内键值的并发插入。

注意

1) InnoDB 存储引擎在 REPEATABLE-READ(可重读) 事务隔离级别下使用的是Next-Key Lock 锁,解决的幻读的问题,因此InnoDB 引擎的默认的隔离级别 REPEATABLE-READ(可重读) 已经达到了SQL标准的 SERIALIZABLE(可串行化) 隔离级别,并且事务不需要串行化。

2) 当查询的索引含有唯一属性时,将next-key lock降级为record key。

3) Innodb中行级锁是加在索引上,所以只有使用索引时,才会加行锁,否则只会加表锁。但这并不意味着,只要使用了索引就会加行级锁,如果MySQL认为全表扫描效率更高,比如对一些很小的表或者索引范围包括大部分表数据,它就不会使用索引,这种情况下InnoDB将使用表锁,而不是行锁。

死锁

 不同于MyISAM总是一次性获得所需的全部锁,InnoDB的锁是逐步获得的,当两个事务都需要获得对方持有的锁,导致双方都在等待,这就产生了死锁。 发生死锁后,InnoDB一般都可以检测到,并使一个事务释放锁回退,另一个则可以获取锁完成事务,我们可以采取以上方式避免死锁:

1) 通过表级锁来减少死锁产生的概率;

2) 多个程序尽量约定以相同的顺序访问表;

3 )同一个事务尽可能做到一次锁定所需要的所有资源。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值