InnoDB引擎的行锁

InnoDB引擎特色简述

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

行锁特色

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

手动事务语法

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

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

 

中间执行SQLsql

最后执行完了须要进行提交session

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发生阻塞,由于没有索引则会发生阻塞。spa

 手动锁定记录

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

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

在中间进行数据更新code

mysql> COMMIT;

最后进行提交。

最佳实践

一、仍是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

 

 

 

 

 

二、上一个测试两个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; #阻塞释放,执行更新

 

 

 

 

 

三、这里能够看到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;  

 

 

 

 

 

 

四、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;  

 

 

 

 

 

 

 

五、咱们发现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;  

 

 

 

 

 

 

 

 

 

 

六、不会吧,所有阻塞了,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就不阻塞。

好吧,算你厉害,你们仍是记住这个结果吧。

这里这种范围的锁定就是传说中的间隙锁,他有一种特色就是只要在范围以内的数据所有被锁住,无论当前是否存在。

七、若是有两张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

欢迎你们索引!

相关文章
相关标签/搜索