事务与数据库并发控制
一、为什么我们需要事务
在数据库的操作中,有的操作需要保证其一致性。
比如A向B发送聊天消息,如果A撤回了一条消息,那么A的聊天界面中应该显示消息已撤回,B的聊天消息中也应该显示消息已撤回,这两个撤回应为同步的,如果一个失败,则另一个也应该失败
为了保证这样的一致性,我们引入了事务的概念
二、事务的四大特性
这里再举一个例子进行说明
如果A向B转账,那么可能有如下多个情况,分别对应一个特性
1.原子性
如何同时保证上述交易中,A账户总金额减少一个亿,B账户总金额增加一个亿?
即一个事务中的多个操作是不可分的,要么都成功,要么都失败
2.一致性
D如何在支持大量交易的同时,保证数据的合法性(没有钱凭空产生或消失) ?
不能出现数据不一致的情况。将在后面进行讲解。
3.隔离型
A账户如果同时在和C账户交易(T2),如何让这两笔交易互不影响?
两个事务之间应该是互不影响的
4.持久性
如果交易完成时数据库突然崩溃,如何保证交易数据成功保存在数据库中?
一旦事务完成,对数据库的影响应该是持久的。这里需要后面的回滚处理进行保障
这四个特性统称为ACID(Atomicity,Consistency, Isolation, Durability)
三、事务的并发控制
为什么需要并发控制
如果不进行并发控制,可能会出现如下的不一致性问题(对应事务的一致性原则):
1.脏读
脏读又称无效数据的读出,是指在数据库访问中,小A将某一值修改,然后小B读取该值,此后小A因为某种原因撤销对该值的修改,这就导致了小B所读取到的数据是无效的。
2.丢失修改
由于两个人同时对数据库进行修改,其中一人的修改覆盖了另一人的修改造成了错误
3.不可重复读
事务A读取数据后,事务B对数据进行了修改,A再次读取,读到的数据和第一次不一样
4.幻读
事务A读取数据后,事务B插入了新的数据,A再次读取,发现有新的数据出现
不可重复读和幻读的区别
- 前者针对的是update,后者针对的是insert
- 对于1的解释:前者针对的是数据的属性,后者针对的是整个表中数据的条数
- 解决方案不同:前者只需要对行加锁,后者需要对表加锁
并发控制的基础概念
正确性
当且仅当这个并发调度下的数据库结果与分别串行地执行这些事务相同,我们称这个调度是正确的。
可串行性
如果不管数据库初始状态如何,一个调度对数据库状态的影响都和某个串行调度相同,则我们说这个调度是可串行化的(Serializable)或具有可串行性(Serializability)。
正确性和可串行性的区别在于,前者是固定数据的,而后者是形式上正确
即使说,如果前者的数据为80时是正确的,是30时却不正确,则我们可以说这个调度在数据为80时是正确的,而这个调度本身却不是可串行的。
这两者如果保证了前者,则不一定保证后者,反之则可以
冲突可串行性
冲突
冲突:调度中一对连续的动作:如果它们的顺序交换,那么涉及的事务中至少有一个事务的行为会改变。
有冲突的两个操作是不能交换次序的,没有冲突的两个事务是可交换的
存在冲突的操作有:
- 同一个事务的所有操作
- 不同事务对同一数据的一读一写
- 不同事务对同一数据的两个写
冲突可串行性
一个调度,如果通过交换相邻两个无冲突的操作能够转换到某一个串行的调度,则称此调度为冲突可串行化的调度。
满足冲突可串行性则一定满足可串行性,反之不然
基于锁的并发控制
锁的类型
排它锁
只有当前获取锁的事务可以读写
共享锁
只有当前获取锁的事务可以写,其他事务可以读
更新锁
用来预定要对此页施加排他锁,它允许其他事务读,但不允许再施加更新锁或排他锁;当被读取的页将要被更新时,则升级为排他锁
意向锁
用于节约资源的锁
考虑这样一个场景,事务A为第十行加了排它锁,此时B想要为整个表加上排它锁,因此需要遍历整个表查询是否有其他事务对表中数据加了锁,这样是很耗费资源的。在实际运用中,当A为第十行加锁时,会为整个表加上意向锁,以向其他事务表明这个表中有数据被加锁了
常用的意向锁有三种:意向共享锁(Intent Share Lock,简称IS锁);意向排他锁(Intent Exclusive Lock,简称IX锁);共享意向排它锁(Share Intent Exclusive Lock,简称SIX锁),共享意向排它锁的意思是,某事务要读取整个表,并更新其中的某些数据
封锁协议
需要考虑封锁的类型、相容性矩阵、加锁/解锁时机、封锁粒度
两段封锁协议
- 读写数据之前需要获得锁
- 分为两段:加锁段和解锁段,加锁段中不能有解锁操作,结算段中不能有加锁操作
- 可以保证冲突可串行性
- 可能产生死锁
基于时间戳的并发控制方法
当事务T启动时,系统将当前时间戳赋予T,为T的时间戳
在并行调度时,依据时间戳大小以从小到大的顺序进行冲突判断
基于有效性确认的并发控制方法
为每一个活跃事务保存其读写数据的集合,通过对多个事务的读写集合的比较,判断是否有冲突
四、基于事务的故障恢复
日志
日志是故障恢复的基础。该文件以流水方式记录了每一个事务对数据库的每一次操作以及操作顺序。
在运行日志中会定期设置及更新检查点。当故障发生时,会以最近的检查点为准,检查点后到故障点前已经完成的事务需要重做(Redo),还未完成的事务需要撤销(Undo)。
五、MySQL的事务
(一)隔离级别
1.未提交读
所有事务都可以看到没有提交事务的数据
有脏读,有不可重复读,有幻读
2.已提交读
只有事务成功提交后才能被看到
无脏读,有不可重复读,有幻读
3.可重复读(InnoDB默认级别)
为读取的行加锁
无脏读,无不可重复读,有幻读
4.可串行化
强制事务进行串行,在表上加锁,会导致大量超时和锁竞争,但是带来最高可靠性
无脏读,有不可重复读,无幻读
(二)锁
表锁
开销小,加锁快;无死锁;锁定粒度大,锁竞争概率高,并发度低
分为表读锁和表写锁,读读不阻塞,读写阻塞,写写阻塞
此外还有意向共享锁和意向排它锁
行锁
开销大,加锁慢;有死锁;锁定粒度小,锁竞争概率低,并发度高
有共享锁、排它锁
MVCC行级锁
对数据库的任何修改的提交都不会直接覆盖之前的数据,而是产生一个新的版本与老版本共存,使得读取时可以完全不加锁
间隙锁
当我们用范围条件检索数据而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合范围条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在 的记录,叫做“间隙(GAP)”。
间隙锁可以防止幻读(在可重复读基础上增加间隙锁即可)

浙公网安备 33010602011771号