(会话1)
START TRANSACTION;
SELECT * FROM tb_name WHERE id>10 LOCK IN SHARE MODE;
(会话2)
START TRANSACTION;
INSERT INTO tb_name(id,name) VLUES(11,"张三")
结果怎样?会话2会进入执行等待状态,直至会话1的锁释放或者锁超时。
next-key锁(记录所和间隙锁的组合)当InnoDB扫描索引记录时,会先对选中的索引记录加上记录锁(record Lock),再对索引记录两遍的间隙加上间隙锁(gap lock)。
还是以间隙锁的例子说,假如表中没有id=10的这行数据,会话2添加的id该为10,会成功吗?
答案是不会,因为它不止锁了id>10的间隙,连id=10也一起锁了。
表锁在InnoDB中绝大部分都应该使用行锁,因为事务和行锁往往是我们选择InnoDB表的理由,但是在个别特殊事务中,也可以考虑使用表锁。
情况1:事务需要更新大部分或者全部数据,表又比较大,如果使用默认的行锁,不仅这个事务执行效率低,而且可能造成其他事务长时间锁等待和锁冲突,这种情况下可以考虑使用表锁来提高事务的执行速度。
情况2:事务涉及多个表,比较复杂,很可能引起死锁,造成大量事务回滚,这种情况也可以考虑一次性锁定事务涉及的表,从而避免死锁,减少数据库因事务回滚带来的开销。
当然,这两种情况不能太多,否则就应该从业务和程序设计上进行拆分处理,而不是由数据库来承担这个事情。
例子如下:
LOCK TABLES tb_name WRITE; UNLOCK TABLES;
注意:在事务中锁表时,在事务结束前不要释放锁,因为unlock tables会隐含提交事务,所以正确的做法是结束事务后再释放锁。
锁等待和死锁锁等待是指一个事务过程中产生的锁,其他事务需要等待上一个事务释放它的锁,才能占用该资源,如果该事务一直不释放,就需要继续等待下去,直到超过了锁等待时间,会报一个超时错误。
查看锁等待允许时间:
SHOW VARIABLES LIKE "innodb_lock_wait_timeout"
死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,就是所谓的死循环。
典型的实验过程就是两个事务并发,互相修改自己的一条数据,紧接着又修改对方的锁定的那条数据,都要等待对方的锁,死锁就产生了。
出现死锁的问题并不可怕,解决死锁通常有如下办法:
1.不要把无关的操作放到事务里,小事务发生冲突的概率较低。
2.如果不同的程序会并发存取多个表,应尽量约定以相同的顺序来访问表,这样事务就会形成定义良好的查询并且没有死锁。
3.尽量按照索引去查数据,范围查找增加了锁冲突的可能性。
4.对于非常容易产生死锁的业务部分,可以尝试升级锁粒度,通过表锁定来减少死锁产生的概率。
获取表锁争用情况:
SHOW STATUS LIKE "table%"
查了很多资料,确实是这个获取方法,但是我自己没测出来它的用处,试了两台数据库都不行,很奇怪。
查询哪些表正在被锁定:
SHOW OPEN TABLES WHERE In_use > 0;
这个命令监控的是被表锁锁住的表,亲测如果用行锁,这个命令是没有反应的,真的得自己动手实践才能发现真相。
行锁监控获取行锁争用情况:
SHOW STATUS LIKE "innodb_row_lock%"
下面介绍几张表,可以帮助我们监控当前的事务并分析可能存在的锁问题。
select * from information_schema.innodb_trx;
主要字段如下:
trx_id:唯一的事务id号
trx_state:当前事务的状态,lock wait锁等待状态,running执行中状态。
trx_started:事务开始时间
trx_wait_started:事务开始等待时间
trx_mysql_thread_id:线程id
trx_query:事务运行的SQL语句
持有锁的对象:
select * from information_schema.innodb_locks;
锁等待的对象:
select * from information_schema.innodb_lock_waits;
解密 为什么锁一行数据,速度就变得这么慢?实验内容:两个会话两个事务,会话1锁,会话2改,目标是不同的行数据。
会话1的where条件必须是索引,才能锁住这一行,否则就会锁住整张表的数据,让会话2上不了锁。