-
2022-03-23 15:54:40
两阶段锁协议
在数据库系统领域,并发控制机制主要有两种,即锁和多版本机制。
1.事务在加锁时有多种方式:
一次性锁协议
事务开始时,即一次性申请所有的锁,之后不会再申请任何锁,如果其中某个锁不可用,则整个申请就不成功,事务就不会执行,在事务尾端,一次性释放所有的锁。一次性锁协议不会产生死锁的问题,但事务的并发度不高。
两阶段锁协议
整个事务分为两个阶段,前一个阶段为加锁,后一个阶段为解锁。在加锁阶段,事务只能加锁,也可以操作数据,但不能解锁,直到事务释放第一个锁,就进入解锁阶段,此过程中事务只能解锁,也可以操作数据,不能再加锁。两阶段锁协议使得事务具有较高的并发度,因为解锁不必发生在事务结尾。它的不足是没有解决死锁的问题,因为它在加锁阶段没有顺序要求。如两个事务分别申请了A, B锁,接着又申请对方的锁,此时进入死锁状态。
树形协议
假设数据项的集合满足一个偏序关系,访问数据项必须按此偏序关系的先后进行。如di->dj,则要想访问dj,必须先访问di。这种偏序关系导出一个有向无环图(DAG),因此称为树形协议。树形协议的规则有:
树形协议只有独占锁;
事务T第一次加锁可以对任何数据项进行;
此后,事务T对数据项Q的加锁前提是持有Q的父亲数据项的锁;
对数据项的解锁可以随时进行;
数据项被事务T加锁并解锁之后,就不能再被事务T加锁。
树形协议的优点是并发度好,因为可以较早地解锁。并且没有死锁,因为其加锁都是顺序进行的。
缺点是对不需要访问的数据进行不必要的加锁。时间戳排序协议
每个事务都有一个唯一的时间戳,也就是其进入系统的时间。时间戳有大小之分,如果事务Ti比Tj先进入系统,则TS(Ti)<TS(Tj)。对于每个数据项Q,有两个时间戳与其绑定:一个是W-TS(Q),表示最近一次写数据项Q的事务的时间戳;一个是R-TS(Q),表示最近一次读数据项Q的事务的时间戳。Thomas协议是对时间戳排序协议的改进,具体内容如下:
若事务Ti发起一个write(Q),则
如果TS(Ti)<R-TS(Q),则表明Ti准备写的值还没来得及写入,Q就提前被读取了,所以Ti的write(Q)操作被拒绝,并且事务Ti被回滚。
如果TS(Ti)<W-TS(Q),表明Ti写的值已过期,比它更新的值已经写到Q上,所以Ti的write(Q)操作被拒绝。
剩下的情况,write(Q)操作被允许。事务在加锁时存在粒度的区别
如数据库锁,表锁,行锁,字段锁;页锁等。不同的数据库支持的锁粒度不同,BerkeleyDB支持页锁,即对数据项所在的内存页加锁。
2.多版本机制:
锁是针对集中式数据管理设计的,缺点是降低了事务的并发,并且锁本身有开销。在分布式系统,尤其是读多写少的系统中,采用多版本机制更合适。每个数据项都有多个副本,每个副本都有一个时间戳,根据多版本并发控制协议(MVCC)维护各个版本。
MVCC又称为乐观锁,它在读取数据项时,不加锁;在更新数据项时,直到最后要提交时,才会加锁。这与CAS(Compare and Swap)的机制很类似,为了提高并发度,它更新数据前,会将数据拷贝一份,进行一系列修改,并且拷贝的同时,会记录当前的版本号(时间戳),当修改完毕,即将提交时,再检查此时的版本号是否与刚才记录的一致,如果不一致,则表明数据项被其他事务修改,当前事务的修改被取消。否则,正式提交修改,并增加版本号。
与MVCC相对,基于锁的并发控制机制称为悲观锁,因为它认为其他事务修改自己正在使用的数据项的概率很高,因此对数据项加锁以阻塞其他事务的读和写。更多相关内容 -
Mysql的两阶段锁协议
2022-01-30 21:58:46mysql的事务为什么需要两阶段锁协议?两阶段锁协议是什么?在mysql优化中如何利用两阶段锁的特性提交mysql并发量?问题引入
我们都知道事务的四大特性,ACID,原子性、一致性、隔离性、持久性。那么事务是如何实现这样的特性的呢?其中原子性通过redo、undo、binlog日志来实现;持久性通过mysql底层的刷磁盘机制实现;隔离性通过MVCC多版本快照读实现;但是数据的一致性如何实现呢?通过什么样的锁机制?如何把一个事务中的多个写操作放到一个锁中呢?每个写操作的锁是如何加的?如何释放的?
此篇博客主要是讲述MySql(仅限innodb)的两阶段加锁(2PL)协议,而非两阶段提交(2PC)协议,区别如下:
2PL,两阶段加锁协议:指的是单机事务中的加锁机制。
2PC,两阶段提交协议:主要用于分布式事务中,通过对每个数据库事务之间的协调来达到分布式事务效果。
什么时候会加锁
在 select for update、lock in share model 时,update, delete 等操作时 ,会对记录加锁(有共享锁、排它锁、意向锁、gap锁、nextkey锁等等),本文为了简单考虑,不考虑锁的种类。
什么是两阶段加锁-2PL
在一个事务里面,分为加锁(lock)阶段和解锁(unlock)阶段,也即所有的lock操作都在unlock操作之前,如下图所示:
工程实践中的两阶段加锁-S2PL
在实际情况下,SQL是千变万化、条数不定的,数据库很难在事务中判定什么是加锁阶段,什么是解锁阶段。于是引入了S2PL(Strict-2PL),即:
在事务中只有提交(commit)或者回滚(rollback)时才是解锁阶段,其余时间为加锁阶段。
如下图所示:
这样的话,在实际的数据库中就很容易实现了。了解了两阶段加锁协议,我们可以通过这个理论进行一些优化,主要就是通过缩短加锁时间来提高Mysql的并发量。
比如:在一个事务中,将不加锁的普通查询写在前面,将需要加锁的操作写在后面;将热点数据的加锁放在事务的最后;合并SQL等。
1、缩短热点数据的加锁时间
上面很好的解释了两阶段加锁,现在我们分析下其对性能的影响。考虑下面两种不同的扣减库存的方案:
方案1:begin; // 扣减库存update t_inventory set count=count-5 where id=${id} and count >= 5;
// 锁住用户账户表select * from t_user_account where user_id=123 for update;
// 插入订单记录insert into t_trans;commit;
方案2:begin;
// 锁住用户账户表select * from t_user_account where user_id=123 for update;
// 插入订单记录insert into t_trans;
// 扣减库存update t_inventory set count=count-5 where id=${id} and count >= 5;commit;
由于在同一个事务之内,这几条对数据库的操作应该是等价的。但在两阶段加锁下的性能确是有比较大的差距。两者方案的时序如下图所示:
由于库存往往是最重要的热点,是整个系统的瓶颈。那么如果采用第二种方案的话, tps应该理论上能够提升3rt/rt=3倍。这还仅仅是业务就只有三条SQL的情况下,多一条sql就多一次rt,就多一倍的时间。
从上面的例子中,可以看出,需要把最热点的记录, 放到事务最后,这样可以显著的提高吞吐量。更进一步: 越热点记录离事务的终点越近(无论是commit还是rollback)
注意:避免死锁
上面我们说了通过调整加锁顺序可以提高Mysql数据库并发量。但是一旦涉及到调整加锁顺序就要注意一件事情:死锁。因为死锁的形成条件就是加锁顺序不一致导致的,所以我们要统一按照调整之后的顺序进行加锁。死锁的形成条件见下图:
2、SQL合并
我们可以直接将一些简单的判断逻辑写到update的谓词里面,以减少加锁时间,考虑下面两种方案:
方案1:
begin: int count = select count from t_inventory for update; if count >= 5: update t_inventory set count = count - 5 where id = 123; commit; else rollback;
方案2:
begin: int rows = update t_inventory set count = count-5 where id = 123 and count >= 5 if rows > 0: commit; else: rollback;
时延如下图所示:
可以看到,通过在update中加谓词计算,少了1rt的时间。由于update在执行过程中对符合谓词条件的记录加的是和select for update一致的排它锁
(具体的锁类型较为复杂,不在这里描述),所以两者效果一样。
-
MySQL 两阶段锁协议
2019-10-08 10:56:05MySQL 两阶段锁协议 在InnoDB事务中,行锁是需要的时候才加上的,但并不是不需要立马释放,而是等事务结束后释放,这个就是--------两阶段协议. 两阶段锁协议引起的死锁 事务A在等事务B释放Id = 2 的锁, 事务B在等事务... -
mysql中行锁、两阶段锁协议、死锁以及死锁检测
2020-09-28 15:31:45行锁 MySQL的行锁都是在引擎层实现的,但是 MyISAM 不支持行锁,意味着并发控制只能使用表锁,同一张表任何时刻只能被一个更新在执行,影响到业务并发度。InnoDB 是支持行锁的,这也是 MyISAM...两阶段锁 (Two-Phase参考:https://www.phpmianshi.com/?id=175
行锁
MySQL的行锁都是在引擎层实现的,但是 MyISAM 不支持行锁,意味着并发控制只能使用表锁,同一张表任何时刻只能被一个更新在执行,影响到业务并发度。InnoDB 是支持行锁的,这也是 MyISAM 被 InnoDB 替换的重要原因之一。
行锁就是针对数据库中表的行记录的锁,这很好理解,比如事务 A 更新了一行,而这时候,事务 B 也要更新一行,则必须等事务 A 的操作完成后才能更新。
两阶段锁 (Two-Phase Locking――2PL)
在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,需要等事务结束时才释放,这就是两阶段锁协议,分为加锁阶段和解锁阶段,所有的 lock 操作都在 unlock 操作之后。
两段锁协议规定所有的事务应遵守的规则:
① 在对任何数据进行读、写操作之前,首先要申请并获得对该数据的封锁。
② 在释放一个封锁之后,事务不再申请和获得其它任何封锁。
即事务的执行分为两个阶段:
第一阶段是获得封锁的阶段,称为扩展阶段。
第二阶段是释放封锁的阶段,称为收缩阶段。遵循两段锁协议的事务有可能发生死锁。
此时事务T1 、T2同时处于扩展阶段,两个事务都坚持请求加锁对方已经占有的数据,导致死锁。
为此,又有了一次封锁法。一次封锁法要求事务必须一次性将所有要使用的数据全部加锁,否则就不能继续执行。因此,一次封锁法遵守两段锁协议,但两段锁并不要求事务必须一次性将所有要使用的数据全部加锁,这一点与一次性封锁不同,这就是遵守两段锁协议仍可能发生死锁的原因所在。死锁
如下图所示,事务 A 在等待事务 B 释放 id = 2 的行锁,而事务 B 在等待 事务 A 释放 id = 1 的行锁,事务 A 和事务 B 在互相等待对方的资源释放,就是进入了死锁状态。
在并发系统中,不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程进入无限等待的状态,成为死锁。
当进入死锁状态时,有下列 2 种策略:
-
通过 innodb_lock_wait_timeout 来设置超时时间,InnoDB 中默认值是 50s,第一个被锁住的事务 A 等待超过 50s 才会超时退出,其他事务才能得以执行,对于在线服务来说,这个等待时间往往是无法接受的。如果设置太短 1s,可能有的事务只是简单的锁等待,就被退出了,会出现很多误伤。
-
通过设置 innodb_deadlock_detect = on,发起死锁检测,发现死锁之后主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。比如回滚事务 A,让事务 B 继续执行。
死锁检测
正常情况下使用第 2 种策略的,但是快速发现死锁并进行处理,也是有额外负担的。你可以想象一下这个发现死锁的过程:每当一个事务被锁的时候,就要看看他依赖的线程有没有被别人锁住,判断是否出现了循环等待,也就是死锁。
假设有 1000 个并发线程,都要同时更新同一行,第 1 个线程来的时候检测数是 0;第 2 个线程来的时候,需要检测【线程1】有没有被别人锁住;第 3 个线程来的时候,需要检测【线程1,线程2】有没有被其他线程锁住,以此类推,第 n 个线程来的时候,检测数是 n - 1,所以总的检测数是 0 + 1 + 2 + 3 + 。。。+ (n - 1) = n(n -1)/2,所以时间复杂度应该是 O(n²)。
也就是 1000 个并发线程同时操作同一行,那么死锁检测操作就是 100 万这个量级的,虽然最终检测的结果是没有死锁,但是这期间要消耗大量的 CPU 资源,就会看到 CPU 利用率很高,但是每秒却执行不了几个事务。
那么怎么处理这种热点行更新导致的性能问题呢?
-
1、关掉死锁检测,等待超时
-
如果你能确定这个业务一定不会出现死锁,可以临时把死锁关掉,这种操作带有一定风险,因为业务设计的时候一般不会把死锁当成一个严重错误,毕竟出现死锁了,就回滚,然后通过业务重试一般就没有问题了,这是业务无损的,而关掉死锁检测意味着可能出现大量超时,这是业务有损的。
-
2、控制并发度
-
比如同一行最多只有 10 个线程在更新,这样死锁检测的成本很低,一个直接的想法就是在客户端做并发控制。可是如果客户端有 600 个,即使每个客户端控制到只有 5 个线程,汇总到数据库服务端以后,峰值并发数也有可能达到 3000。因此这个并发控制要在服务端,比如引入中间件来实现,在进入引擎之前排队。
-
3、热点key拆分
-
将一行改成逻辑上的多行来处理,比如影院的账户余额等于 10 行记录的值总和,这样每次给影院账户加金额的时候,随机选取其中一条记录来加,冲突概率变为原来的1/10,减少锁的等待个数,也就减少了死锁检测的 CPU 消耗。这个方案看上去是无损的,但是需要根据业务逻辑做详细设计。如果账户余额减少,比如退票,这个时候就要考虑当一部分行记录变为 0 的时候,代码要有特殊处理。
-
-
数据库两阶段锁的理解
2022-05-26 11:10:57两阶段锁协议的理解 -
两段锁协议
2021-11-03 22:25:54两段锁协议:是指所有的事务必须分两个阶段对数据项加锁和解锁。即事务分两个阶段,第一个阶段是获得封锁。事务可以获得任何数据项上的任何类型的锁,但是不能释放;第二阶段是释放封锁,事务可以释放任何数据项上的... -
MySQL 行锁、两阶段锁协议、死锁以及死锁检测
2019-05-16 20:46:24行锁 MySQL的行锁都是在引擎层实现的,但是 MyISAM 不支持...行锁就是针对数据库中表的行记录的锁,这很好理解,比如事务 A 更新了一行,而这时候,事务 B 也要更新一行,则必须等事务 A 的操作完成后才能更新。... -
两端锁协议
2021-10-14 11:38:50两段锁协议是指每个事务的执行可以分为两个阶段:生长阶段(加锁阶段)和衰退阶段(解锁阶段) 加锁阶段:在该阶段可以进行加锁操作。在对任何数据进行读操作之前要申请并获得S锁,在进行写操作之前要申请并获得X锁... -
数据库原理 两段锁协议
2020-03-14 18:26:08数据库管理系统通常使用两段锁协议来实现并发调度的可串行性,从而保证调度的正确性 1、两段锁协议 指事务必须分成两个阶段对数据进行加锁和解锁 在释放一个封锁以后,事务不在申请获得其它封锁 2、两段锁的含义 ... -
MySql两阶段加锁(2PL)协议剖析
2021-01-20 01:11:12前言本文主要是讲述MySql(仅限innodb)的两阶段加锁(2PL)协议,而非两阶段提交(2PC)协议,区别如下:2PL,两阶段加锁协议:主要用于单机事务中的一致性与隔离性。2PC,两阶段提交协议:主要用于分布式事务。MySql本身针对... -
分布式锁:两阶段提交协议(two phase commit protocol,2PC)
2016-03-14 21:55:29两阶段提交协议(two phase commit protocol,2PC)可以保证数据的强一致性,许多分布式关系型数据管理系统采用此协议来完成分布式事务。它是协调所有分布式原子事务参与者,并决定提交或取消(回滚)的分布式算法。... -
数据库系统概念两阶段锁
2022-04-24 12:31:32所谓两段锁协议是指所有事务必须分两个阶段对数据项加锁和解锁。 .在对任何数据进行读、写操作之前,首先要申请并获得对该数据的封锁 .在释放一个封锁之后,事务不再申请和获得任何其他封锁。所谓"两段"锁的含义是... -
TwoPhaseLocking:严格的两阶段锁定协议用于并发控制,使用等待死锁方法来处理死锁
2021-05-10 21:08:08严格的两阶段锁定协议用于并发控制,使用等待死锁方法来处理死锁 程序的设计与实现: 我设计了两个HashMap来跟踪所有事务,并设计了PriorityQueue来存储等待的事务。 我们将所有操作的列表存储在交易表(TT)和锁定... -
严格二段锁协议实现
2020-03-26 13:27:17DBMS包含一个锁管理器,用于决定事务是否可以锁定。 它了解系统内部的最新情况。 共享锁(S-LOCK):允许多个事务同时读取同一对象的锁。 如果一个事务持有共享锁,则另一个事务可以获取该共享锁。 独占锁定(X-... -
并发控制之两段锁协议
2022-02-11 10:12:00数据库管理系统普遍采用两段锁协议的方法实现并发调度的可串行性,从而保证调度的正确性 两段锁协议 指所有事务必须分两个阶段对数据项加锁和解锁 在对任何数据进行读、写操作之前,事务首先要获得对该数据的封锁 在... -
数据库中三级封锁协议与两段锁协议有区别吗?
2021-02-03 09:02:19展开全部区别如下:1、过程不同三级封锁协议是一部完成封锁的,而后者却要62616964757a686964616fe78988e69d8331333431356566分为两个阶段,耗费时间2、含义不同两段锁协议是指每个事务的执行可以分为两个阶段:生长... -
两段锁协议(2PL)
2021-01-20 01:17:08因此,一次封锁法遵守两段锁协议,但两段锁并不要求事务必须一次性将所有要使用的数据全部加锁,这一点与一次性封锁不同,这就是遵守两段锁协议仍可能发生死锁的原因所在。 本文原创发布php中文网,转载请注明出处,... -
数据库三级封锁协议和两段锁协议区别
2021-04-02 19:52:53三级封锁协议用于解决修改丢失、不可重复读和读脏数据问题,解决问题的焦点是给数据库对象何时加锁、加什么样的锁 一级封锁协议:事务T在修改数据R之前必须对其加X锁,解决修改丢失问题 二级封锁协议:在一级封锁... -
应试笔记9:并发控制、封锁、死锁和活锁、并发调度的可串行性、两段锁协议、意向锁
2019-03-22 20:47:32并发控制机制的任务 对并发操作进行正确调度;保证事务的隔离性;保证数据库的一致性 数据不一致性:由于并发操作破坏了事务的隔离性 ...两个事务T1和T2读入同一数据并修改,T2的提交结果破坏了T... -
数据库基础知识详解二:乐观/悲观锁、封锁级别、三级封锁协议以及两段锁协议
2022-04-02 23:23:18数据库基础知识详解:乐观/悲观锁、封锁级别、三级封锁协议以及两段锁协议 -
mysql中的两段锁协议和三级封锁协议
2019-04-17 09:14:42两段锁协议 一个事务中一旦开始释放锁,就不能再申请新锁了。事务的加锁和解锁严格分为两个阶段,第一阶段加锁,第二阶段解锁。 目的 :”引入2PL是为了保证事务的隔离性,保证并发调度的准确性,多个事务在并发的... -
数据库系统原理———两段锁协议、死锁练习题
2020-07-04 22:43:32(1)改写T和T2, 增加加锁操作和解锁操作,并要求遵循两阶段封锁协议。 (2)说明T和T2的执行是否会引起死锁,给出T和T2的一个调度并说明之。 二、问题解答 (1)如下表所示 T1 T2 Slock A R(A) Slock B ... -
1.11两阶段提交协议
2022-01-24 16:01:20两阶段提交协议 -
数据库知识整理 - 并发控制(封锁、两段锁协议、意向锁)
2018-12-18 19:37:25两段锁协议 封锁的粒度 多粒度封锁 意向锁 锁的强度 前提 并发控制技术与前一篇提到的数据库恢复技术是主要的事务处理技术,同时并发控制机制和数据库恢复机制是数据库管理系统(DBMS)的重要组成部分。 ...