数据科学每日总结--Day18--数据库
死锁
定义:指在数据库事务执行过程中,两个或多个事务因为相互等待对方所占用的资源而陷入无限等待的状态。假设有事务T1已锁定记录 R1,准备锁定记录 R2,事务T2已锁定记录 R2,准备锁定记录 R1。此时T1 等待 T2 释放 R2,T2 等待 T1 释放 R1,结果陷入循环等待——这就是死锁。
死锁通常需要以下四个条件同时满足:
互斥条件:某个资源一次只能被一个事务占用。
保持并等待条件:事务在持有一个资源的同时,还能够请求其他资源,并且处于等待状态。
不剥夺条件:资源一旦分配给事务,不能被强制剥夺,只能由事务自己释放。
循环等待条件:存在事务形成一个环形等待链,链上的每个事务都在等待下一个事务所持有的资源。
死锁预防
核心思想:在事务运行之前避免死锁的必要条件成立
- 常见预防方法:
一次性申请所需资源 要么全部请求需要的资源,要么不进入事务执行阶段。
资源有序分配 对资源编号,每个事务必须按资源编号顺序申请。
不等待策略 一旦无法立即获取锁,直接回滚事务(适用于悲观锁效率要求高的场景)。
限制锁类型和时间 尽量避免长事务、避免大范围数据锁定。
优点:死锁不可能发生
缺点:资源可能产生浪费,系统吞吐下降
而在死锁预防中,一个经典方法是根据事务的时间戳来决定资源争夺策略:
每个事务在进入系统时分配一个唯一的时间戳(TS,通常是启动时间),TS 越小表示事务越“老”。
当事务请求某个已被其他事务占用的资源时,系统不一定让它等待,可能直接让它回滚——这样提前打破可能形成的循环等待。
这就产生了两种策略:
等待-死亡(Wait-Die) —— 老事务等,年轻事务死。
伤口-等待(Wound-Wait) —— 老事务抢,年轻事务等。
等待-死亡
规则描述:如果一个 老事务(TS 小)请求资源,且该资源被一个 更年轻事务(TS 大)占有,那么老事务 可以等待;如果一个 年轻事务 请求资源,且该资源被一个 更老事务 占有,那么年轻事务 直接回滚“死亡”,重新开始。
- 核心思想:
老事务资格“高”,允许等待。
年轻事务资格“低”,不能抢,只能回滚。
通过这种规则避免形成循环等待。
优点:老事务保留执行权,完成概率高,避免死锁。
缺点:年轻事务可能频繁回滚,开销大。
伤口-等待
规则描述:如果一个 老事务 请求资源,且该资源被一个 更年轻事务 占有,老事务会“伤口”年轻事务 —— 强制让年轻事务回滚释放资源;如果一个年轻事务请求资源,且该资源被一个更老事务占有,年轻事务等待
- 核心思想:
老事务更优先,不愿等待,直接抢占,让年轻事务回滚。
年轻事务愿意等老事务完成。
优点:老事务完成速度最快,系统吞吐量高;避免老事务长时间等待导致死锁
缺点:年轻事务可能被多次中断,延迟高
总结下来,等待-死亡规则是让老事务等待,让年轻事务频繁回滚或中断,而伤口等待规则则是老事务强势,让年轻事务被迫等待
死锁检测
核心思想:允许死锁发生,通过周期性检查或实时监测发现死锁
检测常用方法:节点-边等待图检测(用事务作为节点,存在等待关系时画一条有向边,如果图中存在环路则说明发生了死锁)
优点:不限制事务并发程度
缺点:检测和解除都需要额外的系统开销
不同数据库的死锁处理方式:
MySQL InnoDB:会自动检测死锁,一旦检测到会主动回滚一个事务(一般是锁定较少行数的事务)。
Oracle:允许死锁发生,检测到后立即报错并终止其中一个事务。
SQL Server:定期检测,回滚“死锁牺牲者”事务(在数据库中,如果检测到事务出现死锁,那么必须通过终止至少一个事务来打破循环等待,让其他事务继续执行,被选择回滚的事务就叫做“死锁牺牲者”)。
