Log[数据库_锁]_20.07.04
锁
锁机制用来管理对共享文件的并发访问。InnoDB会在数据库内部多个地方使用锁,从而允许对多种不同资源提供并发访问。
1.InnoDB存储引擎中的锁
1.1 锁的类型
InnoDB实现了如下两种标准的行级锁:
共享锁(读锁):允许事务读一行数据
排他锁(写锁):允许事务删除或更新一行数据
InnoDB引擎支持多粒度的锁定,允许行级锁和表级锁同时存在。为了支持在不同的粒度上进行加锁,InnoDB支持一种叫意向锁的加锁方式,并且在此引擎上意向锁的锁粒度是表级。
个人理解的意向锁的应用场景:事务A想给表1所有行加排他锁,必然要先给表1加意向排他锁,而此时如果检测到事务B给表1加上了意向共享锁,说明表1中某行或者几行记录被加了共享锁。此时就进行等待,等事务B对表1的操作执行完毕撤销共享锁再由事务A加排他锁。意向锁的好处凸显在这个地方:意向共享锁昭示了表中有共享锁的存在,如果没有这个提示,事务A向全表加排他锁而表中某行还有共享锁存在,就发生了冲突。(当然这种情景建立在 有一个事务对表的操作是全表扫描 的基础上)
1.2一致性非锁定读
一致性的非锁定读是指InnoDB存储引擎通过行多版本控制的方式来读取当前执行时间数据库中行的数据。如果此行正在进行别的操作,这时候读取操作不会进行等待,而是读取此行的一个快照数据。这个快照数据是指该行之前版本的数据,通过undo段来实现。此种技术称为多版本并发控制(MVCC)
MVCC原理:
主要用来解决读写冲突。关键字:隐式字段、undo日志、Read View
① 先来认识两个概念:
当前读:读取记录的最新版本,且读取时还要保证其他并发事务都不能修改当前记录,对读取的记录进行加锁
快照读:不加锁的select就是快照读,读到的数据是之前的历史版本,不需要进行加锁操作。前提是隔离级别不是串行级别。读提交级别的快照读是每次select都生成一个快照读;可重复读级别的快照读是开启事务后第一个select语句进行快照读。
② 隐式字段:{最近修改事务ID记录创建这条记录/最后一次修改这条记录的事务的ID,回滚指针指向这条记录的上一个版本,如果表没有主键自动产生一个隐含的自增ID(隐含主键)}
③ undo日志
-
insert undo log:代表事务在insert新记录时产生的undo log,只在事务回滚时需要,并且在事务提交后可以被立即丢弃
-
update undo log:事务进行update或delete时产生的undo log,不仅在事务回滚时需要,在快照读时也需要
有了这些背景,就可以还原以下对记录进行操作的流程:事务A要改写a记录的某字段,先把这行记录加写锁,拷贝到undo log里,然后修改数据,修改完毕后隐式字段的事务ID就是A,回滚指针指向undo log里那一行数据
④ Read View
事务进行快照读操作时产生的读视图。在该事务执行快照读的那一刻,生成数据库当前的一个快照,记录并维护系统当前活跃事务的ID(最新的事务ID值最大)。
所以Read View主要用来做可见性判断,根据其他事务的活跃情况,判断当前执行快照读的事务能看到的数据究竟是哪个版本的数据。
可见性算法:将当前执行快照读的事务ID取出来,与系统中其他活跃的事务ID(由Read View维护)去对比。直到找到符合条件的。那么到底这个条件是什么呢?
先将Read View主要属性进行摘取:一个记录当前活跃的事务的数值列表、一个属性记录列表中最小的事务ID,一个属性记录最大的事务ID+1。执行快照读时隐式字段里记录的事务ID,要不小于最小事务ID,不大于最大事务ID+1,还不能属于当前活跃的列表里。则符合可见性,即得出结论:某事务执行快照读时,可以看到隐式字段里记录的事务执行过的操作。
1.3 一致性锁定读
有两种一致性锁定读的语句:
①SELECT … FROM UPDATE 对记录加X锁,其他事务不能加任何锁
②SELECT … LOCK IN SHARE MODE 对事务加S锁,其他事务可加S锁不可加X锁
2.锁问题
2.1 脏读
脏数据是指事务对缓冲池中行记录进行修改,并且还没有提交,如果另一个事务读取到了这个数据,就称之为脏读。脏读的解决办法是将隔离级别提升到读提交。
2.2 不可重复读
是指一个事务多次执行select语句时,因为中间有别的事务对数据进行了改变,导致查询到的数据不一样。不可重复读的解决方法是将隔离级别提升至可重复读。
与它有点相似的问题叫幻读,主要是中间事务做的操作说新增而不是修改。
3.死锁
3.1 死锁概念
两个或两个以上的事务在执行过程中,因争夺锁资源而造成一种互相等待的现象。
解决死锁最简单的办法是超时:当一个事务的等待时间超过某个阈值时,事务进行回滚,这样其他事务就有可能推进下去了。
另一个方法是建立等待图,等待图需要两个信息:锁的信息链表和事务等待链表,通过这两个链表构造一张图,如果图有回路,代表有死锁,此时InnoDB选择回滚undo量最小的事务。
3.2 一个数据库死锁例子
参考资料: