MySQL 06 全局锁和表锁:给表加个字段怎么有这么多阻碍?

根据加锁的范围,MySQL里面的锁大致可以分成全局锁、表级锁和行锁三类,本文先讨论前两种。

全局锁

全局锁是对整个数据库实例加锁,MySQL提供的加全局读锁的命令是Flush tables with read lock(下面简称FTWRL)。当需要让整个库处于只读状态时,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据的增删改、数据定义语句(表的建立与修改等)、更新类事务的提交语句。

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

让整个库都处于只读,有些弊端:

  • 若在主库上备份,那么在备份期间都不能执行更新,业务会停滞;

  • 若在从库上备份,那么备份期间从库不能执行主库同步过来的binlog,导致主从延迟。

那么既然备份加锁有这么多弊端,是否可以不加锁呢?

答案是不行的,考虑有用户余额表和订单表两张表,当用户发起一次购买,需要在用户余额表扣减余额,在订单表里增加一笔订单。若顺序是先备份用户余额表,然后用户购买,再备份订单表,不难发现会出现问题,顺序反之亦然。

官方自带的逻辑备份工具是mysqldump,当mysqldump使用参数single-transaction,在导数据之前就会启动一个事务,拿到一致性的视图。由于MVCC的支持,这个备份过程中数据可以正常更新。

那既然官方有这个工具,为什么需要FTWRL呢?答案是有些引擎并不支持可重复读的隔离级别,这时候必须使用FTWRL命令。因此,只有所有表都使用事务引擎的库 才能使用single-transaction

还有一种想法是,为了让全库只读,使用set global readonly=true的方式。但还是建议使用FTWRL,原因如下:

  • 在有些系统中,readonly的值会被用作其他逻辑,比如判断库是主库还是从库。

  • 在异常处理机制上,如果执行FTWRL命令之后由于客户端发生异常断开,那么MySQL会自动释放这个全局锁,整个库回到可以正常更新的状态。而将整个库设置为readonly之后,如果客户端发生异常,则数据库会一直保持该状态,导致整个库长时间处于不可写状态,风险较高。

表级锁

这里介绍两种表级别的锁,一种是表锁,一种是元数据锁。

表锁的语法是lock tables ... read/write,可以用unlock tables主动释放锁,也可以在客户端断开时自动释放锁。表锁除了限制其他线程的读写,也限制本线程接下来的操作

在没有出现更细粒度的锁的时候,表锁是最常用的处理并发的方式。对于InnoDB这种支持行锁的引擎,一般不使用lock tables来控制并发。

元数据锁(MDL)不需要显式使用,在访问一个表的时候会被自动加上。其作用是保证读写的正确性。MDL在MySQL 5.5版本引入,当对一个表做增删改查时,会加MDL读锁;当要对表做结构变更操作时,会加MDL写锁。

  • 读锁不互斥,因此可以有多个线程同时对一张表增删改查。

  • 读写锁、写锁之间互斥,用来保证变更表结构操作的正确性。

要注意的是,事务中的MDL锁,在语句执行开始时申请,但语句结束后并不会马上释放 ,而会等到整个事务提交后释放。考虑下面这样一个例子:

首先事务启动,然后session A会对表t加一个MDL读锁,由于session B需要的也是读锁,第二条查询语句也能正常执行。但之后session C会被阻塞,因为前面的读锁还没释放,而session C需要写锁。当session C阻塞,之后只需要读锁的请求也都会被阻塞,等同于这张表此时完全不可读写。

如果某张表上查询语句频繁,而客户端有重试机制,即超时后再起session请求,那么在上述情况下库的线程很快就会爆满。

基于以上分析,考虑一个场景,假设要对一张小表加字段,这张表数据量不大,但是请求很频繁,该怎么做?

此时因为请求频繁,不能简单kill事务。比较好的方法是,在alter table语句里设置等待时间,如果在等待时间里能拿到MDL写锁就进行修改,拿不到也不阻塞后面的业务语句,先放弃修改,之后开发人员人工介入。

目前已有相应的语句:

sql 复制代码
ALTER TABLE tbl_name NOWAIT add column ...
ALTER TABLE tbl_name WAIT N add column ... 

参考资料:极客时间专栏《MySQL实战45讲》https://time.geekbang.org/column/intro/100020801?tab=catalog

相关推荐
KellenKellenHao6 小时前
MySQL数据库主从复制
数据库·mysql
一只fish7 小时前
MySQL 8.0 OCP 1Z0-908 题目解析(16)
数据库·mysql
叁沐8 小时前
MySQL 07 行锁功过:怎么减少行锁对性能的影响?
mysql
Java烘焙师9 小时前
架构师必备:业务扩展模式选型
mysql·elasticsearch·架构·hbase·多维度查询
飞翔的佩奇9 小时前
Java项目:基于SSM框架实现的忘忧小区物业管理系统【ssm+B/S架构+源码+数据库+毕业论文+开题报告】
java·数据库·mysql·vue·毕业设计·ssm框架·小区物业管理系统
@Ryan Ding9 小时前
MySQL主从复制与读写分离概述
android·mysql·adb
feifeigo12316 小时前
升级到MySQL 8.4,MySQL启动报错:io_setup() failed with EAGAIN
数据库·mysql·adb
A__tao21 小时前
一键将 SQL 转为 Java 实体类,全面支持 MySQL / PostgreSQL / Oracle!
java·sql·mysql
一只fish21 小时前
MySQL 8.0 OCP 1Z0-908 题目解析(17)
数据库·mysql