T-SqlMYSQL

MySQL技术内幕-InnoDB存储引擎(第6章)

2019-04-21  本文已影响30人  Fix12138

第6章 锁

什么是锁

Lock与Latch

latch一般称为轻量级锁,因为其要求锁定的时间必须非常短。用来宝成并发线程操作临界资源的正确性,并且通常没有死锁检测机制,在InnoDB中又可分为:

lock的对象是事务,用来锁定数据库中的对象:表、页、行。有死锁机制。下面主要讲的是lock

InnoDB存储引擎中的锁

锁的类型

两种标准的 级锁:

锁兼容:多个事务可以获得同一个行的共享锁。其他情况锁都不兼容

意向锁(表级别的锁),设计目的是在一个事务中揭示下一行将被请求的锁类型,支持两种:

由于InnoDB存储引擎支持的是行级别的锁,因此意向锁其实不会阻塞除全表扫描以外的任何请求。

介绍InnoDB提供的数据使用方法,可以对当前事务运行与获取锁的情况进行分析。

一致性非锁定读

快照读,读取该行之前版本的数据,通过undo段来完成。
一个行记录可能有不止一个快照数据,一般称这种技术为:行多版本技术。由此带来的并发控制,称之为多版本并发控制(Multi Version Concurrency Control, MVCC)。

一致性非锁定读(快照读)在第2和第3个隔离级别下使用,但读取的版本不同:

一致性锁定读

InnoDB存储引擎对SELECT语句支持两种一致性的锁定读(locking read)操作:

上面两个操作必须在事务当中

自增长与锁

自增长语句有四种类型:

自增长锁的模式:

对于上述中主从复制的影响,考虑是因为内存中对互斥量的获取顺序是不能确定的,最终的id值也就没法确定,所以只能基于行来复制。

外键和锁

对于外键值得插入或更新,首先要查询父表中的记录,只能采用一致性锁定读,加S锁,来保证与父表的数据一致性。

锁的算法

行锁的3中算法

InnoDB存储引擎有3中行锁的算法:

采用Next-Key Lock的锁定技术称为Next-Key Locking。设计的目的是为了解决Phantom Problem(幻读)。称作Next-Key的原因是锁定的区间类似(],左开右闭。对应的也有Previous-Key Locking(左闭右开)。

当查询的索引含有唯一属性时(仅单列唯一索引的情况,多列的话仍是范围),Next-Key Lock可以优化降级为Record Lock,仅锁住索引本身。

从文中的例子可以看到,Gap Lock的作用阻止多个事务将记录插入到同一个范围内,而这会导致Phantom Problem问题。

解决Phantom Problem

Phantom Problem是指在同一事务下,连续执行两次同样的SQL语句可能导致不同的结果,第二次的SQL语句可能返回之前不存在的行。
InnoDB存储引擎采用Next-Key Locking来避免Phantom Problem,但必须显式的加锁,否则使用快读,不会加Next-Key Lock,例如:
SELECT * FROM t WHERE a>2 <u>FOR UPDATE</u>

用户可以通过InnoDB存储引擎的Next-Key Locking机制在应用层面实现唯一性检测 这里没看明白

锁问题

锁提高了并发,却会带来问题。但因为事务隔离性的要求,锁只会带来三种问题。

脏读

在事务隔离级别为READ UNCOMMITTED时,当前事务读取到其他事务未提交的修改。

不可重复读

在一个事务内读取同一数据集合,在这个事务还没结束时,另外一个事务也访问了该同一个数据集合,并做了DML(Data Manipulatin Language,数据库操纵语言)操作。由于第二个数据的修改,导致第一个事务两次读取到的数据可能是不一样的,这种情况称为不可重复读。
与脏读相比,不可重复读是读取了其他事务已提交的数据
与幻读相比,从书中P270和P274的描述中,指不可重复读和Phantom Problem是相同的问题。

丢失更新

一个事务的更新操作被另一个事务的更新操作所覆盖,从而导致数据的不一致。但目前数据库不存在这个问题。本节主要描述应用系统中的更新丢失。

为了避免应用系统中更新丢失的问题,应该在事务开始时就对SELECT的数据加排他锁,例如:
SELECT * FROM account WHERE user=pUser <u>FOR UPDATE</u>

阻塞

由于不同锁之间的兼容性关系,在有些时刻一个事务中的锁需要等待另一个事务中的锁释放它所占用的资源,这就是阻塞。
阻塞时间超时,默认配置InnoDB不会进行回滚(大部分异常,死锁除外)。因此用户必须判断是是否需要COMMIT还是ROLLBASCK

死锁

死锁是指两个或两个以上的事务在执行过程中,因争夺资源而造成的一种相互等待的现象。

InnoDB采用wait-for graph(等待图)的方式来主动检查死锁,wait-for graph要求数据库保存以下两种信息:

使用深度优先算法检测是否有环,InnoDB1.2版本之前采用递归实现,1.2版开始采用非递归实现。

判断有向图存在环:
方法一:遍历删除入度为0的节点,如果还有剩余节点,则说明有环(用循环O(n^2),用栈O(n+e))
方法二:DFS
判断有向图是否有环
Detect Cycle in a Directed Graph

死锁概率

发生概率和以下三点因素有关:

死锁的示例

这里介绍了另一个中死锁,和AB-BA死锁不同,死锁发生时处理方式也不同,这种情况会滚undo log记录大的事务

锁升级

锁升级(Lock Escalation)是指将当前锁的粒度降低:
行锁->页锁->表锁
InnoDB存储引擎不存在锁升级的问题。因为其不是根据每个记录来产生行锁的,相反,其根据每个事务访问的每个页对锁进行管理的,采用位图的方式。因此不管一个事务锁住页中一个记录还是多个记录,其开销通常都是一致的。

第七章 事务

认识事务

概述

ACID

分类

从事务理论的角度来说,可以把事务分为以下几种类型:

事务的实现

隔离性:由锁来实现
原子性和持久性:redo log(重做日志)
一致性:undo log

redo:恢复提交事务修改的页操作,通常是物理日志,记录的是页的物理修改操作。基本上都是顺序写

undo:回滚行记录到某个特定版本,是逻辑日志,根据每行记录进行记录。需要进行随机读写

redo

基本概念

redo由两部分组成:

log block(日志块)

在InnoDB引擎中,重做日志缓存、重做日志文件都是以512字节进行存储的,称之为重做日志块(redo log block)。

log group(重做日志组)

InnoDB存储引擎实际上只有一个log group。

redo log 重做日志格式

到InnoDB1.2版本时,一共有51种重做日志类型。

LSN(Log Sequence Number)

表示日志序列号,在InnoDB存储引擎中,LSN占用8字节,并且单调递增。LSN表示的含义有:

恢复

不管上次数据库运行时是否正常关闭,都会尝试进行恢复。由于重做日志时物理日志,因此其是幂等的。

undo

基本概念

redo存放在重做日志文件中。
undo存放在数据库内部的一个特殊段(segment)中,这个段称为undo段(undo segment)。undo段位于共享表空间内。
unodo是逻辑日志:当用户执行ROLLBACK时,对于事务中每个INSERT,InnoDB存储引擎会完成一个DELETE;对于每个DELETE,InnoDB存储引擎会执行一个INSERT;对于每个UPDATE,InnoDB存储引擎会执行一个相反的UPDATE,将修改前的行放回去。

MVCC:
出了回滚操作,undo的另一个作用是MVCC,即在InnoDB存储引擎中MVCC的实现是通过undo来完成的。当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以通过undo读取之前行版本信息,以此实现非锁定读取。

undo log会产生redo log,也就是undo log的产生会伴随着redo log的产生,这时因为undo log也需要持久性的保护。

上一篇下一篇

猜你喜欢

热点阅读