Skip to content

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

@git-zjx

Description

@git-zjx

全局锁

对整个数据库实例加锁,MySQL 提供一个加全局读锁的命令:Flush tables with read lock(FTWRL),命令执行后,数据更新语句(增删改)、数据定义语句(建表和修改表结构等)和更新类事务都会被阻塞,经常用于数据库备份场景

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

参数 -single-transaction 需要数据库引擎支持事务,如果不支持就只能使用 FTWRL 方案

为什么不使用 set global readonly = true 方式?

  1. 有些系统中 readonly 会被用作其他逻辑,例如判断是主库还是从库
  2. 异常处理机制有差异,FTWRL 后客户端异常断开,MySQL 会自动释放全局锁,库恢复正常;设置readonly,客户端异常,则会保持 readonly,会导致长时间处于不可写状态,风险较高
  3. readonly 对 super 权限无效

表级锁

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

lock tables … read/write 语句用于加表锁,使用 unlock tables 可以释放锁,客户端断开连接时也会释放锁
需要注意,lock tables 语法除了会限制别的线程的读写外,也限定了本线程接下来的操作对象
举个例子, 如果在某个线程 A 中执行 lock tables t1 read, t2 write; 这个语句,则其他线程写 t1、读写 t2 的语句都会被阻塞。同时,线程 A 在执行 unlock tables 之前,也只能执行读 t1、读写 t2 的操作。连写 t1 都不允许,自然也不能访问其他表。

在 MySQL 5.5 版本中引入了 MDL,MDL 不需要显式调用,在访问一个表时自动调用,主要保证读写的正确性,防止DDL和DML并发的冲突

当表做增删改查操作时,加MDL读锁;当对表结构变更的时候,加MDL写锁

  • 读锁之间不互斥,多线程可对同一张表增删改查
  • 读写锁之间、写锁之间互斥。两个线程同时给一个表增加字段,则第二个需要等待第一个执行完才能继续

小表加字段为什么会导致整个库崩溃?
image
Session A 会给表 t 加 MDL 读锁,Session B 需要的也是读锁,可以执行
Session C 需要 MDL 写锁,但 Session A 的 MDL 读锁还未释放,需要等待
Session D 需要 MDL 读锁,但是因为 MySQL 内部维护了一个队列决定执行顺序,Session C 阻塞了 Session D
这里可以知道事务中的 MDL 锁会等到整个事务结束后再释放

如何安全的给小表加字段?

  • 解决长事务
  • 在 alter table 语句中设置等待时间

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions