根据加锁的范围,MySQL里面的锁大致可以分成全局锁、表级锁和行锁三类。

    • 全局锁

    顾名思义,全局锁就是对整个数据库实例加锁。MySQL提供了一个加全局读锁的方法,命令是 Flush tables with read lock (FTWRL)。当你需要让整个库处于只读状态的时候,可以使用这个命 令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括 建表、修改表结构等)和更新类事务的提交语句。

    全局锁的典型使用场景是,做全库逻辑备份。也就是把整库每个表都select出来存成文本。

    但是让整库都只读,听上去就很危险:

    • 如果你在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆;
    • 如果你在从库上备份,那么备份期间从库不能执行主库同步过来的binlog,会导致主从延迟。

    image.png
    可以看到,这个备份结果里,用户A的数据状态是“账户余额没扣,但是用户课程表里面已经多了 一门课”。如果后面用这个备份来恢复数据的话,用户A就发现,自己赚了。

    • 表级锁

    MySQL里面表级别的锁有两种:一种是表锁,一种是元数据锁(meta data lock,MDL)。

    表锁的语法是 lock tables … read/write。与FTWRL类似,可以用unlock tables主动释放锁, 也可以在客户端断开的时候自动释放。需要注意,lock tables语法除了会限制别的线程的读写 外,也限定了本线程接下来的操作对象。

    举个例子, 如果在某个线程A中执行lock tables t1 read, t2 write; 这个语句,则其他线程写t1、读 写t2的语句都会被阻塞。同时,线程A在执行unlock tables之前,也只能执行读t1、读写t2的操 作。连写t1都不允许,自然也不能访问其他表。

    另一类表级的锁是MDL(metadata lock)。MDL不需要显式使用,在访问一个表的时候会被 自动加上。MDL的作用是,保证读写的正确性。你可以想象一下,如果一个查询正在遍历一个 表中的数据,而执行期间另一个线程对这个表结构做变更,删了一列,那么查询线程拿到的结果 跟表结构对不上,肯定是不行的。

    因此,在MySQL 5.5版本中引入了MDL,当对一个表做增删改查操作的时候,加MDL读锁;当 要对表做结构变更操作的时候,加MDL写锁。

    • 读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查。
    • 读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性。因此,如果有两个线 程要同时给一个表加字段,其中一个要等另一个执行完才能开始执行。