全局锁
概念
全局锁是对整个数据库实例进行加锁,如全局读锁(Flush table with read lock(GTWRL))。
加了全局读锁之后,整个数据库就处于只读状态,这个时候其他线程的数据更新语句(包括数据的增删改),数据定义语句(包括建表,修改表结构)和更新类的事务提交语句都会被阻塞
使用场景
全库的逻辑备份,把整个库的所有表都 select 出来存成文本。
优缺点
- 优点:
- 可以保证备份的数据的一致性
- 在不支持事务的存储引擎中也可以使用。如 MVCC 就只使用存储引擎支持事务的情况下才可以使用
- 缺点:
- 备份过程中整个库都是只读状态。业务基本是停摆状态。在做主从备份的时候,如果在主库备份,那么备份期间都不能执行更新,业务基本停摆。在从库备份的时候,那么备份期间不能执行主库同步过来的 binlog,会导致主从延迟
与全库只读 set global readonly = true 方式的比较
- readonly 方式也可以让全库进入只读状态,但是 readonly 的值也会被用来做其他逻辑,比如用来判断一个库是主库还是备库。因此 修改 global 变量的方式影响面更大
- 在异常处理机制上的差异。如果执行 FLTWL 命令之后由于客户端发生异常断开,那么 MySQL 会自动释放这个全局锁,整个库回到可以正常更新的状态。但是将整个库设置为
readonly 状态之后,即使客户端发生异常,数据库也并不会释放锁,他会一直保持 readonly 状态,这样会导致整个库长时间处于只读状态,风险较高
表级锁
概念
MySQL 里面的表级锁有两种,一种是表锁,一种是元数据锁(meta data lock(MDL))
语法
- 表锁
表锁的语法是 lock tables … read/write。与 FTWRL 类似,可以用 unlock tables 主动释放锁,也可以在客户端断开的时候自动释放。需要注意的是,lock tables 不仅仅限制了别的线程的读写,同样的也限制了当前线程 - MDL(medadata lock)
MDL 不需要显示使用,在访问一个表的时候会被自动加上。MDL 的作用的是,保证读写的正确性。MySQL 5.5 之后引入了 MDL ,当对一个表做增删改查操作的时候,加 MDL 读锁,当要对表做结构变更操作的时候,加 MDL 写锁
行级锁
MySQL 的行锁是在引擎层的,由各个引擎自己实现。但并不是所有的引擎都支持行锁,MyISAM 引擎就不支持行锁。
概念
行锁是针对数据表中行记录的锁。比如事务 A 更新了一行,事务 B 也要更新同一行,那么事务 B 必须要等事务 A 操作完成之后才能更新
两阶段锁
-
概念
在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束的时候才释放,这就是两阶段锁协议 -
可以解决的问题
如果事务需要锁住多行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放,最大程度减少事务之间的锁等待,提升了并发度
死锁与死锁检测
概念
当并发系统中不同线程之间出现循环依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态,称为死锁。
解决死锁的策略
-
直接进入等待,直到超时。超时时间可以通过参数 innodb_lock_wait_timeout 参数来设置
-
发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。
将 innodb_deadlock_detect 设置为 on ,表示开启这个逻辑两种策略比较
- 在 InnoDB 中,innodb_lock_wait_timeout 的默认值是 50s,等待时间过长,如果把这个时间设置成一个很小的值,又容易误伤。
- innodb_deadlock_detect 默认值就是 on,主动死锁检测在发生死锁的时候,能够快速发现并进行处理,但也有额外的负担。如:每当一个事务被锁的时候,就要看它所依赖的线程有没有被锁住,如此循环进行判断。这样做的时间复杂度是 O(n),这样会消耗大量的 CPU 资源
参考资料: 极客时间 ,林晓斌 的《MySQL实战45讲》 课程