从头开始搞懂 MySQL(05)行锁表锁全局锁

Posted 思想累积

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从头开始搞懂 MySQL(05)行锁表锁全局锁相关的知识,希望对你有一定的参考价值。

1、行锁

行锁是针对数据表中行记录的锁,mysql 的行锁是在引擎层实现的,并不是 所有的引擎都支持行锁,比如 MyISAM 就不支持,InnoDB 支持行锁,避免了并发控制时使用表锁

1.1 两阶段锁

在 InnoDB 事务中,行锁是在需要的时候才被加上的,但并不是不需要了就立刻释放,而是等待事务结束后释放,这个就是两阶段锁协议

如果我们的事务中需要锁多个行,需要把可能造成锁冲突、可能影响并发度的锁尽量往后放,根据两阶段锁协议,不论怎样安排语句顺序,所有操作需要的行锁都是在事务提交的时候释放的,我们要最大程度的减少事务之间的锁等待,提高并发度。

1.2 死锁和锁检测

当系统中不同的线程中出现了循环资源依赖,涉及的线程等都在等待别的线程释放资源,就会导致线程进入无限循环等待的状态,称为死锁

下图举例数据库中的死锁

事务 A 在等待事务 B 释放 id = 2 的行锁,事务 B 在等待事务 A 释放 id = 1 的行锁,互相等待对方的资源释放,进入了死锁状态。

减少死锁的主要方向就是控制访问相同资源的并发事务量

进入死锁状态有两种策略:

  • 进入等待,直到超时,超时时间通过 innodb_lock_wait_timeout 来设置,默认 50s

  • 主动检测死锁,发现死锁后主动回滚某一个事务,让其它事务继续执行,参数 innodb_deadlock_detect 设置为 on 开启,默认为 on

2、表锁

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

表锁一般在数据库引擎不支持行锁的时候才会使用到

2.1 lock

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

比如线程 A 执行 lock tables test1 read, test2 write; 的话,其他线程写 test1、读写 test2 的语句都会被阻塞,同时,线程 A 在 unlock tables 之前,也只能执行读 test1、读写 test2 的操作,也不能访问其它表

2.2 MDL(meta data lock)

MDL 不用显式使用,在访问一个表的时候会自动加上。MDL 作用是保证读写的正确性,对一个表做 CRUD 的时候,加 MDL 读锁,对表结构变更时,加 MDL 写锁

MDL 会直到事务提交才释放,在做表结构变更时,要注意,不要导致锁住线上查询和更新

MDL 读锁之间不互斥,所以可以有多个线程对一张表进行 CRUD,写锁是互斥的,保证变更表结构的安全性

3、全局锁

全局锁就是对整个数据库实例加锁,MySQL 让整个数据库处于只读的命令 flush tables with read lock(FTWRL) 使用该命令后,其他线程的更新语句、数据定义语句和更新类事务提交语句都会被阻塞

3.1 FTWRL

使用 FTWRL 命令后,如果由于客户端异常而断开连接,MySQL 会自动释放这个全局锁,整个库回到正常状态

全局锁的典型使用场景就是做全库逻辑备份,通过 FTWRL 保障不会有其它线程对数据库做更新,数据库处于只读状态,然后对整个库做备份

如果这样的话,在主库上备份,备份期间都不能执行更新,在从库上备份,从库不能执行主库同步的 binlog,导致主从延迟

如果不加锁的话,备份系统备份得到的库不是一个逻辑时间点,视图是逻辑不一致的

3.2 mysqldump

当然,FTWRL 比较适用于不支持事务的引擎,对于支持事务的引擎,我们可以使用官方自带的逻辑备份工具 mysqldump,当 mysqldump 使用参数 -single-transaction 的时候,导数据之前就会启动一个事务,确保拿到一致性视图。由于 MVCC 的支持,整个过程是可以正常更新的

以上是关于从头开始搞懂 MySQL(05)行锁表锁全局锁的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 全局锁表锁以及行锁

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

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

全局锁表锁行锁

MySQL 行锁表锁

MySQL-----锁(行锁表锁页锁乐观锁悲观锁)