最近项目的在对方测试时候出现死锁,根据对方提供的图片以及文档资料,大致知道是在代码的具体那些步骤,
根据这两张图片判断出来的是一个查询以及一个更新的sql之前发生了相互等待的情况
一.死锁发生的条件满足四种情况,
-
互斥条件(Mutual Exclusion):资源不能同时被多个进程或线程占用。这意味着一次只有一个进程或线程可以使用资源,如果一个进程或线程已经占用了某个资源,其他进程或线程就无法再次占用该资源。
-
占有和等待(Hold and Wait):进程或线程至少已经占有一个资源并在等待获取其他资源。也就是说,当一个进程或线程占用了某个资源时,它可以继续请求其他资源,同时保持对已占用资源的控制。
-
非抢占条件(No Preemption):系统不能抢占进程或线程已占有的资源。这意味着,如果一个进程或线程占有了某个资源,其他进程或线程不能强制它释放这个资源,而只能等待。
-
循环等待(Circular Wait):存在一个进程或线程的资源等待链,形成了一个循环。也就是说,进程或线程之间相互等待彼此持有的资源,形成了一个闭环。
这里是官方层面的说法,按照个人通俗点的就是AB两人都需要对方的资源,但两人都不放弃就一直僵持着,导致死锁(deadlock)的发生
要处理这个问题就只能破坏其中的任意一个条件即可.
但第一个互斥基本没法处理,除非就是单线程去访问,但这样就限制了整体的系统速度,以及原本的框架
思路
一.等待的破坏:
sql查询结构的优化,对按照条件查询的尽量减少查询的时间,
①可以对查询的字段添加索引(单个的索引,或者聚合索引)
②非必要,不要一次性返回过多的数据(加返回数据量)
③这个是同事给出的方案,对于查询频繁的部分可以将查询结果放置在redis上,减少对数据库的压力,也避免了对锁的频繁抢用,之后可以设置过期时间,到时间后过期(根据自己业务逻辑来判断处理)
二.条件抢占
例如我查的部分编译语言
-
C/C++:C/C++ 是系统编程语言,提供了丰富的系统调用接口,可以在程序中实现对进程的抢占操作。例如,通过调用
pthread_cancel
函数可以取消 POSIX 线程,从而实现对进程的抢占。 -
Java:Java 提供了对线程的抢占支持。你可以使用
Thread.interrupt
方法来中断一个线程的执行,从而实现对线程的抢占。此外,Java 的线程调度器也会根据线程的优先级和其他因素来决定哪个线程应该被执行,从而实现对线程的抢占。 -
Python:Python 也提供了对线程的抢占支持。你可以使用
threading.Thread
类来创建线程,并使用threading.Thread.join
方法来等待线程执行完成。此外,Python 的threading
模块还提供了一些其他方法来控制线程的执行。 -
C#:在 .NET 框架中,C# 语言提供了对线程的抢占支持。你可以使用
Thread.Abort
方法来中断一个线程的执行,从而实现对线程的抢占。不过,这种方法容易导致资源泄漏和不一致的状态,因此需要谨慎使用。
可惜我该项目是用ts来处理的
三.设置数据库的等待时长
例如server sql, SET LOCK_TIMEOUT 10000; -- 时间单位为毫秒,这里是 10 秒
这个设置仅对当前会话生效,而不是全局设置。因此,每个会话都可以独立地设置自己的锁超时时间。
我对查询的添加联合索引,限制查询大小
对更新部分,先查询之后再将结果更新到新的data之后再通过save保存,这里就可以返回最新的结果(原本的逻辑是先查,再更新,再查最新的结果返回)