06 | 全局锁和表锁 :给表加个字段怎么有这么多阻碍?
根据加锁的范围,MySQL的锁大致可以分为全局锁、表级锁和行锁三类。
全局锁
全局锁就是对整个数据库实例加锁,整个库都会处于只读的状态。
全局锁的适用场景是做全库逻辑备份,加锁后整个库都完全处于只读状态。但是这样听起来就比较危险:
如果在主库上备份,备份期间不能进行更新,业务基本处于停止状态
如果在从库上备份,备份期间从库不能执行主库同步过来的binlog,会导致主从延迟。
但是换一个思考方式,备份为什么要加锁,不加锁会有什么问题呢?
不加锁的话,备份系统备份得到的库不是一个逻辑时间点,这个视图是逻辑不一致的。
还有一种设置全库为只读状态的方式是set global readonly=true
,但是还是更推荐使用FTWRL
方式,原因如下:
有些系统里,
readonly
的值会用来做其他逻辑,比如判断一个库是主库还是备库,修改global
变量的方式的影响大在异常处理机制上有差异,如果是
FTWRL
方式,客户端发生异常断开后,MySQL会自动释放这个全局锁,整个库就可以恢复正常;如果是redonly
状态,客户端异常断开后,数据库会一直保持,导致整个库长时间处于不可写状态。
表级锁
表级别的锁有表锁和元数据锁两种。
表锁的语法是lock tables ...read/write
,与FTWRL
类似,可以使用unlock tables
主动释放锁,也可以在客户端断开的时候自动释放。需要注意的一点是这个除了会限制别的线程的读写外,也限定了本线程接下来的操作对象。
比如某个线程A中执行lock tables t1 read,t2 write
这个语句,那么其他线程写t1
、读写t2
的语句都会被阻塞。而且线程A也只能执行读t1
、读写t2
的操作,连写t1
都不允许,自然也不能访问其他表。
在没有出现更细粒度的锁的时候,表锁是最常用的处理并发的方法。
另一类表级的锁是MDL,也就是元数据锁。MDL不需要显式使用,在访问一个表的时候会自动加上,作用是保证读写的正确性。