两万字详解InnoDB的锁( 八 )


文章插图
 
为什么事务B插入语句会阻塞,事务C的更新语句不会呢?

  • 这是因为事务会话A在遍历的时候,先访问第一个c=10的记录 。它根据原则 1,加一个(c=5,id=5) 到 (c=10,id=10)的next-key lock 。
  • 然后,事务会话A向右查找,直到碰到 (c=15,id=15) 这一行,循环才结束 。根据优化 2,这是一个等值查询,向右查找到了不满足条件的行,所以会退化成(c=10,id=10) 到 (c=15,id=15)的间隙Gap锁 。即事务会话A这个select...for update语句在索引 c 上的加锁范围,就是下图灰色阴影部分的:

两万字详解InnoDB的锁

文章插图
 
因为c=13是这个区间内的,所以事务B插入insert into t5 values(13,13,13);会阻塞 。因为根据优化2,已经退化成 (c=10,id=10) 到 (c=15,id=15) 的间隙Gap锁,即不包括c=15,所以事务C,Update t5 set d=d+1 where c=15不会阻塞
4.7 案例七: limit 语句减少加锁范围如果一个SQL有limit,会不会对加锁有什么影响呢?我们用4.6的例子,然后给查询语句加个limit:
Select * from t5 where c=10 limit 2 for update;事务A、B执行如下:
两万字详解InnoDB的锁

文章插图
 
发现事务B并没有阻塞,而是可以顺利执行
两万字详解InnoDB的锁

文章插图
 
这是为什么呢?跟上个例子,怎么事务B却不会阻塞了,事务会话A的select只是加多了一个limit 2 。
这是因为明确加了limit 2的限制后,因此在遍历到 (c=10, id=30) 这一行之后,满足条件的语句已经有两条,循环就结束了 。因此,索引 c上的加锁范围就变成了从(c=5,id=5) 到(c=10,id=30) 这个前开后闭区间,如下图所示:
两万字详解InnoDB的锁

文章插图
 
索引平时我们写SQL的时候,比如查询select或者delete语句时,尽量加一下limit哈 。
5. 如何查看事务加锁情况我门怎么查看SQL加了什么锁呢?或者换个说法,如何查看事务的加锁情况呢?有这两种方法:
  • 使用infomation_schema数据库中的表获取锁信息
  • 使用show engine innodb status
5.1 使用infomation_schema数据库中的表获取锁信息infomation_schema数据库中,有几个表跟锁紧密关联的 。
  • INNODB_TRX:该表存储了InnoDB当前正在执行的事务信息,包括事务id、事务状态(比如事务是在运行还是在等待获取某个所)等
  • INNODB_LOCKS:该表记录了一些锁信息,包括两个方面:1.如果一个事务想要获取某个锁,但未获取到,则记录该锁信息 。2. 如果一个事务获取到了某个锁,但是这个锁阻塞了别的事务,则记录该锁信息 。
  • INNODB_LOCK_WAITS:表明每个阻塞的事务是因为获取不到哪个事务持有的锁而阻塞 。
5.1.1 INNODB_TRX我们在一个会话中执行加锁的语句,在两个会话窗口,即可查看INNODB_TRX的信息啦,如下:
两万字详解InnoDB的锁

文章插图
 
表中可以看到一个事务id为1644837正在运行汇中,它的隔离级别为REPEATABLE READ 。我们一般关注这几个参数:
  • trx_tables_locked:该事务当前加了多少个表级锁 。
  • trx_rows_locked:表示当前加了多少个行级锁 。
  • trx_lock_structs:表示该事务生成了多少个内存中的锁结构 。
5.1.2 INNODB_LOCKS一般系统中,发生某个事务因为获取不到锁而被阻塞时,该表才会有记录 。
事务A、B执行如下:
两万字详解InnoDB的锁

文章插图
 
使用select * from
information_schema.INNODB_LOCKS;查看
两万字详解InnoDB的锁

文章插图
 
可以看到两个事务Id 1644842和1644843都持有什么锁,就是看那个lock_mode和lock_type哈 。但是并看不出是哪个锁在等待那个锁导致的阻塞,这时候就可以看INNODB_LOCK_WAITS表啦 。
5.1.3 INNODB_LOCK_WAITSINNODB_LOCK_WAITS 表明每个事务是因为获取不到哪个事务持有的锁而阻塞 。
两万字详解InnoDB的锁

文章插图