InnoDB级别的各种锁

InnoDB级别的锁化分

按照 MySQL 官方的说法,InnoDB 中锁可以分为:

image.png

可见,InnoDB 中锁非常多,总的来说,可以如下分类:

image.png

image.png

锁的意义——解决并发事务问题

我们已经知道事务并发执行时可能带来的各种问题,最大的一个难点是:一 方面要最大程度地利用数据库的并发访问,另外一方面还要确保每个用户能以一 致的方式读取和修改数据,尤其是一个事务进行读取操作,另一个同时进行改动操作的情况下。

而一般新手朋友们都有一个疑惑。MySQL花了那么大的功夫,搞出来了MVCC机制提高并发。RR机制下,也有读提交这个机制来保证数据不会发生错乱。那还要锁这个东西干啥呢?

本文的目的就在于:除了让你了解“锁”的机制,也让了解MVCC搞定不了,只有锁能解决的业务场景。

方案一:读操作MVCC,写操作进行加锁

这就是我们平时默认操作数据库的方式。假设你启动一个事务不加锁,默认就是这个模式。

写操作加锁?我平时使用update,insert,delete也没加锁啊。确实,这些操作都是隐式加好锁了的,平时不需要你操心,安心用就好。

一致性读(Consistent Reads)/快照读

事务利用 MVCC 进行的读取操作称之为一致性读,或者一致性无锁读,也称 之为快照读,但是往往读取的是历史版本数据。所有普通的 SELECT 语句(plain SELECT)在 READ COMMITTED、REPEATABLE READ 隔离级别下都算是一致性读。(读读分离)

一致性读并不会对表中的任何记录做加锁操作,其他事务可以自由的对表中的记录做改动。(读写分离)

这个模式的强大之处就在于一致性读,所有的读操作都是不加锁的(使用MVCC机制即可),导致读-读,读-写操作都是不冲突的。那么写-写之间互斥改如何理解呢?

写-写互斥之两阶段锁

在下面的操作序列中,事务B的update语句执行时会是什么现象呢?假设字段id是表t的主键。

image

这个问题的结论取决于事务A在执行完两条update语句后,持有哪些锁,以及在什么时候释放。你可以验证一下:实际上事务B的update语句会被阻塞,直到事务A执行commit之后,事务B才能继续执行。

知道了这个答案,你一定知道了事务A持有的两个记录的行锁,都是在commit的时候才释放的。

也就是说,在InnoDB事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。(用的时候加锁,事务结束解锁)

两段式锁协议的启发

知道了这个设定,对我们使用事务有什么帮助呢?那就是,如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。我给你举个例子。

假设你负责实现一个电影票在线交易业务,顾客A要在影院B购买电影票。我们简化一点,这个业务需要涉及到以下操作:

  1. 从顾客A账户余额中扣除电影票价;
  2. 给影院B的账户余额增加这张电影票价;
  3. 记录一条交易日志。

也就是说,要完成这个交易,我们需要update两条记录,并insert一条记录。当然,为了保证交易的原子性,我们要把这三个操作放在一个事务中。那么,你会怎样安排这三个语句在事务中的顺序呢?

我们分析这三个操作。操作1一般不会有冲突的可能,自己的账号自己玩。而操作2有可能同时有多个用户交钱,共同修改影院的余额字段。操作3新增数据,只需要加隐式锁来保护这条新插入的记录在本事务提交前不被别的事务访问。

很明显,操作2最容易引发锁等待。因此把步骤2放到事务最后面,可以尽可能让个人支付流程这个事务对影院余额字段加锁的时间缩到最短。

方案二:读、写操作都采用加锁的方式

什么?MVCC这么完美的解决方案不香么?还非要给读操作也加上一把锁。那么我们来看看下面这个场景。

只用MVCC读的银行转账问题

比方在银行存款的事务中,我们要经历这三个步骤:

  1. 把账户的余额读出来,
  2. 给原金额加上本次存款的数额
  3. 写到数据库中

我们想想,如果依旧是MVCC读机制的情况下,我们将余额读出来的时候,其他事务依旧是可以读取余额的。假设你的女票打开了你们的银行账号,并且对着余额发呆。此时你继续执行了步骤2,步骤3。那么此时该账户的余额应该已经增加了,可是你发呆中的女朋友肯定发现,她盯着的页面金钱始终没变!

如果真是这样,那你只能对着你傻乎乎的女朋友说一句,刷新一下页面就好了。但是对于金钱这种敏感的数据,这样的操作是肯定不被允许的。那么此时我们发现,我们就需要在操作第步骤1的时候,就让你女朋友查不到余额才是对的。此时的读-读之间也是互斥的了。

锁解决MVCC中的各种问题

加锁解决脏读

我们说脏读的产生是因为当前事务读取了另一个未提交事务写的一条记录, 如果另一个事务在写记录的时候就给这条记录加锁,那么当前事务就无法继续读取该记录了,所以也就不会有脏读问题的产生了。

加锁解决不可重复读

不可重复读的产生是因为当前事务先读取一条记录,另外一个事务对该记录做了改动之后并提交之后,当前事务再次读取时会获得不同的值,如果在当前事务读取记录时就给该记录加锁,那么另一个事务就无法修改该记录,自然也不会发生不可重复读了。

加锁解决幻读

我们说幻读问题的产生是因为当前事务读取了一个范围的记录,然后另外的 事务向该范围内插入了新记录,当前事务再次读取该范围的记录时发现了新插入的新记录,我们把新插入的那些记录称之为幻影记录。采用加锁的方式解决幻读 问题就有不太容易了,因为当前事务在第一次读取记录时那些幻影记录并不存在, 所以读取的时候加锁就有点麻烦 —— 因为我们锁的是已经存在的行,但是新增的是原本不存在的行。当然,我们InnoDB也有锁去专门处理他,后面我们再说。

锁的实现原理——锁定读/当前读

当前读的定义是:用某个操作指令操作数据时,都是先读后写的。读取的是最新版本, 并且对读取的记录加锁。

既然加了锁,那么如果有其他活跃事务正持有这个锁,那么阻塞当前事务,等其他事务都提交后,获取最新的版本数据。

哪些语句会触发当前读呢

哪些操作会触发当前读呢?select lock in share mode (共享锁)、select for update (排他锁)、update (排他锁)、insert (排他锁)、delete (排他锁)、串行化事务隔离级别都是当前读。 当前读这种实现方式,也可以称之为 LBCC(基于锁的并发控制,Lock-Based Concurrency Control)。

当前读打破RR(可重复读)隔离级别

其实可重复读机制是有一个很严重的问题存在的。假设在执行某个事务期间,某行数据被其他事务修改并提交了。在可重复读隔离级别下,别的事务任何操作对于本事务而言,都可以视而不见。然而,假设我现在的事务也准备对这行数据进行修改的话,问题就来了。

如果此时依旧不理会其他事务对此行的修改,那么当本事务提交时也会忽略其他事务对此行的修改,别的事务相当于白忙活了!

我们来看下面这个例子

image

按照可重复读的逻辑来讲,事务C将k的值改为(1,2),事务B应该视而不见。事务B的get k仅仅看得到本事务对k的更新。因此事务B的get k的值应该是(1,2)。

可答案出乎所料,是(1,3)。也就是说事务B此次并没有对事务C视而不见。是可重复读失效了么?

配合上我们之前提出的问题,我们发现从结果上来看,InnoDB已经在可重复读隔离级别下搞定了,那么是如何搞定的呢?

这里就用到了这样一条规则:更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)。

也就是只要是更修改相关的操作,你必须更新自己的视图了。即使是可重复读级别,号称自己一张视图用到老的隔离级别,你也得换张图了。

因此,在更新的时候,当前读拿到的数据是(1,2),更新后生成了新版本的数据(1,3),这个新版本的row trx_id是101。

所以,在执行事务B查询语句的时候,一看自己的版本号是101,最新数据的版本号也是101,是自己的更新,可以直接使用,所以查询得到的k的值是3。

这里我们提到了一个概念,叫作当前读。其实,除了上面的update语句外,select lock in share mode (共享锁)、select for update (排他锁)、update (排他锁)、insert (排他锁)、delete (排他锁)、串行化事务隔离级别都是当前读。而当前读的本质实际上就是加锁。

当前读的实现原理

共享锁和独占锁(S锁和X锁)

在使用加锁的方式解决问题时,要满足写-写、读-写或写-读情况中的操作相互阻塞,读-读之间可共享,或者甚至连读-读之间也阻塞的需求。设立的锁有好几类:

共享锁,英文名:Shared Locks,简称 S 锁。在事务要读取一条记录时,需要先获取该记录的 S 锁。

独占锁,也常称排他锁,英文名:Exclusive Locks,简称 X 锁。在事务要改动一条记录时,需要先获取该记录的 X 锁。

假如事务 E1 首先获取了一条记录的 S 锁之后,事务 E2 接着也要访问这条记 录:如果事务 E2 想要再获取一个记录的 S 锁,那么事务 E2 也会获得该锁,也就意味着事务 E1 和 E2 在该记录上同时持有 S 锁。

如果事务 E2 想要再获取一个记录的 X 锁,那么此操作会被阻塞(因为E1获取了S锁),直到事务 E1 提交之后将 S 锁释放掉。

如果事务 E1 首先获取了一条记录的 X 锁之后,那么不管事务 E2 接着想获取 该记录的 S 锁还是 X 锁都会被阻塞,直到事务 E1 提交。

所以我们说 S 锁和 S 锁是兼容的,S 锁和 X 锁是不兼容的,X 锁和 X 锁也是 不兼容的,画个表表示一下就是这样:

X 不兼容 X 不兼容 S

S 不兼容 X 兼容 S

熟悉多线程的朋友看到这里直呼:这不就是个MySQL版本的读写锁么?

SELECT 语句加锁的两种方式

我们上面说的共享锁以及独占锁,都是针对查询语句而言的。其他的update (排他锁)、insert (排他锁)、delete (排他锁)这些锁都是独占锁。

当然,上面这些锁InnoDB都帮我们加好了,而真正需要我们自己去操作的只有select语句上的锁。针对查询语句的锁,就分为了一下两种:

SELECT ... LOCK IN SHARE MODE;//S共享锁
SELECT ... FOR UPDATE;//X独占锁

LOCK IN SHARE MODE共享锁

也就是在普通的 SELECT 语句后边加 LOCK IN SHARE MODE,如果当前事务执行了该语句,那么它会为读取到的记录加 S 锁,这样允许别的事务继续获取这些记录的 S 锁(比方说别的事务也使用 SELECT ... LOCK IN SHARE MODE 语句来读取 这些记录)

但如果别的事务想要获取这些记录的 X 锁,那么它们会阻塞,直到当前事务提交之后将这些记录上的 S 锁释放掉。

SELECT ... FOR UPDATE独占锁

也就是在普通的 SELECT 语句后边加 FOR UPDATE(查它是为了更新的),如果当前事务执行了该语句,那么它会为读取到的记录加 X 锁,这样既不允许别的事务获取这些记录的 S 锁(比方说别的事务使用 SELECT ... LOCK IN SHARE MODE 语句来读取这些记录), 也不允许获取这些记录的 X 锁(比如说使用 SELECT ... FOR UPDATE 语句来读取这 些记录,或者直接修改这些记录)。    

也就是其他事物想要读,写本事务加了FOR UPDATE锁的行都会被阻塞。

共享锁的意义

MVCC的意义当然是为了尽可能保证事务安全性的情况下,提高并发能力。而锁的机制会极大的影响并发。

我们现在来对比RR隔离级别,共享锁,独占锁三个对事务并发的影响

  • RR:读读共享,读写共享(当然读的是自己的版本),写写互斥。
  • 共享锁:读读共享,读写互斥,写写互斥。
  • 独占锁:读读互斥,读写互斥,写写互斥。

InnoDB的表级锁

表级意向锁(S锁)和独占锁(X锁)

我们知道,上面我们锁讲述的S锁与X锁都是针对行锁而言的。

那么行锁都能加S锁,那我的表锁当然是当人不让了!

如果深刻的理解了行级别的S锁与X锁,那么表级别的S锁与X锁你定可以自己推到出来了。

假设给某一个表持有X锁

  1. 别的事务不可以继续获得该表的 S 锁
  2. 别的事务不可以继续获得该表中任意一行的 S 锁
  3. 别的事务不可以继续获得该表的 X 锁
  4. 别的事务不可以继续获得该表中任意一行的 X 锁。

加入S锁就交给你来分析了。

表级锁的实现原理——意向锁(表级别锁)

我们思考一个问题。假设一个正在被使用表,底下有无数的行被加了行锁。这时候你想要加一个表级锁该怎么办?

假设插入表级意向(S)锁

  1. 申请S级表锁时,阻塞后续带行级别X锁的事务
  2. 等到所有活跃的行级别X锁事务退出
  3. 成功插入表级别的S锁

假设插入表级独占(X)锁

  • 申请X级表锁时,阻塞后续带行级别S,X锁的事务
  • 等到所有活跃的行级别S,X锁事务退出
  • 成功插入表级别的X锁

如何快速判断整个表中是否有活跃的行锁

那么现在的问题就是,无论是插入表级别的X锁还是S锁,我们都要判断整个表中是否有获取的X/S锁。

最容易想到的方法就是在一个循环中,不断地遍历整个表中的每一行,直到所有行锁全部释放为止,表锁成功添加。不过这样的效率可想而知。

意向锁的诞生底层实现

此时,意向锁就横空出世,解决了真个难题。

意向共享锁,英文名:Intention Shared Lock,简称 IS 锁。当事务准备在某条 记录上加 S 锁时,需要先在表级别加一个 IS 锁。

意向独占锁,英文名:Intention Exclusive Lock,简称 IX 锁。当事务准备在某条记录上加 X 锁时,需要先在表级别加一个 IX 锁。

总结一下:IS、IX 锁是表级锁,它们的提出仅仅为了在之后加表级别的 S 锁 和 X 锁时可以快速判断表中的记录是否被上锁,以避免用遍历的方式来查看表中有没有上锁的记录。

真的,一起振臂高呼:这个设计理念,这就是可重入锁的理念!

IX与IS的兼容性为问题

这两个意向锁必然是兼容的。这俩锁要是不兼容,那也就没行锁什么事儿了。此处总结一下意向锁与行锁之间的关系。

image.png

表级别的 AUTO-INC 锁

AUTO-INC锁是一个表级锁,这种锁是作用于语句的而不是事务(即语句执行完了锁就会被释放)。innodb_autoinc_lock_mode用于控制AUTO-INC的锁表逻辑,可能的取值为0,1,2。

innodb_autoinc_lock_mode=0 (每次都会产生表锁)

每条insert sql语句都会产生auto-inc表锁,每一次产生新的行记录就会进行获取一个auto_increment值。此种情况下产生的insert_id是连续的,但是并发性较差。

innodb_autoinc_lock_mode=2 (不会锁表,来一个sql则处理一个,会出现多个sql产生的insert_id相互交叉的现象)

这属于一种轻量级锁。当出现一个insert sql时,就分配一个insert id。并不需要等到整个插入语句执行完才释放锁。

我们知道分配顺序并不一定是实际落盘的顺序。因此可能会造成不同事务中的插入语句为 AUTO_INCREMENT 修饰的列生成的值是交叉的。

innodb_autoinc_lock_mode=1 (默认,1,2的混合模式。可预判行数时使用新方式,不可确定数量时使用表锁,对于simple insert会获得批量的锁,保证连续插入)

两种方式混着来(也就是在插入记录数量确定时采用轻量级锁,不确定时使用 AUTO-INC 锁)

其他存储引擎中的锁

其实这个 InnoDB 存储引擎提供的表级 S 锁或者 X 锁是相当鸡肋,只会在一些特殊情况下,比方说崩溃恢复过程中用到。

对于 MyISAM、MEMORY、MERGE 这些存储引擎来说,它们只支持表级锁, 而且这些引擎并不支持事务,所以使用这些存储引擎的锁一般都是针对当前事务来说的。比方说在 Session 1 中对一个表执行 SELECT 操作,就相当于为这个表加 了一个表级别的 S 锁,如果在 SELECT 操作未完成时,Session 2 中对这个表执行 UPDATE 操作,相当于要获取表的 X 锁,此操作会被阻塞,直到 Session 1 中的 SELECT 操作完成,释放掉表级别的 S 锁后,Session 2 中对这个表执行 UPDATE 操 作才能继续获取 X 锁,然后执行具体的更新语句。 (哎,没有意向锁真难)。

因为使用 MyISAM、MEMORY、MERGE 这些存储引擎的表在同一时刻只允许一个会话对表进行写操作,所以这些存储引擎实际上最好用在只读,或者大部分都是读操作,或者单用户的情景下。 另外,在 MyISAM 存储引擎中有一个称之为 Concurrent Inserts 的特性,支持在对 MyISAM 表读取时同时插入记录,这样可以提升一些插入速度。

行锁的使用条件

不论是使用主键索引、唯一索引或普通索引,InnoDB 都会使用行锁来对数据加锁。

只有执行计划真正使用了索引,才能使用行锁:即便在条件中使用了索引字段,但是否使用索引来检索数据是由 MySQL 通过判断不同执行计划的代价来决定的,如果 MySQL 认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下 InnoDB 将使用表锁,而不是行锁。

同时当我们用范围条件而不是相等条件检索数据,并请求锁时,InnoDB会给一批符合条件的行数据的索引项加锁。

InnoDB的三种行锁算法

Record Locks(记录锁)

记录锁,就是仅仅把一条记录锁上,官方的类型名称为:LOCK_REC_NOT_GAP。比方说我们把 number 值为 9 的那条记录加一个记录锁的

示意图如下:

image.png

记录锁是有 S 锁和 X 锁之分的,当一个事务获取了一条记录的 S 型记录锁后, 其他事务也可以继续获取该记录的 S 型记录锁,但不可以继续获取 X 型记录锁; 当一个事务获取了一条记录的 X 型记录锁后,其他事务既不可以继续获取该记录 的 S 型记录锁,也不可以继续获取 X 型记录锁

 

Gap Locks(间隙锁)

什么是幻读?

指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的数据行。

RR隔离级别如何处理幻读

我们知道,在MVCC的RR隔离级别下,是基本解决了幻读问题的(有个别极为特殊的例子出现幻读)。使用的原理就是在事务生成的时刻生成了一致性视图,并根据每行数据的事务id来进行比较来,确定视图版本。

如下面这个例子:

begin 
    select  count(*)  from table  where id >10 
    ...... 一系列的其他操作  ......
    select count(*) from table  where id >10 
commit

上面的sql 语句如果在执行的过程中(中间的一系列操作中), 其他的事务新增了 id>10 的记录。但对于本事务前后两次查询记录的结果还是一样的。

普通的行锁能解决幻读么?

假设将上述sql改为这样:

begin 
    select  count(*)  from table where id >10  for update
    ...... 一系列的其他操作  ......
    select count(*) from table  where id >10  for update
commit

此时,由于for update 的特性导致这个查询语句是使用的当前读, 会使原本事务启动时的快照失效。

如果仅仅是对 id>10 的所有已存在的行记录加锁。有用么?答案肯定是没用的。因为我插入的数据肯定是此时不存在的id。仅仅给已存在的行加锁,并不能阻止我新增一个不存在的数据。因此一旦其他的事务新增了 id>10 的记录,上述两个查询结果一定是不一致的。

用Gap Locks间隙锁来处理幻读

那么快照就不能保证解决幻读问题了。 这个时候就要用上间隙锁的概念了。

间隙锁实质上是对索引前后的间隙上锁,不对索引本身上锁。以下面的例子为例:

首先,开启第一个事务,执行以下sql(name是二级索引):

begin; 
update teacher set domain ='Spring' where name='James';

image.png

image.png

此时会对(['Jack',3],['James',9])之间, (['James',9], ['King',15])之间进行上锁。

此时另外开一个事务:

begin; 
insert into teacher value(22,'Jahes','docker');

image.png    果然,如我们所料,加锁失败了。原因很简单,就是插入的(22,'Jahes')这行数据落在了 (['James',9], ['King',15])之间。

Next-Key Locks(记录锁+间隙锁,默认使用)

当然,正常情况下,我们怎么会只锁操作行的间隙。当然也要包含扫描行自己啦!所以 InnoDB 就提出了一种称之为 Next-Key Locks 的锁,官方的类型名 称为:LOCK_ORDINARY,我们也可以简称为 next-key 锁。next-key 锁的本质就是 一个记录锁和一个 gap 锁的合体。

Next-Key Locks保证幻读

那么,继续回到我们刚才没说完的问题:

begin 
    select  count(*)  from table where id >10  for update
    ...... 一系列的其他操作  ......
    select count(*) from table  where id >10  for update
commit

这个sql的事务在执行到第一个查询语句的时候, 会加上间隙锁的, 锁住的范围是(9,+无穷] . 那么其他的事务无法再这个id范围进行任何的修改和插入操作了, 他们如果要操作就会堵住。

这样mysql又通过间隙锁的功能解决了幻读问题,但是可以看到这个代价很大的, 仅次于全表写锁了。

InnoDB有三种行锁的算法总结

  1. Record Lock:单个行记录上的锁。
  2. Gap Lock:间隙锁,锁定一个范围,但不包括记录本身。GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况。
  3. Next-Key Lock:1+2,锁定一个范围,并且锁定记录本身。对于行的查询,都是采用该方法,主要目的是解决幻读的问题。

默认情况下,所有的行锁都使用的Next-Key Lock算法。

那有人有觉得怪了。有了Next-Key Lock不是很全面么?为啥还需要Record Lock,Gap Lock这俩锁,直接打包合并不就完了。我们接着来看这俩锁的用武之地。

Next-Key Lock的变化

Next-Key Lock退化成间隙锁

假设我们查询的语句一个根据一个索引列的值,查询数据却没有查到时加锁么?(name是二级索引)

 

 image.png

select * from teache where name='HRD' for update;

我们发现,这时候表里没有HRD这行数据,那Next-Key Lock就不加了么?我们想想,

begin;
select * from teache where name='HRD' for update;----事务A执行

//其他事务执行
insert into teacher value(2,'HRD','docker');----事务B执行

select * from teache where name='HRD' for update;----事务A执行

很明显,又出现幻读了。那么解决方案就是:根据索引查询一个查不到的值时,会退化成一个间隙锁。

image.png

这下你别的事务也别想搞事了。

Next-Key Lock用不成,只能用表锁

我们说,一个普通无索引字段作为查询条件,由于没有索引,也没有顺序可言,因此Next-Key Lock用不成,直接加表锁。悄悄,不写索引你以为只有影响查询效率么?锁层面的影响更是大到夸张。

Next-Key Lock升级为行锁

与唯一索引做等值查询加锁的时候,next-key lock升级为行锁。很明显,间隙锁的产生原因离不开"范围"这两个关键字,而唯一索引固定只对应一条数据,因此没有范围的概念,因此升级为行锁,提高并发。

加锁规则总结

  1. 加锁的基本单位是(next-key lock),他是前开后闭原则
  2. 使用自增id插入对象时,会加表级别的 AUTO-INC 锁
  3. 索引上的等值查询--给唯一索引加锁的时候,next-key lock升级为行锁
  4. 索引上的等值查询--向右遍历时最后一个值不满足查询需求时,next-key lock 退化为间隙锁
  5. 唯一索引上的范围查询会访问到不满足条件的第一个值为止
  6. 查询条件用不上索引时,直接加表锁

MySQL发生死锁了该怎么办?

当并发系统中不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态,称为死锁。这里我用数据库中的行锁举个例子。

image.png

这时候,事务A在等待事务B释放id=2的行锁,而事务B在等待事务A释放id=1的行锁。 事务A和事务B在互相等待对方的资源释放,就是进入了死锁状态。当出现死锁以后,有三种策略:

  • 第一种策略:修改代码,让事务A,事务B的更新顺序一致。从根本上避免死锁
  • 第二种策略是,直接进入等待,直到超时。这个超时时间可以通过参数innodb_lock_wait_timeout来设置。
  • 第三种策略是,发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数innodb_deadlock_detect设置为on,表示开启这个逻辑。

在InnoDB中,innodb_lock_wait_timeout的默认值是50s,意味着如果采用第一个策略,当出现死锁以后,第一个被锁住的线程要过50s才会超时退出,然后其他线程才有可能继续执行。对于在线服务来说,这个等待时间往往是无法接受的。

但是,我们又不可能直接把这个时间设置成一个很小的值,比如1s。这样当出现死锁的时候,确实很快就可以解开,但如果不是死锁,而是简单的锁等待呢?所以,超时时间设置太短的话,会出现很多误伤。

所以,在第一种方法暂时无法实现的情况下(事实上实际开发中很难按照第一种方案严格执行)。我们就需要使用第三种方案,即:主动死锁检测,而且innodb_deadlock_detect的默认值本身就是on。主动死锁检测在发生死锁的时候,是能够快速发现并进行处理的,但是它也是有额外负担的。

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大将黄猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值