数据库并发的概念
事务并发基本概念
- 为什么要并发?
一个数据库管理系统必须能够支持多个用户同时使用它 - 概念
同一时间段内,有多个事务同时访问。 - 并发如果不处理,会产生的问题
①丢失修改
例如:两个事务都修改了,后修改的会把前面的修改覆盖了。前面的修改就无意义了。
②读入“脏数据”
事务A,B。A修改了,然后被B读了,但是A发现改错了需要回退,这样B读到的就是错误的数据。
③不可重复读
事务B读了两次,在这两次之间被A改了,这就导致两次读到的并不相同,这就会导致很多错误。 - 解决:构造一个事务执行的序列——并发调度
①串行调度:一个一个的走(不合理,可以作为并发调度里的一部分)
②一致性调度:如果执行某一个调度,可以吧数据库从一个一致性状态转成另外一个一致性状态。(一致性状态与业务状态有关,大抵就是说正确的状态) - 可串行化调度
对于一组事务,有串行调度S,还有一个调度S`,如果两者有相同一致性的一个调度,则称后者为一个可串行化调度。(通俗讲,就是可以和串行调度得到相同结果的调度序列,也可以理解为串行调度为一个标准) - 冲突可串行化调度:某一个并行的调度,通过非冲突指令转化为串行调度,且与该串行调度的执行结果一致,则S是冲突可串行化的。(通俗讲:就是可以通过把那些不冲突的指令交换顺序(比如对不同数据的操作),然后可以变成串行调度的样子,就说明他是冲突可串行化调度,同时,他也一定是可串行化调度)
- 有些调度是可串行化的,但是不可以通过调整非冲突指令变成串行化的,这种调度叫做视图可串行化调度(非冲突可串行化调度)
- 冲突可串行化的判断
优先图:节点代表事务,有向弧代表事务的先后顺序。如果这个图是有环的,代表是冲突可串行化的,如果是无环的,代表是非冲突可串行化的
基于封锁的并发控制机制
锁的概念及封锁的原理
简单来讲:一个事务对某个数据进行操作,就需要上锁(就像上厕所一样,进去以后锁门)。如果你是读,就要上S锁,如果你是写,就要上X锁。别人来了以后,会根据相容矩阵来看是否能进。即操作对应上锁,锁不是限制,锁是操作行为。(伴随着管理器的grant授权)
- 锁的基本模式
①共享锁(读锁,shared——S锁):允许执行读操作
②排他锁(写锁,Exclusive——X锁):允许执行读/写操作 - 锁的调度策略
①接触一个数据对象的排他锁之前,其他事务不能对它加任何锁
②一个数据对象允许加几个共享锁,但不能在共享锁之上,加排他锁 - 锁的相容矩阵
S与S是相容的,SX,XX都是不相容的 - 随意加锁也会产生一系列的问题
①可能会产生不正确的结果(加和问题)
②可能会产生死锁
③饿死:始终有优先级更高的在用锁,某一个事务一直得不到。避免:对等待的事务随着等待时间优先级提高。
两阶段协议
- 两阶段锁协议:为了能够正确的加锁
每个事务分为两个阶段提出加锁和解锁要求。
一开始只能加锁,不能执行任何一个解锁操作。直到到达某一个节点,也就是所有该加的锁都加完了,才能执行解锁操作,在执行解锁操作的时候,不能执行加锁操作。
这种方式可以避免第一个问题,但是不可以避免死锁和饿死。
同时也无法解决级联回滚
级联回滚:多个事务对同一个数据进行操作,前面的事务发生错误,那么要几个事务都回滚。
怎么避免级联回滚:把两阶段锁协议严格化。——把释放排他锁的时机从加锁结束后移到事务提交。只有提交后才能释放锁。
强两阶段锁协议:比严格两阶段锁协议更加严格:在事务提交之前不允许释放任何锁。
- 锁转换机制:由于严格和强两阶段锁并发程度太低,所以为了提高并行能力提高效率引入的。做法为在写的时候先上S锁,然后真的要写的时候再将S锁升级成X锁,写完再降为S锁。
多粒度锁和意向锁
前面讲的都是对某一个元素进行的处理,但是在数据库中,有不同的粒度,也有需要多继承关系,所以要考虑多粒度加锁的问题。
简单来说,就是给一个东西加锁,对其祖先都会加上意向锁,对其子孙都会加上这个东西相同的锁
- 多粒度加锁的特点
显示加锁:多粒度树上的每个节点都可以单独加锁。
隐式加锁:对当前节点加锁会导致隐式地对其全部后代节点加上相同类型的锁。
检查锁冲突的时候,必须检查其所有的祖先后代节点。 - 意向锁:一个事物对一个数据显式加锁之前,必须对它的全部祖先节点先加意向锁。
①意向读锁(IS锁)若对某节点加IS,那么将对后代节点显示的加S
②意向写锁(IX锁)若对某节点加X锁,那么将对后代节点显式的加X锁
③SIX锁:若对某节点加SIX锁,则对该节点不仅显示的加S锁,而且还对他加IX锁,即=S+IX。 - 多粒度锁的加锁协议
①必须遵守两阶段锁协议
②必须遵守锁类型的相容性矩阵
③根节点必须首先加锁
④任何事物加锁必须从根到叶子顺序进行
⑤任何事物开锁必须从叶子到跟顺序进行
死锁的处理
- 死锁预防
引入死锁预防协议:通过对加锁请求进行排序。
①抢占和事务回滚技术
每一个事物赋予一个时间戳,若一个事物回滚,则重启该事务时,必须抱持原时间戳。然后采用以下机制:
wait-die——如果A申请的数据被B持有,则只有A启动的时间比B晚(A的时间戳大于B),也就是B先启动的,A才会等待,否则A回滚。
wound-wait——与前面相反,先启动的等待,后启动的回滚。
②基于超时的机制,事务给出事务的等待时间,超时即回滚。 - 死锁的检测和解除
通过等待图
指T1等待T3释放锁。有环说明死锁。
如果有环,那就得解除死锁,选择一个代价小的事务进行回滚。(也可能每次都选择它回滚,最后饿死)
Sql serve中,就是周期性的检测。
构造冲突可串行化的其他调度协议
- 基于时间戳的调度协议
每一个事务在开始执行的时候都赋予一个时间戳,时间戳决定串行化的顺序。
TS——时间戳,小的开始时间早
W(Q)在数据项Q上成功执行写操作的所有事物的最大时间戳
R(Q)在Q上执行读的所有事物的最大时间戳
排序协议:如果T想要执行读操作
- 基于有效性检验的调度协议(诊治型)
把事务的生命周期分为了三个阶段
读阶段:读数据并保存在局部变量中,以后对该数据的更新均在局部变量中进行。
有效检查阶段:检查是否可以将局部变量的更新复制到数据库中而不违反可串行化。
写阶段:十五通过有效性检查以后,更新数据库。