哇靠,死锁了之innodb锁解读

Posted 破产DBA

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了哇靠,死锁了之innodb锁解读相关的知识,希望对你有一定的参考价值。

为什么要有锁?

如果我回答锁的出现大大提高了数据库的并发性,维护了数据的一致性,你一定会认为这也太套路了,理论性的东西谁都可以照着念,所以我想从另一个角度来解答这个问题。

“如果数据库没有锁会怎么样?”

我们假设一个场景

京东电商场景,最新款iPhoneX以1500元的价格参与秒杀,但参与秒杀的不可能有那么多,只有5部,在秒杀前做足了宣传,总共大概有3000被吸引到了秒杀页面等待最后的时刻。

假设在秒杀的那一刻有1000个网速比较块,动作敏捷的幸运儿提交了订单,那么这1000个幸运儿将会执行:

Update product set stock= stock-1 where id=’1’ and stock>0;

由于没锁,1000个进程连接执行成功,实际上到最后stock仅仅是减了1,后面还有2000个慢一点连接还可以继续。

 如果没有锁,京东的这场活动就是一场自残秀了。

   (上面之所以没提1000个事务,是因为事务的基础是锁,没有锁也就无法建立事务了)

(本来是想按照以下思维导图的逻辑写,但写了一半才发现,这个主题太大了,单锁的类别,加锁方式,MVCC都可以单独写一篇长文了,能力有限,所以还是着重挑了一些,后续将补充相关主题.)

ACID的需要

数据库事务正确执行的四个基本要素: 原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)

其中隔离性需要使用锁来实现

哇靠,死锁了(二)之innodb锁解读

INNODB常见的几种锁

共享锁(Share Lock)

也叫S锁,若事务A对表记录1加了共享锁,则其他事务只能加共享锁,不能加排他锁,通俗的讲,别的事务只能读,不能做修改操作。

显示的指定锁定方法:Select … lock in share mode

举例:

事务A对记录id=5添加S锁:

begin;

select * from tb where id=5 lock in share mode;

..

在事务A未提交的情况下事务B也想添加S锁,然后再修改此记录:

begin;

select * from tb where id=5 lock in share mode; --OK,没问题。

update tb set name=’xxx’ where id=5;  ---此时无法继续, 必须等到事务A提交或回滚才能执行

排他锁(Exclusive Lock)

也叫X锁,写锁,独占锁,事务对数据A加了X锁后,其他事务不能对数据A加任何锁。

Update,delete,insert都会对相应记录添加X锁;

也可以显示的给指定数据添加X锁:select ... for update

举例:

举例:

事务A对id=5添加排他锁(使用for update或直接执行update、delete语句效果类似):

begin;

select * from tb where id=5 for update;

事务B需要对数据做select,delete操作

begin;

select * from tb where id=5 for update; -无法继续,执行lock in share mode也不支持;

间隙锁(GAP LOCK)

Gap锁,中文名间隙锁,锁住的不是记录,而是范围,比如:(negative infinity, 10),(10, 11)区间,主要作用是用来防止insert的,从而防止出现幻读,是“可重复读”隔离级别的基础。

实验;

test表结构:(id int 自增主键,code int 普通索引)

包含数据:

哇靠,死锁了(二)之innodb锁解读

现在事务A删除code=4的记录,经过实验我们发现,处理code=4记录本身会添加X锁外,还会对【2,4),(4,6)之间添加gap锁,其中包括前一条记录的边界值2、

哇靠,死锁了(二)之innodb锁解读

NEXT-KEY LOCK

Next-Key Locks = Gap Locks + Record Locks 的结合, 不仅仅锁住记录,还会锁住间隙,比如上个例子中记录code=4的x锁,也有周边的gap锁;

意向锁

意向锁是表级锁,作用是告诉下一个事务,我即将添加什么锁,你就不要排队了。

InnoDB 中的两个表锁:

意向共享锁(IS):事务准备对表中数据加共享锁,提示别的事务此时不要添加表级X锁

意向排他锁(IX):类似于IS,只是即将对表中数据添加X锁。

锁的兼容性

  • S锁只与S锁兼容,X锁与什么锁都不兼容

哇靠,死锁了(二)之innodb锁解读

  • IS锁除了X锁外都兼容,IX锁只与IS,IX兼容

哇靠,死锁了(二)之innodb锁解读

隔离级别

常见的4种隔离级别

  • Read Uncommited

可以读取未提交记录。此隔离级别,不会使用,忽略。

  • Read Committed (RC)

快照读忽略,本文不考虑。

针对当前读,RC隔离级别保证对读取到的记录加锁 (记录锁),存在幻读现象。

  • Repeatable Read (RR)

快照读忽略,本文不考虑。

针对当前读,RR隔离级别保证对读取到的记录加锁 (记录锁),同时保证对读取的范围加锁,新的满足查询条件的记录不能够插入 (间隙锁),不存在幻读现象。

  • Serializable

从MVCC并发控制退化为基于锁的并发控制。不区别快照读与当前读,所有的读操作均为当前读,读加读锁 (S锁),写加写锁 (X锁)。

Serializable隔离级别下,读写冲突,因此并发度急剧下降,在mysql/InnoDB下不建议使用。

隔离级别能解决的问题

脏读:读取到了别的事务未提交的数据

不可重复读:第一次读取数据与第二次读取的不同,因为其他事务对数据做了修改且已提交;

幻读:连续两次读取的数据行数不一样;

哇靠,死锁了(二)之innodb锁解读

分析锁的方法

工欲善其事必先利其器

查看最后一次死锁信息 show innodb engine status;

查看LATEST DETECTED DEADLOCK这一栏的相关信息。

查看正在使用的锁

SELECT r.trx_id waiting_trx_id,

r.trx_query waiting_query,

b.trx_id blocking_trx_id,

b.trx_query blocking_query,

b.trx_mysql_thread_id blocking_thread,

b.trx_started,

b.trx_wait_started

FROM information_schema.innodb_lock_waits w

INNER JOIN information_schema.innodb_trx b

ON b.trx_id = w.blocking_trx_id

INNER JOIN information_schema.innodb_trx r

ON r.trx_id = w.requesting_trx_id

查看事务的隔离级别

show variables like 'tx_isolation';

典型场景下锁解读

select做insert或create table添加S锁

insert into target_tab select * from source_tab where ...

create table new_tab as select ... From source_tab where ...

在RR隔离级别下,会对source_tab上锁,防止出现幻读;RC隔离级别下,不上锁。

以下是来自何登成老师的经典加锁分析

http://hedengcheng.com/?p=771 MySQL加锁处理分析

以下语句怎么加锁?

delete from t1 where id = 10;

这个问题没有答案。如果让我来回答这个问题,我必须还要知道以下的一些前提,前提不同,我能给出的答案也就不同。要回答这个问题,还缺少哪些前提条件?

前提一:id列是不是主键?

前提二:当前系统的隔离级别是什么?

前提三:id列如果不是主键,那么id列上有索引吗?

前提四:id列上如果有二级索引,那么这个索引是唯一索引吗?

前提五:两个SQL的执行计划是什么?索引扫描?全表扫描?

下面来一一分析:

组合一:id列是主键,RC隔离级别

id是主键,Read Committed隔离级别,给定SQL:delete from t1 where id = 10; 只需要将主键上,id = 10的记录加上X锁即可。

哇靠,死锁了(二)之innodb锁解读

组合二:id列是二级唯一索引,RC隔离级别

id不是主键,而是一个Unique的二级索引键值。那么在RC隔离级别下,首先会将unique索引上的id=10索引记录加上X锁,同时还会对对应主键上加X锁

哇靠,死锁了(二)之innodb锁解读

组合三:id列是二级非唯一索引,RC隔离级别

与组合二唯一的区别在于,组合二最多只有一个满足等值查询的记录,而组合三会将所有满足查询条件的记录都加锁。

哇靠,死锁了(二)之innodb锁解读

组合四:id列上没有索引,RC隔离级别

由于id列上没有索引,因此只能走聚簇索引,进行全部扫描。从图中可以看到,满足删除条件的记录有两条,但是,聚簇索引上所有的记录,都被加上了X锁。

哇靠,死锁了(二)之innodb锁解读

组合五:id列是主键,RR隔离级别

与组合一加锁相同

组合六:id列是二级唯一索引,RR隔离级别

与组合二一致

组合七:id列是二级非唯一索引,RR隔离级别

通过id索引定位到第一条满足查询条件的记录,加记录上的X锁,加GAP上的GAP锁,然后加主键聚簇索引上的记录X锁,然后返回;

组合八:id列上没有索引,RR隔离级别

只能进行全表扫描。最终的加锁情况,如下图所示:


以上是关于哇靠,死锁了之innodb锁解读的主要内容,如果未能解决你的问题,请参考以下文章

mysql之show engine innodb status解读(转)

InnoDB死锁示例

巧用MySQL InnoDB引擎锁机制解决死锁问题(转)

mysql innodb 行锁解锁后出现死锁

MySQL——innodb锁

查看锁信息(开启InnoDB监控)