zoukankan      html  css  js  c++  java
  • mysql innodb锁详解

    文章转载自http://www.2cto.com/database/201508/429967.html

    InnoDB锁模式

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

    共享锁(S:允许一个事务去读一行,阻止其他事务获得相同的数据集的排他锁。

    排他锁(X:允许获得排他锁的事务更新数据,但是组织其他事务获得相同数据集的共享锁和排他锁。

    可以这么理解:

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

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

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

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

    InnoDB行锁模式兼容列表:

    加载中...

    注意:

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

    意向锁是InnoDB自动加的,不需要用户干预。

    对于insert、update、delete,InnoDB会自动给涉及的数据加排他锁(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 Lock和Next-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]和(6,9] 都加入了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、当事务需要更新大部分数据时,表又比较大,如果使用默认的行锁,不仅效率低,而且还容易造成其他事务长时间等待和锁冲突。

    2、事务比较复杂,很可能引起死锁导致回滚。

    死锁:

    我们说过MyISAM中是不会产生死锁的,因为MyISAM总是一次性获得所需的全部锁,要么全部满足,要么全部等待。而在InnoDB中,锁是逐步获得的,就造成了死锁的可能。

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

    发生死锁后,InnoDB一般都可以检测到,并使一个事务释放锁回退,另一个获取锁完成事务。

    避免死锁:

    有多种方法可以避免死锁,这里只介绍常见的三种:

    1、如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。

    2、在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;

    3、对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率;

    既不回头,何必不忘; 既然无缘,何须誓言; 今日种种,逝水无痕; 明夕何夕,君已陌路;
  • 相关阅读:
    luogu P3128 [USACO15DEC]最大流Max Flow (树上差分)
    codeforces 600E . Lomsat gelral (线段树合并)
    bzoj 1483: [HNOI2009]梦幻布丁 (链表启发式合并)
    bzoj 1257: [CQOI2007]余数之和 (数学+分块)
    codevs 2606 约数和问题 (数学+分块)
    bzoj 2038: [2009国家集训队]小Z的袜子(hose) (莫队)
    bzoj 1086: [SCOI2005]王室联邦 (分块+dfs)
    bzoj 4542: [Hnoi2016]大数 (莫队)
    【NOIp模拟赛】Tourist Attractions
    【NOIp模拟赛】String Master
  • 原文地址:https://www.cnblogs.com/zyjfire/p/6753726.html
Copyright © 2011-2022 走看看