vlambda博客
学习文章列表

MySQL系列3 - 全局锁、表锁、行锁

全局锁

对整个库进行加锁的操作。此时,整个数据库只有读权限,一般只在对整库进行备份的时候使用全局锁。命令是:

Flush tables with read lock;

在客户端断开会自动释放,不用担心客户端连接断开了,锁一直都没法释放。


如果数据库是只读状态,这个时候所有的insert、update操作都是停摆状态,必定会影响业务逻辑。(在主库上加锁无法insert、update,在从库上加锁影响binlog的同步)


全局锁虽然影响大,但是很有必要的。假设一个购买过程如下 :

  1. 扣除该用户的余额

  2. 扣除商品的库存

  3. 生成一个订单记录


这三步分别是对三个表的操作,如果在数据备份的时候不使用全局锁,而使用表锁,那么备份库的数据很可能出现不一致。


表锁

有两种类型的表锁,一种是通过命令显示调用 (同样在客户端断开会自动释放)

// table_a 加上读锁// table_b 加上写锁lock tables table_a read, table_b write;


另一种是 MDL(metadata lock)。不需要显示使用,为了保证读写正确性自动加上的锁。


例如一个线程正在查询某张表的数据时,另一个线程正在该表上增加/删除一个字段,或者增加索引,那么此时查询线程拿到的结果表结构是对应不上的。


MDL有读锁和写锁

1. 读锁之间是不互斥的,a线程读和b线程读之间不影响。

2. 读写锁之间是互斥的,参考上面的例子。

3. 写锁之间也是互斥的,假设两个线程都在变更表结构。


所以,我们在对线上的表结构做变更、增加索引等操作时,是要获取该表的MDL写锁的,避免影响线上业务,建议在业务相对不繁忙时操作。


行锁

不同于全局锁和表锁,行锁是引擎层实现的。MyISAM存储引擎不支持行锁,innoDB存储引擎支持行锁。(这也是innoDB可以取代MyISAM的原因之一)


我们来看下下面这个场景:

在时刻2 session2会怎样呢?


在知道有行锁的存在之后,很容易得出结论在时刻2 session2会被阻塞。那么问题又来了,session2的update语句在什么时候才会执行呢?


答案是在时刻3 session1 commit之后才会执行,并不是在时刻2 session1执行完update语句后。


在 InnoDB 事务中,行锁是在需要的时候才加上的,在事务结束时才释放。这就是两阶段锁协议。


我们要特别注意:InnoDB 事务中,行锁在事务结束时才释放而不是语句执行完就释放。那么我们再回过头看下之前的一个购买流程的例子:


  1. 扣除该用户的余额

  2. 扣除商品的库存

  3. 生成一个订单记录


那么这三条语句在一个事务中的执行顺序你会怎么安排呢?


答案应该是:3,1,2。

如果多个用户购买同一个商品,就会在第2步在扣除商品库存时,对这个商品这一行数据产生行锁,而行锁在事务提交后才释放。这样可以最大的缩短行锁存在的时间,从而提高并发量。当然,步骤1该用户账号同时购买商品,也可能存在行锁,这种概率比2小得多。


往期回顾: