为了避免涌现数据不一致问题,须要在存储过程中引入事务的观点,将更新语句绑在一起,让它们成为一个”原子性”的操作:更新语句要么都实行,要么都不实行。
一、关闭MySQL自动提交的方法
1、显示关闭自动提交

set autocommit=0;1
2、隐式关闭自动提交:(推举)
start transaction1
一样平常推举利用隐式的提交办法,由于不会修正到autocommit系统变量。
二、关闭自动提交后,提交更新语句的方法有
1.显示地提交:(推举)
commit;1
2.隐式地提交
包括了begin、set autocommit=1、start transaction、rename table、truncate table等语句;
数据定义(create、alter、drop)function、alter procedure、drop database、drop table、drop function、drop index、drop procedure等;
权限管理和账户管理语句(grant、revoke、set password、create user、drop user、rename user)
锁语句(lock tables、unlock tables)
更推举用显示提交地方式。
三、事务处理利用方法
在处理缺点代码地程序中利用rollback(事务回滚)。
在具有原子性操作的地方,利用start transaction隐式地关闭自动提交,并且在结束的为止上利用commit显示提交。
四、事务保存点的利用方法
在事务中利用savepoint 保存点名 的格式创建保存点,实现事务的”部分”提交或”部分”撤销(rollback to savepoint 保存点名)。
保存点是”临时状态”,既可以回滚到事务开始前的状态,也能决定事务的下一个状态,是介于两自动提交语句所引发状态中的一种临时状态。
五、锁机制的必要性
内存中的数据与外存中的数据不同步,个中的表记录之间存在“同步延迟“。
六、MyISAM表施加表级锁的语法格式
lock tables 表1 [as 别名] read [local]
[,表2[ as 别名2][low_priority] write] ...12
个中read local与read选项的差别为 READ LOCAL许可在锁定被保持时,实行非冲突性INSERT语句(同时插入)。
七、锁的粒度、隐式锁与显示锁、锁的类型、锁的钥匙、锁的生命周期
1.锁的粒度指锁的浸染范围:
多个MySQL客户机并发访问同一个数据时,为担保数据的同等性,数据库管理系统会自动地为该数据加锁、解锁,这种是隐式锁。
而有时单靠隐式锁无法实现数据的同等性访问哀求(例如对付临界资源的争夺上),此时须要手动地加锁、解锁,这种称为显示锁。
2.锁的类型分为:读锁(共享锁)和写锁(排他锁或独占锁)
3.锁的钥匙: 当多个MySQL客户机并发访问同一个数据时、如果MySQL客户机A对该数据成功地施加了锁,那么只有MySQL客户机A拥有这把锁的钥匙。
4.锁的生命周期:在同一个MySQL会话内,对数据加锁到解锁之间的韶光间隔。锁的生命周期越长,并发访问性能越低;锁的生命周期越短,并发访问性能越高。
八、InnoDB表施加行级锁的语法格式
共享锁 select from 表 where 条件语句 lock in share mode;
排他锁 select from 表 where 条件语句 for update;
九、InnoDB中的间隙锁、记录锁
当检索条件知足某区间范围,但表中不存在的记录,此时也有共享锁或排他锁,即行级锁会锁定相邻的键,这种机制便是间隙锁(next-key锁)
当事务隔离级别设置为repeatable read,此时InnoDB表施加行级锁,默认利用间隔锁(须要索引),
当事务的隔离级别设置为read uncommited或者read commited,此时InnoDB表施加行级锁,默认情形下利用记录锁(record lock)。
与间隙锁不同,记录锁仅仅为知足该查询范围的记录施加共享锁或排他锁。
十、锁等待与去世锁
一)锁等待:是为了担保事务可以正常地并发运行,锁等待不一定导致去世锁问题的发生。而去世锁问题的发生一定伴随着锁等待征象。
二)去世锁:
1、定义: 是指两个或两个以上的进程在实行过程中,由于竞争资源或者由于彼此通信而造成的一种壅塞的征象,若无外力浸染,它们都将无法推进下去。当线程进入工具的synchronized代码块时,便霸占了资源,直到它退出该代码块或者调用wait方法,才开释资源,在此期间,其他线程将不能进入该代码块。当线程相互持有对方所须要的资源时,会相互等待对方开释资源,如果线程都不主动开释所霸占的资源,将产生去世锁。
2、去世锁的产生4个必要条件:
1)互斥条件:进程对付所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程开释
2)霸占且等待:一个进程因要求被占用资源而发生壅塞时,对已得到的资源保持不放。
3)不可抢占条件:任何一个资源在没被该进程开释之前,任何其他进程都无法对他剥夺占用
4)循环等待条件:当发生去世锁时,所等待的进程必定会形成一个环路(类似于去世循环),造成永久壅塞。
3、去世锁的另一种:递归去世锁,举例:
递归函数便是自调用函数,在函数体内直接或间接的调用自己,即函数的嵌套是函数本身。
递归办法有两种:直接递归和间接递归,直接递归便是在函数中涌现调用函数本身。间接递归,指函数中调用了其他函数,而该其他函数又调用了本函数。
那什么时候利用递归呢?一样平常来说当你要在某段代码逻辑中利用循环迭代的时候但是迭代的次数在迭代之前无法知晓的情形下利用递归。打个比方你要在一个文件夹中查找某个文件,而这个文件夹底下有N多子文件夹和文件,当你在不知道有多少层文件夹和文件的情形下你就得用到递归了。
递归的优点便是让代码显得很简洁,同时有些运用处景不得不该用递归比如前面说的找文件。递归是个好东西但是在某些时候也会给你带来一些麻烦。比如在多线程的环境下利用递归,碰着了多线程那么就不得不面对同步的问题。而递归程序碰着同步的时候很随意马虎出问题。
多线程的递归便是指递归链中的某个方法由其余一个线程来操作,以下代码的意思都是这个意思即调用recursive()和businessLogic()并非一个线程(如果是在一个线程中就不存在去世锁问题,例如下面的recursive变成private就不存在问题。)
[java] view plain copy
public class Test { public void recursive(){ this.businessLogic(); } public synchronized void businessLogic(){ System.out.println(\"大众处理业务逻辑\"大众); System.out.println(\公众保存到<a href=\"大众http://lib.csdn.net/base/mysql\"大众 class='5aa4f1628108c488 replace_word' title=\"大众MySQL知识库\"大众 target='_blank' style='color:#df3434; font-weight:bold;'>数据库</a>\"大众); this.recursive(); } }以上这段代码便是个能形成去世锁的代码,事实上这个“synchronized”放在“businessLogic()”和“recursive()”都会形成去世锁,并且是多线程的情形下就会锁住!
他的逻辑顺序是先实行recursive()方法然后接下来实行businessLogic()方法同时将businessLogic()方法锁住,接下来程序进入businessLogic()方法内部实行完打印语句后开始实行recursive(),进入recursive()后准备实行businessLogic(),等等问题来了!
之前实行的businessLogic()的锁还没有放开这次又实行到这里了,当然是过不去的了,形成了去世锁!
从这个例子我们总结出来一个规律便是在递归的时候在递归链上面的方法上加锁肯定会涌现去世锁(所谓递归链便是指recursive()链向businessLogic(),businessLogic()又链回recursive()),办理这个问题的方法便是避免在递归链上加锁,请看以下的例子
[java] view plain copy
public class Test { public void recursive(){ this.businessLogic(); } public void businessLogic(){ System.out.println(\"大众处理业务逻辑\"大众); this.saveToDB(); this.recursive(); } public synchronized void saveToDB(){ System.out.println(\"大众保存到数据库\"大众); } }saveToDB()不在这条递归链上面自然不会涌现去世锁,以是说在递归中加锁是件很危险的事情,实在逃不过要加锁就加在最小的粒度的程序代码上以减小去世锁的概率。
4、处理去世锁的方法
4.1 预防去世锁
4.2 避免去世锁
4.2.1 常用避免去世锁的方法
4.2.1.1 有序资源分配法
4.2.1.2 银里手算法
4.2.2 常用避免去世锁的技能
4.2.2.1 加锁顺序
4.2.2.2 加锁时限
4.2.2.3 去世锁检测
4.3 检测去世锁
4.4 解除去世锁
处理去世锁的方法
4.1、去世锁预防 ----- 确保系统永久不会进入去世锁状态 产生去世锁须要四个条件,那么,只要这四个条件中至少有一个条件得不到知足,就不可能发生去世锁了。由于互斥条件是非共享资源所必须的,不仅不能改变,还应加以担保,以是,紧张是毁坏产生去世锁的其他三个条件。
a、毁坏“霸占且等待”条件
方法1:所有的进程在开始运行之前,必须一次性地申请其在全体运行过程中所须要的全部资源。
优点:大略易履行且安全。
缺陷:由于某项资源不知足,进程无法启动,而其他已经知足了的资源也不会得到利用,严重降落了资源的利用率,造成资源摧残浪费蹂躏。 使进程常常发生饥饿征象。
方法2:该方法是对第一种方法的改进,许可进程只得到运行初期须要的资源,便开始运行,在运行过程中逐步开释掉分配到的已经利用完毕的资源,然后再去要求新的资源。这样的话,资源的利用率会得到提高,也会减少进程的饥饿问题。
b、毁坏“不可抢占”条件 当一个已经持有了一些资源的进程在提出新的资源要求没有得到知足时,它必须开释已经保持的所有资源,待往后须要利用的时候再重新申请。这就意味着进程已霸占的资源会被短暂地开释或者说是被抢占了。 该种方法实现起来比较繁芜,且代价也比较大。开释已经保持的资源很有可能会导致进程之前的事情实效等,反复的申请和开释资源会导致进程的实行被无限的推迟,这不仅会延长进程的周转周期,还会影响系统的吞吐量。
c、毁坏“循环等待”条件 可以通过定义资源类型的线性顺序来预防,可将每个资源编号,当一个进程霸占编号为i的资源时,那么它下一次申请资源只能申请编号大于i的资源。如图所示:这样虽然避免了循环等待,但是这种方法是比较低效的,资源的实行速率回变慢,并且可能在没有必要的情形下谢绝资源的访问,比如说,进程c想要申请资源1,如果资源1并没有被其他进程霸占,此时将它分配个进程c是没有问题的,但是为了避免产生循环等待,该申请会被谢绝,这样就降落了资源的利用率
4.2、避免去世锁 ----- 在利用提高行判断,只许可不会产生去世锁的进程申请资源的去世锁避免是利用额外的考验信息,在分配资源时判断是否会涌现去世锁,只在不会涌现去世锁的情形下才分配资源。
两种避免办法:
1、如果一个进程的要求会导致去世锁,则不启动该进程
2、如果一个进程的增加资源要求会导致去世锁 ,则谢绝该申请。
把稳:预防去世锁和避免去世锁的差异:
预防去世锁是设法至少毁坏产生去世锁的四个必要条件之一,严格的防止去世锁的涌现,
而避免去世锁则不那么严格的限定产生去世锁的必要条件的存在,由于纵然去世锁的必要条件存在,也不一定发生去世锁。避免去世锁是在系统运行过程中把稳避免去世锁的终极发生。
4.2.1 常用避免去世锁的方法
4.2.1.1 有序资源分配法
这种算法资源按某种规则系统中的所有资源统一编号(例如打印机为1、磁带机为2、磁盘为3、等等),申请时必须以上升的次序。系统哀求申请进程:
1、对它所必须利用的而且属于同一类的所有资源,必须一次申请完;
2、在申请不同类资源时,必须按各种设备的编号依次申请。例如:进程PA,利用资源的顺序是R1,R2; 进程PB,利用资源的顺序是R2,R1;若采取动态分配有可能形成环路条件,造成去世锁。
采取有序资源分配法:R1的编号为1,R2的编号为2;
PA:申请次序应是:R1,R2
PB:申请次序应是:R1,R2
这样就毁坏了环路条件,避免了去世锁的发生。
4.2.1.2银里手算法
详见银里手算法.
避免去世锁的详细实现常日利用银里手算法 银里手算法a、银里手算法的干系数据构造
1)可利用资源向量Available:用于表示系统里边各种资源剩余的数目。由于系统里边拥有的资源常日都是有很多种(假设有m种),以是,我们用一个有m个元素的数组来表示各种资源。数组元素的初始值为系统里边所配置的该类全部可用资源的数目,其数值随着该类资源的分配与回收动态地改变。
2) 最大需求矩阵Max:用于表示各个进程对各种资源的额最大需求量。进程可能会有很多个(假设为n个),那么,我们就可以用一个nxm的矩阵来表示各个进程多各种资源的最大需求量
3)分配矩阵Allocation:顾名思义,便是用于表示已经分配给各个进程的各种资源的数目。也是一个nxm的矩阵。
4)需求矩阵Need:用于表示进程仍旧须要的资源数目,用一个nxm的矩阵表示。
系统可能没法一下就知足了某个进程的最大需求(常日进程对资源的最大需求也是指它在全体运行周期中须要的资源数目,并不是每一个时候都须要这么多),于是,为了进程的实行能够向前推进,常日,系统会先分配个进程一部分资源担保进程能够实行起来。那么,进程的最大需求减去已经分配给进程的数目,就得到了进程仍旧须要的资源数目了。
银里手算法通过对进程需求、霸占和系统拥有资源的实时统计,确保系统在分配给进程资源不会造成去世锁才会给与分配。
把稳:去世锁避免的优缺陷
1)去世锁避免的优点:不须要去世锁预防中的抢占和重新运行进程,并且比去世锁预防的限定要少。
2)去世锁避免的限定: 必须事先声明每个进程要求的最大资源量 考虑的进程必须无关的,也便是说,它们实行的顺序必须没有任何同步哀求的限定 分配的资源数目必须是固定的。 在霸占资源时,进程不能退出
4.2.2 常用避免去世锁的技能:
4.2.2.1 加锁顺序:(线程按照一定的顺序加锁)
4.2.2.2 加锁时限(线程考试测验获取锁的时候加上一定的时限,超过时限则放弃对该锁的要求,并开释自己霸占的锁)
4.2.2.3 去世锁检测
1]加锁顺序
当多个线程须要相同的一些锁,但是按照不同的顺序加锁,去世锁就很随意马虎发生。
如果能确保所有的线程都是按照相同的顺序得到锁,那么去世锁就不会发生。看下面这个例子:
Thread 1: lock A lock BThread 2: wait for A lock C (when A locked)Thread 3: wait for A wait for B wait for C
如果一个线程(比如线程3)须要一些锁,那么它必须按照确定的顺序获取锁。它只有得到了从顺序上排在前面的锁之后,才能获取后面的锁。
例如,线程2和线程3只有在获取了锁A之后才能考试测验获取锁C(译者注:获取锁A是获取锁C的必要条件)。由于线程1已经拥有了锁A,以是线程2和3须要一贯等到锁A被开释。然后在它们考试测验对B或C加锁之前,必须成功地对A加了锁。
按照顺序加锁是一种有效的去世锁预防机制。但是,这种办法须要你事先知道所有可能会用到的锁(译者注:并对这些锁做适当的排序),但总有些时候是无法预知的。
2]加锁时限
其余一个可以避免去世锁的方法是在考试测验获取锁的时候加一个超时时间,这也就意味着在考试测验获取锁的过程中若超过了这个时限该线程则放弃对该锁要求。若一个线程没有在给定的时限内成功得到所有须要的锁,则会进行回退并开释所有已经得到的锁,然后等待一段随机的韶光再重试。这段随机的等待韶光让其它线程有机会考试测验获取相同的这些锁,并且让该运用在没有得到锁的时候可以连续运行(译者注:加锁超时后可以先连续运行干点其它事情,再转头来重复之前加锁的逻辑)。
以下是一个例子,展示了两个线程以不同的顺序考试测验获取相同的两个锁,在发生超时后回退并重试的场景:
Thread 1 locks AThread 2 locks BThread 1 attempts to lock B but is blockedThread 2 attempts to lock A but is blockedThread 1's lock attempt on B times outThread 1 backs up and releases A as wellThread 1 waits randomly (e.g. 257 millis) before retrying.Thread 2's lock attempt on A times outThread 2 backs up and releases B as wellThread 2 waits randomly (e.g. 43 millis) before retrying.
在上面的例子中,线程2比线程1早200毫秒进行重试加锁,因此它可以先成功地获取到两个锁。这时,线程1考试测验获取锁A并且处于等待状态。当线程2结束时,线程1也可以顺利的得到这两个锁(除非线程2或者其它线程在线程1成功得到两个锁之前又得到个中的一些锁)。
须要把稳的是,由于存在锁的超时,以是我们不能认为这种场景就一定是涌现了去世锁。也可能是由于得到了锁的线程(导致其它线程超时)须要很长的韶光去完成它的任务。
此外,如果有非常多的线程同一韶光去竞争同一批资源,就算有超时和回退机制,还是可能会导致这些线程重复地考试测验但却始终得不到锁。如果只有两个线程,并且重试的超时时间设定为0到500毫秒之间,这种征象可能不会发生,但是如果是10个或20个线程情形就不同了。由于这些线程等待相等的重试韶光的概率就高的多(或者非常靠近以至于会涌现问题)。
(译者注:超时和重试机制是为了避免在同一韶光涌现的竞争,但是当线程很多时,个中两个或多个线程的超时时间一样或者靠近的可能性就会很大,因此就算涌现竞争而导致超时后,由于超时时间一样,它们又会同时开始重试,导致新一轮的竞争,带来了新的问题。)
这种机制存在一个问题,在Java中不能对synchronized同步块设置超时时间。你须要创建一个自定义锁,或利用Java5中java.util.concurrent包下的工具。写一个自定义锁类不繁芜,但超出了本文的内容。后续的Java并发系列会涵盖自定义锁的内容。
3]去世锁检测
去世锁检测是一个更好的去世锁预防机制,它紧张是针对那些不可能实现按序加锁并且锁超时也不可行的场景。
每当一个线程得到了锁,会在线程和锁干系的数据构造中(map、graph等等)将其记下。除此之外,每当有线程要求锁,也须要记录在这个数据构造中。
当一个线程要求锁失落败时,这个线程可以遍历锁的关系图看看是否有去世锁发生。例如,线程A要求锁7,但是锁7这个时候被线程B持有,这时线程A就可以检讨一下线程B是否已经要求了线程A当前所持有的锁。如果线程B确实有这样的要求,那么便是发生了去世锁(线程A拥有锁1,要求锁7;线程B拥有锁7,要求锁1)。
当然,去世锁一样平常要比两个线程相互持有对方的锁这种情形要繁芜的多。线程A等待线程B,线程B等待线程C,线程C等待线程D,线程D又在等待线程A。线程A为了检测去世锁,它须要递进地检测所有被B要求的锁。从线程B所要求的锁开始,线程A找到了线程C,然后又找到了线程D,创造线程D要求的锁被线程A自己持有着。这是它就知道发生了去世锁。
下面是一幅关于四个线程(A,B,C和D)之间锁霸占和要求的关系图。像这样的数据构培养可以被用来检测去世锁。
4.3 检测去世锁
去世锁检测算法。
算法一:
算法利用的数据构造是如下这些:
霸占矩阵A:nm阶,个中n表示并发进程的个数,m表示系统的各种资源的个数,这个矩阵记录了每一个进程当前霸占各个资源类中资源的个数。
申请矩阵R:nm阶,个中n表示并发进程的个数,m表示系统的各种资源的个数,这个矩阵记录了每一个进程当前要完成事情须要申请的各个资源类中资源的个数。
空闲向量T:记录当前m个资源类中空闲资源的个数。
完成向量F:布尔型向量值为真(true)或假(false),记录当前n个并发进程能否进行完。为真即能进行完,为假则不能进行完。
临时向量W:开始时W:=T。
算法步骤:
(1)W:=T,
对付所有的i=1,2,…,n,
如果A[i]=0,则F[i]:=true;否则,F[i]:=false
(2)找知足下面条件的下标i:
F[i]:=false并且R[i]〈=W
如果不存在知足上面的条件i,则转到步骤(4)。
(3)W:=W+A[i]
F[i]:=true
转到步骤(2)
(4)如果存在i,F[i]:=false,则系统处于去世锁状态,且Pi进程参与了去世锁。什么时候进行去世锁的检测取决于去世锁发生的频率。如果去世锁发生的频率高,那么去世锁检测的频率也要相应提高,这样一方面可以提高系统资源的利用率,一方面可以避免更多的进程卷入去世锁。如果进程申请资源不能知足就急速进行检测,那么每当去世锁形成时即能被创造,这和去世锁避免的算法附近,只是系统的开销较大。为了减小去世锁检测带来的系统开销,一样平常采纳每隔一段韶光进行一次去世锁检测,或者在CPU的利用率降落到某一数值时,进行去世锁的检测。
那么当检测出去世锁时,这些线程该做些什么呢?
1)一个可行的做法是开释所有锁,回退,并且等待一段随机的韶光后重试。这个和大略的加锁超时类似,不一样的是只有去世锁已经发生了才回退,而不会是由于加锁的要求超时了。虽然有回退和等待,但是如果有大量的线程竞争同一批锁,它们还是会重复地去世锁(编者注:缘故原由同超时类似,不能从根本上减轻竞争)。
2)一个更好的方案是给这些线程设置优先级,让一个(或几个)线程回退,剩下的线程就像没发生去世锁一样连续保持着它们须要的锁。如果授予这些线程的优先级是固定不变的,同一批线程总是会拥有更高的优先级。为避免这个问题,可以在去世锁发生的时候设置随机的优先级。
算法二:
去世锁检测与解除 ----- 在检测到运行系统进入去世锁,进行规复。 许可系统进入到去世锁状态 去世锁检测下图截自《操作系统--精髓与设计事理》
4.4 去世锁的解除
如果利用去世锁检测算法检测出系统已经涌现了去世锁 ,那么,此时就须要对系统采纳相应的方法。
常用的解除去世锁的方法:
1、抢占资源:从一个或多个进程中抢占足足数目标资源分配给去世锁进程,以解除去世锁状态。但应防止被挂起的进程永劫光得不到资源,而处于资源匮乏的状态。
2、终止(或撤销)进程:终止或撤销系统中的一个或多个去世锁进程,直至冲破去世锁状态。撤销的原则可以按进程优先级和撤销进程代价的高低进行。
a、终止所有的去世锁进程。这种办法大略粗暴,但是代价很大,很有可能会导致一些已经运行了良久的进程半途而废。
b、逐个终止进程,直至去世锁状态解除。该方法的代价也很大,由于每终止一个进程就须要利用去世锁检测来检测系统当前是否处于去世锁状态。哀求系统保持进程的历史信息,设置还原点。其余,每次终止进程的时候终止那个进程呢?每次都该当采取最优策略来选择一个“代价最小”的进程来解除去世锁状态。
一样平常根据如下几个方面来决定终止哪个进程: 1】进程的优先级
2】进程已运行韶光以及运行完成还须要的韶光
3】进程已占用系统资源
4】进程运行完成还须要的资源终止进程数目
5】进程是交互还是批处理
十一、MySQL支持的事务隔离级别
4种:
read uncommited –> 读”脏”数据征象,“脏读\"大众
read commited –> 不可重复读征象,
repeatable read(MySQL默认)–>幻读征象
serializable->可串行化,
©著作权归作者所有:来自51CTO博客作者浅嫣的原创作品,如需转载,请注明出处,否则将深究法律任务
转载地址:https://blog.51cto.com/14150615/2351911