数据库事务(死锁)

死锁!!!!!!

最近面试被问的比较多的就是死锁。。(记录一下吧

  • 什么情况下会出现死锁
  • 项目中什么时候会出现死锁
  • 死锁跟事务的联系?
  • 死锁产生的条件
  • 有哪些锁? 死锁会造成什么问题?
  • Mysql如何保持原子性,一致性,持久性(ACID)(事务的四大特性)

死锁产生的条件

死锁发生的条件
- 互斥条件:就是一个资源只能有一个进程占有,不可以被两个或者多个进程占有
- 不可抢占条件:进程已经获得的资源在未使用之前,不可被抢占,只能在使用完之后自己释放
- 占有申请条件:进程自己已经至少保持一个资源,又请求其他资源,但是这个资源被其他进程占有,
  			  而且又不释放自己已经占有的资源
- 循环等待条件:发生死锁时,必定会形成一个进程一一资源的环路。进程集合{P1,P2,P3}中,P1请求P2占有的资源,P2请求P3占有的资源,P3请求P1占有的资源

出现的原因

  1. 系统资源不足
  2. 进程允许推荐顺序不当
  3. 资源分配不当

所谓死锁,是指各并发进程彼此互相等待对方所拥有的资源,且这些并发进程在得到对方的资源之前不会释放自己所拥有的资源。从而造成大家都想得到资源而又得不到资源,各并发进程不能继续向前推进的状态。
面试时候的回答

死锁是一种特定的状态,在实体之间,由于循环依赖导致彼此一直处于等待之中,没有任何个体可以继续前进。死锁不仅仅是在线程之间会发生,存在的资源独占的进程之间也有可能出现死锁。通常来说,我们大多是聚焦在多线程场景中的死锁,指两个或多个线程之间,由于没有互相持有对方需要的锁,而永久处于阻塞状态。

预防死锁

  1. 尽量避免使用多个锁,并且只有需要时才持有锁。否则,嵌套的synchronized或者lock非常容易出问题。
  2. 如果必须使用多个锁,尽量设计好的获取顺序,这个可以参考经典的银行家算法。但一般算法下,可以采取一些简单的辅助手段,比如:
  • 将对象(方法) 和锁之间的关系,用图形化的方法表示分别抽取出来。
  • 然后根据对象之间组合,调用的关系对比组合,考虑所有的可能的调用顺序。
    按照可能时序合并。发现可能死锁的场景。
  1. 使用带超时的方法,为程序带来更多的可控性。

死锁与事务的联系

死锁是两个或多个事务再同一资源相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。

假设如下事务:
事务1

START TRANSACTION;
UPDATE StockPrice SET colse=45.50 WHERE stock_id=4 and date='2002-05-01';
UPDATE StockPrice SET colse=19.80 WHERE stock_id=3 and date='2002-05-02';
COMMIT;

事务2

START TRANSACTION;
UPDATE StockPrice SET high=20.12 WHERE stock_id=3 and date='2002-05-02';
UPDATE StockPrice SET high=47.20 WHERE stock_id=4 and date='2002-05-02';
COMMIT;

如果凑巧,两个事务都执行了第一条UPDATE语句,更新了一行数据,同时锁定了该行数据,接着每个事务都尝试去执行第二条UPDATE语句,却发现该行已经被对方锁死,然后都在等待对方释放锁,同时又互相持有对方需要的锁,则陷入死循环。
为了解决这种问题,数据库系统实现了各种死锁检测和超时机制。一般会在检测到死锁后立即返回一个错误。
死锁的出现有时候是由于存储引擎导致的,而有的则是业务中真正的数据冲突,而且基本无法避免。死锁发生后只有回滚其中的一个事务才能打破死锁。所以程序设计的时候必须考虑如何处理死锁。

Mysql如何保持原子性,一致性,持久性

  • 原子性
    整个事务是不可分割的最小单位,事务中任何一个语句执行失败,所有已经执行成功的语句也要回滚 ,整个数据库状态要恢复到执行事务前的状态
  • 一致性
    事务将数据库从一种状态变为下一种一致的状态。在事务的前后,数据库的完整性约束没用被破坏。(事务的acid不是完全正交的,尤其是一致性,可能跟原子性,隔离性都有一定关系)
  • 持久性
    事务一旦提交,那么就是永久性的,不会因为宕机等故障导致数据丢失( 外力影响不保证,比如磁盘损害)。持久性是保证了数据库的高可靠性,而不是高可用性。高可用性并不能通过事务来保证。

持久性的实现

MySQL的InnoDB存储引擎,使用Redo log保证了事务的持久性

当事务提交时,必须先将事务的所有日志写入日志文件进行持久化,就是我们常说的WAL(Write ahead log)机制,这样才能保证断电或宕机等情况发生后,已提交的事务不会丢失,这个能力称为crash-safe

Redo log包括两部分,重做日志缓冲(redo log buffer)和重做日志文件(redo log file),前者是易失的缓存,后者是持久化的文件。
举一个事务的例子:

  • 步骤1:begin;
  • 步骤2:insert into t1 …r
  • 步骤3:insert into t2 …
  • 步骤4:commit
    这个事务的写入过程实际拆解如下:
    在这里插入图片描述
    重点关注在这个事务提交前,将 redo log的写入拆成两个步骤,prepare和commit,这就是两个阶段提交

原子性的实现

Undo log 保证了事务的原子性

在对数据库进行修改时,InnoDB引擎除了会产生redo log,还会产生undo log。InnoDB实现回滚,靠的是undo log:当事务对数据库进行修改时,InnoDB会生成对应的undo log;如果事务执行失败导致事务需要回滚,就利用undo log中的信息将数据回滚到修改之前的样子。
redo log恢复事务导致的数据页的修改,而undo log是一种逻辑日志(数据记录)
undo log还有另外一种重要作用,就是用于mvcc中,进行多版本控制,也就是实现事务隔离性的基础,当用户读取一行记录时,如果这个记录已经被其他事务占用,那么当前事务就可以通过undo读取之前的新版本信息,用来实现非锁定读取,就是“快照读”

一致性的实现

事务的ACID性质不是完全正交的,尤其是一致性,我们可以认为原子性,持久性和隔离性都是为了实现事务的一致性。当然,这里的一致性是指数据库层面的事务一致性。

应用层面: 给转帐者扣钱,没给接收者加钱,那么这个不一致跟事务的不一致是没用关系的,需要开发人员自己做业务逻辑一致性的保证。

猜你喜欢

转载自blog.csdn.net/weixin_42061487/article/details/108715508
今日推荐