设为首页 加入收藏

TOP

innodb的锁讲解(二)
2019-03-05 16:09:42 】 浏览:143
Tags:innodb 讲解
行锁

根据前面的讲述,假如我们要针对某一条记录加排他锁的话,那么会在记录对应的表里面先加一个共享排他锁,然后再到记录上面加一个排他锁,这时候我们可以发现session1可以针对student表加了一个共享排他锁了,那么这时候session2发现student上面已经有其他事务加上共享排他锁了,因此会阻塞。如果没有意向锁的话,那session2就要走方法2了,那么就需要判断每一行,那么需要遍历整个表,这种效率非常差,特别碰到表数据量大的时候。

意向共享锁

事务想要获取一个表中某几行的共享锁

意向排他锁

事务想要获取一个表中某几行的排他锁

请注意:对于insert、update、delete,InnoDB会自动给涉及的数据加排他锁(X);对于一般的 Select 语句,InnoDB 不会加任何锁,可能有人会有疑问,什么是一般的select,什么是特殊的select呢,一般的select就是select column from table where x=1 特殊的select就是select column from table where x=1 lock in share mode或者select column from table where x=1 for udpate ,前面加共享锁,后者加排他锁

行锁

Record Locks

记录锁是索引记录上的锁,例如SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;防止任何其他事务插入,更新或删除t.c1的值为10的行

记录锁始终锁定索引记录,如果一个表没有定义任何的索引,像这种情况,innodb会创建一个隐藏的聚簇索引并且使用此索引进行记录锁定

需要注意的是:

innodb的记录锁是针对索引加锁,不是针对物理记录加锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,将出现锁冲突

Gap Locks

间隙锁是锁定索引记录之间的间隙,或锁定第一个索引记录之前或最后一个索引记录之后的间隙上。。例如 SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;由于范围内所有现有值之间的间隔都被锁定,因此可以防止其他事务向列t.c1中插入值15,无论该列中是否已有任何此类值。

对于使用唯一索引锁定行的语句,不需要使用间隙锁(这不包括搜索条件仅包括多列唯一索引的一些列的情况; 在这种情况下,确实会出现间隙锁定),例如,如果id列是一个唯一索引,那么下面的语句只会在id=100的那行上面加一个索引记录锁,而不会关心别的会话(session)是否在前面的间隙中插入数据。

SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;



如果id没有索引或者没有唯一索引,则该语句将锁定前述的间隙

间隙锁的主要作用,就是和记录锁组成Next-key锁,解决幻读问题

间隙锁在不同的隔离级别下,有着不同的作用范围,能发挥间隙锁作用的,是’REPEATTABLE READ’隔离级别,在这个级别下使用带有间隙锁的Next-Key锁,解决了幻行的问题。这个涉及到了事务隔离级别和一致读的相关信息,后面我也会更新对应的文章

Next-Key Locks

next-key锁是索引记录上的记录锁和索引记录之前的间隙上的间隙锁的组合,也就是相当于Record Locks+Gap Locks

InnoDB以这种形式实现行级锁,当它查找或扫描表索引的时候,它会在遇到的索引记录上设置共享或排它锁。因此,行级锁实际上是索引记录锁。next-key锁同样会影响索引记录之前的间隙。就是说,next-key 锁就是一个索引记录锁加上索引记录前间隙的间隙锁。如果一个会话拥有记录 R 的索引上面的一个共享锁或独占锁,另一个会话不能在索引顺序中的R之前的间隙中插入新的索引记录。

下面我们来看一个表

mysql> create table goods(

-> id int not null auto_increment primary key,

-> title varchar(32) not null default '' comment '商品名称',

-> classify tinyint not null default 0 comment '商品类型',

-> index idx_classify (classify)

-> )engine=innodb charset=utf8;

mysql> insert into goods (title,classify) values ('商品1',1),('商品2',3),('商品3',5),('商品4',8),('商品5',10),('商品6',1),('商品7',3),('商品8',5),('商品9',8),('商品10',10);

mysql> select * from goods;

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

| id | title | classify |

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

| 1 | 商品1 | 1 |

| 2 | 商品2 | 3 |

| 3 | 商品3 | 5 |

| 4 | 商品4 | 8 |

| 5 | 商品5 | 10 |

| 6 | 商品6 | 1 |

| 7 | 商品7 | 3 |

| 8 | 商品8 | 5 |

| 9 | 商品9 | 8 |

| 10 | 商品10 | 10 |

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

10 rows in set (0.00 sec)

mysql> select distinct(classify) from goods;

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

| classify |

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

| 1 |

| 3 |

| 5 |

| 8 |

| 10 |

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

5 rows in set (0.02 sec)

#在REPEATTABLE READ隔离级别下,执行查询时,因为Next-Key锁存在,写Next-Key的锁定范围如下

(-∞,1) 锁定索引项1和1之前的间隙,因为1之前没有其他索引项,所以负无穷

(1,3) 锁定1和3之前的间隙,不包括1,包括3

(3,5) 同上

(5,8) 同上

(8,10) 同上

(10,∞) 锁定索引项10和10之后的间隙,因为10之后没有其他索引项,所以为正无穷

下面我们就用一个demo来验证一下

Session1

mysql> start transaction;

Query OK, 0 rows affected (0.00 sec)

mysql> select * from goods where classify=3 for update;

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

| id | title | classify |

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

| 2 | 商品2 | 3 |

| 7 | 商品7 | 3 |

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

2 rows in set (0.00 sec)

Session2

mysql> start transaction;

Query OK, 0 rows affected (0.00 sec)

mysql> insert into goods (title,classify) select '商品11',4;

#这时候可以看到session2给阻塞了,因为session1的Next-Key的锁定范围是(3,5),正好包含了4

my
编程开发网

首页 上一页 1 2 3 下一页 尾页 2/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇MySQL基础解析 下一篇事务隔离级别

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

array(4) { ["type"]=> int(8) ["message"]=> string(24) "Undefined variable: jobs" ["file"]=> string(32) "/mnt/wp/cppentry/do/bencandy.php" ["line"]=> int(217) }