InnoDB引擎的行锁

InnoDB引擎特点简述

  • 支持事务
  • 支持外键
  • 必须要有主键,聚焦索引
  • 不支持全文检索
  • 使用行锁

行锁特点

  支持事务,加锁开销大,加锁慢,会出现死锁,锁的粒度小,并发下等待锁的概率较低,所以支持高并发。

手动事务语法

手动测试行锁需要首先关闭自动提交,每个会话都需要关闭自动提交

mysql> SHOW VARIABLES LIKE 'autocommit';    #查看
mysql> SET autocommit = 0;

 

中间执行SQL

最后执行完了需要进行提交

mysql> COMMIT;

最佳实践

步骤 Session1 Session2
1 可以查询innodb_lock表数据  可以查询innodb_lock表数据
2 可以增删改innodb_lock表数据 可以查询旧数据,增删改innodb_lock表数据阻塞
3 commit commit同时阻塞的增删改成功
4 可以增删改innodb_lock表数据 增删改innodb_lock表与Session1不同行数据也阻塞
5 commit commit同时阻塞的增删改成功
6 没能获取session2更新的数据 commit
7 可以查询innodb_lock表更新后数据  

 

 

 

 

 

 

 

 

这里有几个问题:

  • 这两个session,谁先进行的增删改,谁就先拿到锁。但是在查询的时候,如果session1进行了一次SELECT操作,而session2提交之后,session1再一次的SELECT是无法获取session2更新的值的,必须session1进行提交后才可以更新数据。
  • 在第3步,session2会将session1更新的数据覆盖掉
  • 在第4步,session2修改的与session1不同的行也会阻塞,在这里其实session1的锁是表锁,只有用上索引之后才能变为行锁。

针对给表加索引,让表锁变行锁的实践

表名:innodb_lock

 

步骤 Session1 Session2
1 CREATE INDEX idx_id ON innodb_lock(id);  
2 COMMIT;  
3 UPDATE innodb_lock SET color = 'pink' WHERE id = 1; UPDATE innodb_lock SET color = 'yellow' WHERE id = 2;
4 COMMIT; COMMIT;

 

 

 

 

 

在这里两个session可以同时更新数据,实现行锁。我们可以看出这个索引落在WHERE语句参数的ID上,假如没有WHERE 条件并且没有索引是否也可成功?

步骤 Session1 Session2
1 DROP INDEX idx_id ON innodb_lock;  
2 COMMIT;  
3 UPDATE innodb_lock SET color = 'red' WHERE id = 1; UPDATE innodb_lock SET color = 'black';
4 COMMIT; COMMIT;

 

 

 

 

 

在第3行session2发生阻塞,因为没有索引则会发生阻塞。

 手动锁定记录

这种锁方式也是MySQL实现悲观锁的方式

mysql> BEGIN;
mysql> SELECT ... FOR UPDATE;

在中间进行数据更新

mysql> COMMIT;

最后进行提交。

最佳实践

1、还是innodb_lock这张表和数据,自动提交开启的情况和没有索引的实践手动锁定记录。

步骤 Session1 Session2
1 BEGIN;  
2 SELECT * FROM innodb_lock WHERE id = 1 FOR UPDATE;  UPDATE innodb_lock SET color = 'orange' WHERE id = 1; #阻塞
3 UPDATE innodb_lock SET color = 'pink' WHERE id = 1;  
4 COMMIT; #阻塞释放,执行更新,数据被覆盖为orange

 

 

 

 

 

2、上一个测试两个session修改的是同一条数据,如果修改不一样的数据,是否还会发生阻塞

步骤 Session1 Session2
1 BEGIN;  
2 SELECT * FROM innodb_lock WHERE id = 1 FOR UPDATE;  UPDATE innodb_lock SET color = 'yellow' WHERE id = 2; #阻塞
3 UPDATE innodb_lock SET color = 'red' WHERE id = 1;  
4 COMMIT; #阻塞释放,执行更新

 

 

 

 

 

3、这里可以看到session1对数据表进行了锁表,那我们给id加上索引是否就不会阻塞

步骤 Session1 Session2
1 CREATE INDEX idx_id ON innodb_lock(id);  
2 BEGIN;  
3 SELECT * FROM innodb_lock WHERE id = 1 FOR UPDATE;  UPDATE innodb_lock SET color = 'blue' WHERE id = 2; #未阻塞
4 UPDATE innodb_lock SET color = 'pink' WHERE id = 1;  
5 COMMIT;  

 

 

 

 

 

 

4、session2未发生阻塞,那么假如锁定的行与更新的行不是一个行那session2能更新哪行呢

步骤 Session1 Session2
1 BEGIN;  
2 SELECT * FROM innodb_lock WHERE id = 1 FOR UPDATE;  
3 UPDATE innodb_lock SET color = 'yellow' WHERE id = 2;  UPDATE innodb_lock SET color = 'red' WHERE id = 1;  #阻塞
4   UPDATE innodb_lock SET color = 'orange' WHERE id = 2;  #阻塞
5   UPDATE innodb_lock SET color = 'grey' WHERE id = 3;  #未阻塞
6 COMMIT;  

 

 

 

 

 

 

 

5、我们发现id为1和2的两行都被锁住了,如果这样的话是不是说明只要是锁定或更新的行都会被锁

步骤 Session1 Session2
1 INSERT INTO innodb_lock VALUES(4, 'red'),(5, 'orange');  
1 BEGIN;  
2 SELECT * FROM innodb_lock WHERE id < 3 FOR UPDATE;  
3 UPDATE innodb_lock SET color = 'black' WHERE id > 3;  UPDATE innodb_lock SET color = 'black' WHERE id = 1;  #阻塞
4   UPDATE innodb_lock SET color = 'black' WHERE id = 2;  #阻塞
5   UPDATE innodb_lock SET color = 'black' WHERE id = 3;  #阻塞
6    UPDATE innodb_lock SET color = 'black' WHERE id = 4;  #阻塞
7   UPDATE innodb_lock SET color = 'black' WHERE id = 5;  #阻塞
8 COMMIT;  

 

 

 

 

 

 

 

 

 

 

6、不会吧,全部阻塞了,id为3的数据明明没在锁住和更新的数据里,这是为什么呢?我们再试一组数据。

步骤 Session1 Session2
1 INSERT INTO innodb_lock VALUES(6, 'purple'),(7, 'white');  
2 BEGIN;  
3 SELECT * FROM innodb_lock WHERE id < 3 FOR UPDATE;  
4 UPDATE innodb_lock SET color = 'black' WHERE id > 5; UPDATE innodb_lock SET color = 'black' WHERE id = 2;  #阻塞
5   UPDATE innodb_lock SET color = 'black' WHERE id = 3;  #阻塞
6   UPDATE innodb_lock SET color = 'black' WHERE id = 4;  #未阻塞
7   UPDATE innodb_lock SET color = 'black' WHERE id = 5;  #未阻塞
8   UPDATE innodb_lock SET color = 'black' WHERE id = 6;  #阻塞
9   INSERT INTO innodb_lock VALUES(8, 'test');  #阻塞
10 COMMIT;  

 

 

 

 

 

 

 

 

 

 

我再添加两条数据,我知道的颜色单词就这些了,多一个也想不起来了。

我嘞个去,结果让人匪夷所思,小于号是后妈养的吗?为什么3就阻塞而5就不阻塞。

好吧,算你厉害,大家还是记住这个结果吧。

这里这种范围的锁定就是传说中的间隙锁,他有一种特点就是只要在范围之内的数据全部被锁住,不管当前是否存在。

7、如果有两张innodb的表,两个session锁住各一张表,然后再去更新对方的那张表会怎样呢?

步骤 Session1 Session2
1 BEGIN;  BEGIN;
2 SELECT * FROM innodb_lock WHERE id = 1 FOR UPDATE;  SELECT * FROM innodb_test WHERE id = 1 FOR UPDATE;
3 UPDATE innodb_test SET color = 'pink' WHERE id = 1;  #阻塞 UPDATE innodb_lock SET color = 'pink' WHERE id = 1;  #阻塞
4 COMMIT;  

 

 

 

 

 

在第3行都发生了阻塞,而在Session2的第3行出现以下错误

这里出现了传说中的死锁,就是两个session吃着自己碗里的还看着对方碗里的,双方还都是倔脾气,就在这里杠上了,谁也不服谁。

查看行锁的信息

  • Innodb_row_lock_current_waits:当前正在等待的数量
  • Innodb_row_lock_time:从启动到现在锁定的总时长,单位ms
  • Innodb_row_lock_time_avg:锁等待的平均时长,单位ms
  • Innodb_row_lock_time_max:等待锁时间最长的一个时间,单位ms
  • Innodb_row_lock_waits:总共的等待次数

本文索引关键字:

间隙锁:http://www.cnblogs.com/huanStephen/p/8076172.html#c_lock

悲观锁:http://www.cnblogs.com/huanStephen/p/8076172.html#p_lock

死锁:http://www.cnblogs.com/huanStephen/p/8076172.html#d_lock

欢迎大家索引!

posted @ 2017-12-20 22:31  huanStephen  阅读(548)  评论(0编辑  收藏  举报