高并发和死锁

互斥条件:一个资源每次只能被一个进程使用。
 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

锁的类别有两种

1. 从数据库系统的角度来看:分为独占锁(即排它锁),共享锁和更新锁  

MS-SQL Server 使用以下资源锁模式。 

锁模式描述: 

  共享 (S) :读锁,用于不更改或不更新数据的操作(只读操作),如 SELECT 语句。  
  更新 (U) :可以让其他程序在不加锁的条件下读,但本程序可以随时更改。 

写锁。

  用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。  
意向锁 用于建立锁的层次结构。意向锁的类型为:意向共享 (IS)、意向排它 (IX) 以及与意向排它共享 (SIX)。  
架构锁 在执行依赖于表架构的操作时使用。架构锁的类型为:架构修改 (Sch-M) 和架构稳定性 (Sch-S)。  
大容量更新 (BU) 向表中大容量复制数据并指定了 TABLOCK 提示时使用。  

共享锁 
  共享锁允许并发事务读取 一个资源。资源上存在共享锁时,任何其它事务都不能修改数据。一旦已经读取数据,便立即释放资源上的共享 (S) 锁,除非将事务隔离级别设置为可重复读或更高级别,或者在事务生存周期内用锁定提示保留共享 (S) 锁。  

更新锁 
  更新 (U) 锁可以防止通常形式的死锁。一般更新模式由一个事务组成,此事务读取记录,获取资源(页或行)的共享 (S) 锁,然后修改行,此操作要求锁转换为排它 (X) 锁。如果两个事务获得了资源上的共享模式锁,然后试图同时更新数据,则一个事务尝试将锁转换为排它 (X) 锁。共享模式到排它锁的转换必须等待一段时间,因为一个事务的排它锁与其它事务的共享模式锁不兼容;发生锁等待。第二个事务试图获取排它 (X) 锁以进行更新。由于两个事务都要转换为排它 (X) 锁,并且每个事务都等待另一个事务释放共享模式锁,因此发生死锁。  

  若要避免这种潜在的死锁问题,请使用更新锁。一次只有一个事务可以获得资源的更新 (U) 锁。如果事务修改资源,则更新 (U) 锁转换为排它 (X) 锁。否则,锁转换为共享锁。  

排它锁 
  排它 (X) 锁可以防止并发事务对资源进行访问。其它事务不能读取或修改排它 (X) 锁锁定的数据。  

意向锁 
  意向锁表示 SQL Server 需要在层次结构中的某些底层资源上获取共享  锁或排它 锁。例如,放置在表级的共享意向锁表示事务打算在表中的页或行上放置共享 (S) 锁。在表级设置意向锁可防止另一个事务随后在包含那一页的表上获取排它锁。意向锁可以提高性能,因为 SQL Server 仅在表级检查意向锁来确定事务是否可以安全地获取该表上的锁。而无须检查表中的每行或每页上的锁以确定事务是否可以锁定整个表。 

  意向锁包括意向共享 (IS)、意向排它 (IX) 以及与意向排它共享 (SIX)。

死锁原理

    根据操作系统中的定义:死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态。

    死锁的四个必要条件:
互斥条件(Mutual exclusion):资源不能被共享,只能由一个进程使用。
请求与保持条件(Hold and wait):已经得到资源的进程可以再次申请新的资源。
非剥夺条件(No pre-emption):已经分配的资源不能从相应的进程中被强制地剥夺。


循环等待条件(Circular wait):系统中若干进程组成环路,该环路中每个进程都在等待相邻进程正占用的资源。

死锁处理方法:

根据2中提供的sql,查看那个spid处于wait状态,然后用kill spid来干掉(即破坏死锁的第四个必要条件:循环等待);当然这只是一种临时解决方案,我们总不能在遇到死锁就在用户的生产环境上排查死锁、Kill sp,我们应该考虑如何去避免死锁。

 使用SET LOCK_TIMEOUT timeout_period来设定锁请求超时。默认情况下,数据库没有超时期限

当请求锁超过timeout_period时,将返回错误。timeout_period值为0时表示根本不等待,一遇到锁就返回消息。设置锁请求超时,破环了死锁的第二个必要条件(请求与保持条件)。

服务器: 消息 1222,级别 16,状态 50,行 1
已超过了锁请求超时时段。

 SQL Server内部有一个锁监视器线程执行死锁检查,锁监视器对特定线程启动死锁搜索时,会标识线程正在等待的资源;然后查找特定资源的所有者,并递归地继续执行对那些线程的死锁搜索,直到找到一个构成死锁条件的循环。检测到死锁后,数据库引擎选择运行回滚开销最小的事务的会话作为死锁牺牲品,返回1205 错误,回滚死锁牺牲品的事务并释放该事务持有的所有锁,使其他线程的事务可以请求资源并继续运行。

 多线程5状态

新建状态:当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();

就绪状态:当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;

运行状态:当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就     绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

阻塞状态:处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:

1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;

2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;

3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

死亡状态:线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

什么是并发

并发,最简单的理解就是,两个或者以上的活动同时进行。举个比较实际的例子,你可以手脚并用,两只手做不同的动作等等。

在计算机中的“并发”,是指一个系统可以同时执行多个独立的活动。在以前大多数计算机都只有一个处理单元(或者核心),这种计算机在同一时刻只能执行一个任务,任务只能一个一个的执行,但是这样的计算机可以通过任务的高速切换来产生“并发”的假象。

在现如今,拥有多个处理器的计算机已经遍地都是,这些计算机可以真真正正的在同一时刻执行多个独立的任务,这样的被叫“硬件并发”

多进程并发

多进程并发是指,存在多个单线程的进程,将应用程序分为多个,比如想要实现同时播放音乐和播放影像,那么就分为两个进程,一个进程播放音乐,另一个进程播放影像。

但是这样有一个问题,比如你想要放一部电影,声音和影像应该同步才对(没人想看影音不同步的电影),那么两个进程之间为了实现播放电影就必须有所交流,那么就涉及到进程间通信,进程间的通信方式就不多说了,各种各样的通信方式也有满足不了需求的时候,比如同步复杂,速度慢造成延迟,更糟糕的是可能两者都有。

看到这里,可能会说速度很重要,多进程并发你可真是个小老弟。多进程并发存在的意义是什么?它的优点是什么?

多进程一个很大的优点就是进程间非常独立,除了通信之外基本不会互相影响,比如两进程跑着跑着,一个进程突然死了,另一个进程别提伤心了,连看都不看一眼就继续跑(可能比较极端,但是多进程的架构一个重要的角度就是安全),最有力的一个例子就是chrome浏览器,chrome浏览器采用多进程沙盒架构,为的就是当一个进程崩溃时不会影响主进程和其他进程。

多线程并发

并发的另一个途径是单个进程中运行多个线程,线程在很多书中都被称为“轻量级的进程”,每个线程可以相互独立运行,但是进程中的所有线程都享有共同的地址空间,并且线程间拥有不少共享数据(那么线程间同步和死锁之类的概念就引出来了)。

那么缺点就来了,线程间的同步并不简单,是挺复杂的一个事情,并且加锁之类的操作也有成本,会耗费一些资源

同时安全性问题也存在,比起多进程并发来说,多线程并发更像是四人三脚跑步,一个线程倒下了,其他三个可能也就完蛋了。

为什么要使用并发

为了性能而使用并发

在同一时刻干好几个任务,肯定比一个一个任务执行来的快。(这个说法其实并不准确,这里的意思是为了在硬件条件允许的情况下为了提高性能而使用并发,需要综合考虑)

为了划分关注点而使用并发

如果一场派对的举办都让一个人干,肯定会手忙脚乱,也不易于管理。任务也是一样,又是读文件又是网络请求啥的,就算计算机比人的头脑好一些,不会手忙脚乱,但是任务是要人来写和管理的。一大锅混在一起是很难进行管理的。

使用并发的一个目的就是为了将任务拆解,将关注点划分开。易于管理。

 

 

 

posted on 2020-03-18 21:17  我胡闹i你善后i  阅读(550)  评论(0编辑  收藏  举报