InnoDB引擎的行锁

InnoDB引擎特点简述

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

行锁特点

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

手动事务语法

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

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

 

中间执行SQL

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

mysql> COMMIT;

最佳实践

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

 

 

 

 

 

 

 

 

这里有几个问题:

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

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

表名:innodb_lock

 

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

 

 

 

 

 

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

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

 

 

 

 

 

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

 手动锁定记录

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

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

在中间进行数据更新

mysql> COMMIT;

最后进行提交。

最佳实践

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

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

 

 

 

 

 

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

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

 

 

 

 

 

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

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

 

 

 

 

 

 

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

步骤Session1Session2
1BEGIN; 
2SELECT * FROM innodb_lock WHERE id = 1 FOR UPDATE; 
3UPDATE 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;  #未阻塞
6COMMIT; 

 

 

 

 

 

 

 

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

步骤Session1Session2
1INSERT INTO innodb_lock VALUES(4, 'red'),(5, 'orange'); 
1BEGIN; 
2SELECT * FROM innodb_lock WHERE id < 3 FOR UPDATE; 
3UPDATE 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;  #阻塞
8COMMIT; 

 

 

 

 

 

 

 

 

 

 

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

步骤Session1Session2
1INSERT INTO innodb_lock VALUES(6, 'purple'),(7, 'white'); 
2BEGIN; 
3SELECT * FROM innodb_lock WHERE id < 3 FOR UPDATE; 
4UPDATE 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');  #阻塞
10COMMIT; 

 

 

 

 

 

 

 

 

 

 

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

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

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

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

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

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

 

 

 

 

 

在第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

欢迎大家索引!

转载于:https://www.cnblogs.com/huanStephen/p/8076172.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值