在我们开发的过程中 , 使用全局锁和表锁的场景比较少 , 接触的也相对少一点 , 下面主要介绍一下 。
全局锁FTWRL
全局锁就是对整个数据库实例加锁 , MySQL 提供了 flush tables with read lock (FTWRL) 的方式去加全局锁 。当你需要让整个库处于只读状态的时候 , 就可以使用这个命令了 , 之后所有线程的更改操作都会被阻塞 。
mysqldump
mysqldump 是官方提供的备份工具 , 可以通过 --single-transaction 参数来启用可重复读隔离级别 , 从而可以拿到一个一致性视图 。
set global readonly = true
通过上述命令可以让全库进入只读状态 , 但是在开发当中 , 事务框架往往会利用这个参数来处理读写分离 。所以通常情况下 , 还是不建议使用这种方式 。
表级锁MySQL 的表级锁有 2 种:表锁 和 元数据锁 。
表锁
表锁可以使用 lock tables T read/write , 可以使用 unlock tables 主动释放锁 , 也可以在客户端断开的时候自动释放锁 。
MDL (metadata lock)
MDL 没有显示的命令 , 当执行改表语句时 , MDL 会保证读写的正确性 。MySQL 在 5.5 版本以后引入了 MDL 锁 , 当对一个表做 增删改查 的时候 , 加 MDL 读锁;当要多表结构做变更的时候 , 加 MDL 写锁 。
- MDL 读锁之间不互斥 , 因此可以有多个线程同时对一张表 增删改查 。
- MDL 读-写、写-写之间是互斥的 , 因此如果同时有 2 个线程给表加字段 , 则需要顺序执行 。
文章插图
从上面流程可以看出 , 当我们在更改线上表时 , 此时可能早上服务不可用 。此时可以增加一个改表的超时时间 , 但是MySQL官方还没有支持这种功能 , MarialDB、AliSQL 的开源分支是可以支持的 。
行锁两阶段锁
当使用update 更新数据时 , 会对 where 条件扫描到的行加行锁 。下面看一下更新语句的执行流程:
文章插图
从上图可以看出 , 事务 A 对 id=1 和 id=2 的行加锁之后 , 事务 B 要对 id=1 加锁的时候就会阻塞 。在 InnoDB 事务中 , 行锁是在需要的时候才加上的 , 并且在事务提交后释放的 , 这就是两阶段锁协议 。所以我们在更新数据时 , 应尽量把容易产生并发更新的行放在事务末端执行 。
死锁
在事务对不同行加锁的时候 , 就很有可能出现死锁的情况 。如下所示:
文章插图
上面事务A 和 事务B 都在等待对方释放资源 , 从而就产生了死锁的状态 。MySQL 有 2 中策略去解决死锁:
- 超时等待 , 可以通过 innodb_lock_wait_timeout 来设置 , 默认 50S 。(一般不采用 , 因为 50S 对应用来说是不可接受的 , 并且这个值的设置也没有合适的估算值)
- 死锁检测 , 就是发现死锁后 , 主动回滚其中一个事务 。可以通过 innodb_deadlock_detect 设置为 on 。
- 按规则加锁 。如 A 转账给 B , 同时 B 也转账给 A , 此时就很可能出现死锁 。但是如果我们根据 userId 的升序规则去加锁 , 就不会产生死锁的问题了 。
- 控制并发度 。如支付系统中的账户系统 , 可以将总账户拆分成子账户 , 然后每个子账户是一个独立的锁实体 。
【MySQL全局锁表锁&行锁】
推荐阅读
- 详解Oracle与Mysql在主键、索引、分页、组函数和单引号的区别
- 记住:永远不要在 MySQL 中使用 UTF-8
- MySQL事务隔离实现
- mysql参数设置--max_allowed_packet 值如何调整?
- MySQL - mysqldump 命令参数
- 在既有系统中打通Apache Ignite、MySQL和Node.js
- CRUD MySQL数据库必会的增删查改操作
- mysql数据库建表的完整步骤 sqlserver导入excel表格命令
- 更好的MySQL客户端推荐 mysql客户端哪个好用
- 建立数据库的步骤 mysql数据库系统需求分析