Mysql中那些锁机制之InnoDB

我们知道mysql在曾经。存储引擎默认是MyISAM。可是随着对事务和并发的要求越来越高,便引入了InnoDB引擎。它具有支持事务安全等一系列特性。

InnoDB锁模式

InnoDB实现了两种类型的行锁。

共享锁(S同意一个事务去读一行,阻止其它事务获得同样的数据集的排他锁。

排他锁(X同意获得排他锁的事务更新数据,可是组织其它事务获得同样数据集的共享锁和排他锁。

能够这么理解:

共享锁就是我读的时候,你能够读,可是不能写。排他锁就是我写的时候。你不能读也不能写。事实上就是MyISAM的读锁和写锁,可是针对的对象不同了而已。

除此之外InnoDB还有两个表锁:

意向共享锁(IS表示事务准备给数据行增加共享锁。也就是说一个数据行加共享锁前必须先取得该表的IS

意向排他锁(IX类似上面,表示事务准备给数据行增加排他锁,说明事务在一个数据行加排他锁前必须先取得该表的IX锁。


InnoDB行锁模式兼容列表:



注意:

当一个事务请求的锁模式与当前的锁兼容。InnoDB就将请求的锁授予该事务;反之假设请求不兼容,则该事务就等待锁释放。

意向锁是InnoDB自己主动加的。不须要用户干预。

对于insertupdatedeleteInnoDB会自己主动给涉及的数据加排他锁(X);对于一般的Select语句,InnoDB不会加不论什么锁,事务能够通过下面语句给显示加共享锁或排他锁。

共享锁:select * from table_name where .....lock in share mode

排他锁:select * from table_name where .....for update


增加共享锁的样例:




利用select ....for update增加排他锁





锁的实现方式:

InnoDB行锁是通过给索引项加锁实现的。假设没有索引,InnoDB会通过隐藏的聚簇索引来对记录加锁。

也就是说:假设不通过索引条件检索数据。那么InnoDB将对表中全部数据加锁。实际效果跟表锁一样。

行锁分为三种情形:

Record lock :对索引项加锁。即锁定一条记录。

Gap lock对索引项之间的‘间隙’、对第一条记录前的间隙或最后一条记录后的间隙加锁,即锁定一个范围的记录,不包括记录本身

Next-key Lock:锁定一个范围的记录并包括记录本身(上面两者的结合)。

注意:InnoDB默认级别是repeatable-read级别,所以以下说的都是在RR级别中的。


之前一直搞不懂Gap LockNext-key Lock的差别,直到在网上看到一句话豁然开朗,希望对各位有帮助。

Next-Key Lock是行锁与间隙锁的组合。这样,当InnoDB扫描索引记录的时候。会首先对选中的索引记录加上行锁(Record Lock,再对索引记录两边的间隙加上间隙锁(Gap Lock

假设一个间隙被事务T1加了锁,其他事务是不能在这个间隙插入记录的

干巴巴的说没意思,我们来看看详细实例:

如果我们有一张表:

+----+------+

| id | age  |

+----+------+

|  1 |    3 |

|  2 |    6 |

|  3 |    9 |

+----+------+

表结构例如以下:

CREATE TABLE `test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `keyname` (`age`)
) ENGINE=InnoDB AUTO_INCREMENT=302 DEFAULT CHARSET=gbk ;


这样我们age段的索引就分为

(negative infinity, 3],

(3,6],

(6,9],

(9,positive infinity)


我们来看一下几种情况:


1、当事务A运行下面语句:

mysql> select * from fenye where age=6for update ;

不仅使用行锁锁住了对应的数据行。同一时候也在两边的区间,(5,6]和(69] 都增加了gap锁。

这样事务B就无法在这个两个区间insert进新数据,可是事务B能够在两个区间外的区间插入数据。

2、当事务A运行

select * from fenye where age=7 for update ;

那么就会给(6,9]这个区间加锁,别的事务无法在此区间插入或更新数据。

3、假设查询的数据不再范围内,

比方事务A运行 select * from fenye where age=100 for update ;

那么加锁区间就是(9,positive infinity)。


小结:

行锁防止别的事务改动或删除,GAP锁防止别的事务新增。行锁和GAP锁结合形成的的Next-Key锁共同攻克了RR级别在写数据时的幻读问题。


何时在InnoDB中使用表锁:

InnoDB在绝大部分情况会使用行级锁,由于事务和行锁往往是我们选择InnoDB的原因,可是有些情况我们也考虑使用表级锁。

1、当事务须要更新大部分数据时。表又比較大。假设使用默认的行锁,不仅效率低。并且还easy造成其它事务长时间等待和锁冲突。

2、事务比較复杂。非常可能引起死锁导致回滚。

死锁:

我们说过MyISAM中是不会产生死锁的,由于MyISAM总是一次性获得所需的所有锁,要么所有满足。要么所有等待。

而在InnoDB中。锁是逐步获得的。就造成了死锁的可能。

在上面的样例中我们可以看到。当两个事务都须要获得对方持有的锁才可以继续完毕事务,导致两方都在等待。产生死锁。

发生死锁后,InnoDB一般都能够检測到,并使一个事务释放锁回退。还有一个获取锁完毕事务。

避免死锁:

有多种方法能够避免死锁,这里仅仅介绍常见的三种:

1、假设不同程序会并发存取多个表,尽量约定以同样的顺序訪问表。能够大大减少死锁机会。

2、在同一个事务中。尽可能做到一次锁定所须要的全部资源,降低死锁产生概率;

3、对于很easy产生死锁的业务部分,能够尝试使用升级锁定颗粒度,通过表级锁定来降低死锁产生的概率;



參考:

Innodb中的事务隔离级别和锁的关系

MySQL InnoDB锁机制(二)

Innodb锁机制:Next-Key Lock 浅谈

深入浅出Mysql数据库开发、优化与管理维护.第二版






posted on 2017-06-28 21:52  ljbguanli  阅读(168)  评论(0编辑  收藏  举报