并发控制
并发控制概述
- 事务是并发控制的基本单位
- 并发操作带来的数据不一致性,所有的问题都指的是在一次事务内,两次事务间就不管了
- 丢失修改
- 不可重复读
- 读脏数据
丢失修改
T1 | T2 |
---|---|
读 | |
读 | |
写 | |
over | 写 |
不可重复读
不可重复读是指事务T1读完数据后T2执行了更新操作,T1再读时无法出现和上一次一样的结果
不可重复读有三种情况:
- T2修改了某些数据
- T2删除了某些数据
- T2插入了数据
后两种又叫幻读
脏读
事务T1修改了数据,并写回了磁盘
事务T2读了数据
T1又撤销了数据
T2读的埋汰啊
封锁
锁的基本类型
共享锁(读锁) | 排它锁(写锁) |
---|---|
事务T对数据加了锁,则事务T可以读但是不能修改。其他事务也只能加读锁 | 若事务T对数据加了写锁,则只允许T读写 |
排它锁和谁都不兼容
三级封锁协议
一级封锁协议
事务T在修改
数据R前,必须先对其加X锁,直到事务结束才释放
- 正常结束——commit
- 异常结束——rollback
一级封锁协议可以防止丢失修改,并保证事务是可恢复
能解决丢失修改,但不能解决脏读和不可重复读
二级封锁协议
一级协议加上读数据前加S锁,读完后才可释放
能解决丢失修改,脏读,但不能解决不可重复读
三级封锁协议
一级封锁协议加上读数据前加S锁,事务结束后才能释放
活锁和死锁
活锁
事务T1封锁了数据R
事务T2又请求封锁R,于是T2等待
T3也请求封锁R,当T1释放了锁后,系统批准了T3的请求,T2继续等待
T4也请求,锁释放后给了T4
…
T2总也得不到批准,这就是活锁
情况
解决活锁办法:先来先服务
死锁
死锁的预防
-
一次封锁法
要求每个事务必须一次把要用到的数据都加上锁
问题是并发度下降
-
顺序封锁法
顺序封锁法是预先对数据对象规定个封锁顺序,所有事务都按这个顺序实行封锁
维护成本高
死锁的诊断
-
超时法
如果一个事务等待时间过长,就认为发生了死锁
- 优点:实现简单
- 缺点:
- 可能造成误判
- 若时间设置太长,死锁可能不能及时发现
-
等待图法
把事务等待结构画成带方向的图,然后判断图是否有回路
解除死锁
找一个处理死锁代价最小的事务,将其撤销
并发调度的可串行性
可串行化调度
多个事务的并发执行是正确的,当且仅当结果与按某一次序串行地执行这些事务时的结果相同
可串行性
可串行性是并发事务正确调度的准则
两段锁协议
所有事务必须分两个阶段对数据项加锁和解锁
- 读写之前要先获得锁
- 释放一个锁后,事务不再申请和获得任何其他锁
遵守两段锁协议可能发生死锁,因为并没有对使用的数据全部加锁
封锁粒度
封锁对象的大小称为封锁粒度
多粒度封锁
- 以树的结构表示多级封锁力度
- 根节点是整个数据库,表示最大的数据粒度
- 叶节点表示最小的数据粒度
显式封锁和隐式封锁
- 显式封锁:直接加载数据对象上
- 隐式封锁:父节点上了锁,子节点有同样的锁
意向锁
目的:提高对某个数据对象加锁时系统的检查效率
- 如果对一个节点加意向锁,则说明该节点
下层节点
正在被加锁 - 对任意节点加基本锁,必须先对它的上层节点
加意向锁
锁的强度
对其他锁的排斥程度
强锁代替弱锁没问题,反过来就要出事