常州个人网站建设,vps 安装 wordpress,花都建站,做外贸一般用什么网站文章目录lock与latch锁的类型MVCC一致性非锁定读#xff08;快照读#xff09;一致性锁定读#xff08;当前读#xff09;锁算法死锁锁升级lock与latch
在了解数据库锁之前#xff0c;首先就要区分开 lock 和 latch。在数据库中#xff0c;lock 和 latch 虽然都是锁快照读一致性锁定读当前读锁算法死锁锁升级lock与latch
在了解数据库锁之前首先就要区分开 lock 和 latch。在数据库中lock 和 latch 虽然都是锁却有着截然不同的含义。 latch 通常被我们称为闩锁(轻量级锁)因为其要求锁定的时间必须非常短。在 InnoDB 中latch 可以分为 mutex(互斥锁) 和 rwlock(读写锁) 它的作用是用来保证并发线程操作临界资源的正确性并且通常没有死锁检测机制。 lock 的操作对象则是事务用来锁定数据库中的对象如表、页、行等一般 lock 的对象仅在事务提交或者回滚后释放lock 有死锁检测机制。
关于数据库页结构的详细内容可以看这篇博客 锁的类型
在 InnoDB存储引擎 中实现了下面两种标准的行级锁
共享锁S Lock) 允许事务读一行数据排他锁X Lock) 允许事务删除或者更新一行数据。
由于共享锁并不涉及到数据的修改所以即使一个事务已经获得了某行的共享锁另外的事务也可以立即获得该行的共享锁这种情况又被称为锁兼容。
对于排他锁又是另一种情况由于排他锁涉及到了数据的修改为了保证安全其他的事务想要获得同一行的排他锁时必须要等到前一个事务释放锁才行这种情况又被称为锁不兼容。
下面是排他锁和共享锁的兼容性
由于 InnoDB 支持多粒度锁定所以允许事务可以同时存在行锁和表锁为了支持在不同粒度上进行加锁操作InnoDB 支持一种额外的锁方式即 意向锁Intention Lock。意向锁即将锁定的对象分为多个层次意味着希望事务在更细粒度上进行加锁。
如果我们想对下层的对象如记录【一行就是一个记录】上一个 X锁 就需要先对粒度更粗的上层对象上锁需要分别先对数据库、表、页上 意向排他锁IX锁 再对记录上 X锁 。如果没有意向锁我们对一行上了读锁之后假如某个事务申请了一个表级的写锁此时这个事务就会对我们上锁的数据进行修改。
PS读/写锁 是 共享/排他锁 的一种。 InnoDB 支持意向锁设计比较简练其意向锁即为表级别的锁意向锁主要是为了在一个事务中揭示下一行将被请求的锁的类型两种意向锁分别如下
意向共享锁IS Lock事务要想获得某一张表中某几行的共享锁。意向排他锁IX Lock事务要想获得某一张表中某几行的排他锁。
由于 InnoDB 支持的是行级别的锁因此意向锁不会阻塞 除全表扫描外 的任何请求故兼容性如下: MVCC
MVCCMulti-Version Concurrency Control即多版本并发控制是数据库并发控制的一种方法
一致性非锁定读快照读
如果我们读取的行正在执行 DELETE 或者 UPDATE 操作这时就不会去等待锁释放后再读取而是直接去读取行的一个快照数据如下图所示
快照数据指的是该行之前版本的数据这些数据存储在 undo段 中由于 undo 用来在事务中回滚数据因此这些快照数据本身并没有额外的开销。并且我们只是读操作并不涉及修改而且也没有事务会去对历史数据进行修改所以在读取快照数据的时候不需要进行加锁。
快照读机制让读取操作不再占用和等待表上的锁极大的提高了数据库的性能。但由于每个快照都相当于是一个历史版本行的多版本需要并发控制—— MVCC 。
需要注意的是MVCC 只在 READ COMMITTED读已提交 和 REPEATABLE READ可重复读两个隔离级别下工作。由于 READ UNCOMMITTED读未提交 总会读取最新的数据而 SERIALIZABLE可串行化 会对所有读取的行加锁所以这两种都不兼容 MVCC 。
在 RC 隔离级别下是每个快照读都会生成并获取最新的 Read View也就是同一个事务中每次快照读的结果都不一样在 RR 隔离级别下则是同一个事务中的第一个快照读才会创建 Read View, 之后的快照读获取的都是同一个 Read View 也就是同一个事务中每次快照读的结果都跟第一次快照读一样。 当前读快照读和MVCC的关系 MVCC 多版本并发控制是 「维持一个数据的多个版本使得读写操作没有冲突」 的概念只是一个抽象概念并非实现。MVCC 只是一个抽象概念MySQL 需要提供具体的功能去实现它「快照读就是 MySQL 实现了 MVCC 理想模型的其中一个功能——非阻塞读」。而相对而言当前读就是悲观锁的具体功能实现。要说的再细致一些快照读本身也是一个抽象概念再深入研究。MVCC 模型在 MySQL 中的具体实现则是由 3 个隐式字段、undo 日志 、Read View 等去完成的。
更多MVCC知识可以阅读这篇博客 一致性锁定读当前读
一致性锁定读又称为当前读读取的是行的最新版本并且读取的时候为了防止其他事务修改当前行还会对当前行进行加锁。
在 InnoDB 中对于 SELECT语句 支持以下两种一致性锁定读操作
SELECT…FOR UPDATE(排他锁) 会对读取的行加一个排他锁此时其他事务不能对该行上任何锁。SELECT…LOCK IN SHARE MODE(共享锁) 会位读取的行加上一个共享锁此时其余的事务可以对该行加上共享锁但是如果想加排他锁则会被阻塞。 锁算法
在 InnoDB 中有三种行锁的算法
Record Lock记录锁 单个行记录上的锁。Gap Lock间隙锁 锁定一个范围但不包含记录本身。Next-Key Lock下一键锁 前两种锁的结合既锁定一个范围也锁定记录本身。
举例
有一组数据其索引分别为 10、30、60 此时使用 SQL 语句 SELECT * FROM t WHERE id 10 FOR UPDATE 三种锁的范围如下
Record 对10单行进行加锁Gap Lock (-∞ 10)、(10, 30)、(30 60)、(60 ∞)Next-Key Lock (-∞10]、(1030]、(3060]、(60∞)
对于 Record Lockl 来说其总是会去锁住索引记录即使没有设置任何一个索引它也会使用隐式的索引进行锁定。
Next-Key Lock 是 结合了前面所说的两种锁算法既锁住范围也锁住记录本身在 InnoDB 中对于行的查询都会采用这种算法而设计它的目的正是为了解决幻读问题。
幻读指 在同一事务中用同样的操作读取两次得到的记录数却不一样针对同一个范围的数据。 主要原因就是当第一个事务对表中的所有数据行进行修改同时第二个事务向表中插入了一行。这样也就导致了操作第一个事务的用户发现表中还有没修改的数据行像发生了幻觉一样。 明明在 会话A 的第一次查询中大于 2 的数只有行只有一行而由于 会话B 插入了新行后对于 会话A 而言就凭空多出来了一行像出现了幻觉一样。
对于以上数据Next-Key Locking算法 在 SELECT * FROM t WHERE a 2 FOR UPDATE 这条语句中锁住的不仅仅是 5 这个数值而是对直接对[2∞ 这个范围加了排他锁所以任何对于这个范围的插入都不能进行也就避免了幻读现象的发生。
同理间隙锁 Gap Lock 也是锁定某个范围所以它也能防止幻读的出现。
InnoDB 正是借助 锁(Gap Lock、Next-Key Lock) 以及 MVCC(快照读) 这两个机制实现了事务的隔离性。 死锁
死锁指的是两个或者两个以上的事务在执行过程中因为争抢所资源而导致的一种互相等待的现象。在死锁的情况下如果没有外力作用事务将永远无法推进下去。
在数据库中通常都会使用超时机制来解决死锁为事务设置超时时间当其中一方超时后立刻进行回滚另一个事务就能够继续进行了。
虽然超时机制可以解决这个问题但是我们并不能掌握回滚的事务的量级倘若事务更新庞大则回滚就会带来大量的性能损耗所以我们通常会采用更加主动的策略即使用等待图来进行死锁检测 图中每个节点即为一个事务。每条指向其他节点的线则代表着正在等待该节点的资源。当存在回路时则代表着事务互相等待此时就意味着存在死锁。
每当事务请求锁并发生等待时都会主动判断等待图中是否存在回路如果存在则代表着有死锁产生此时就会主动选择 undo量最小的事务 来打破死锁。在现版本的 InnoDB 中通常采用 深度优先搜索(老版本使用递归) 来检测死锁的存在。 锁升级
在数据库为了保证安全大量的并发下必定存在着大量的锁但锁是一种稀有资源为了避免大量锁的开销数据库中存在着锁升级的机制。
锁升级指的是将当前的锁升级为更粗粒度的锁 例如我们可以将多个行锁升级为一个页锁又或者将多个页锁升级为一个表锁。这种升级减少了锁的数量、保护了系统资源防止系统使用太多内存来维护大量的锁在一定程度上提高了效率。
在 SQL Server 中锁升级是很常见的现象当满足以下条件中其中一个时则会进行锁升级
锁资源占用的内存超过了激活内存的 40%一条单独的 SQL语句 在一个对象上持有的锁数量超过了阈值阈值默认为 5000
而在 MySQL 的 InnoDB 中则不存在锁升级的问题。其根据每个事务访问的每个页对锁进行管理并且使用位图来标记所以一个事务无论锁住页中多少条记录开销都相同。