系列文章目录
第一章:sql_mode模式
第二章:optimize table、analyze table、alter table、gh-ost
第三章:InnoDB MVCC原理
第四章:sql语句执行过程
第五章:Percona Toolkit工具简介
第六章:MySQL索引
第七章:MySQL的锁
文章目录
一、MySQL锁的分类
根据加锁的范围,MySQL 里面的锁大致可以分成全局锁、表级锁和行锁三类。
二、全局锁
全局锁就是对整个数据库实例加锁。通过命令 Flush tables with read lock (FTWRL),让整个库处于只读状态的时候,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。
2.1 使用场景
全局锁的典型使用场景是,做全库逻辑备份。但是对于都是InnoDB引擎的库,官方自带的逻辑备份工具是 mysqldump。当 mysqldump 使用参数–single-transaction 的时候,导数据之前就会启动一个事务,来确保拿到一致性视图。而由于 MVCC 的支持,这个过程中数据是可以正常更新的。
2.2 set global readonly=true
FTWRL与set global readonly=true的区别:
- 在有些系统中,readonly 的值会被用来做其他逻辑,比如用来判断一个库是主库还是备库。
- 在异常处理机制上有差异。如果执行 FTWRL 命令之后由于客户端发生异常断开,那么 MySQL 会自动释放这个全局锁,整个库回到可以正常更新的状态。而将整个库设置为 readonly 之后,如果客户端发生异常,则数据库就会一直保持 readonly 状态,这样会导致整个库长时间处于不可写状态,风险较高。
三、表级别锁
1.表锁
表锁的语法是 lock tables … read/write。与 FTWRL 类似,可以用 unlock tables 主动释放锁,也可以在客户端断开的时候自动释放。需要注意,lock tables 语法除了会限制别的线程的读写外,也限定了本线程接下来的操作对象。
2.MDL锁
MDL全称为metadata lock,即元数据锁。MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务(显式或隐式)的时候,不可以对元数据进行写入操作。因此从MySQL5.5版本开始引入了MDL锁,来保护表的元数据信息,用于解决或者保证DDL操作与DML操作之间的一致性。
对于引入MDL,其主要解决了2个问题,一个是事务隔离问题,比如在可重复隔离级别下,会话A在2次查询期间,会话B对表结构做了修改,两次查询结果就会不一致,无法满足可重复读的要求;另外一个是数据复制的问题,比如会话A执行了多条更新语句期间,另外一个会话B做了表结构变更并且先提交,就会导致slave在重做时,先重做alter,再重做update时就会出现复制错误的现象。
元数据锁是server层的锁,表级锁,每执行一条DML、DDL语句时都会申请MDL锁,DML操作需要MDL读锁,DDL操作需要MDL写锁(MDL加锁过程是系统自动控制,无法直接干预,读读共享,读写互斥,写写互斥),申请MDL锁的操作会形成一个队列,队列中写锁获取优先级高于读锁。一旦出现写锁等待,不但当前操作会被阻塞,同时还会阻塞后续该表的所有操作。事务一旦申请到MDL锁后,直到事务执行完才会将锁释放。
四、行锁
MySQL的行锁是有各个引擎自己实现的,但是并不是所有的引擎都支持行锁,由于目前主要使用InnoDB,所有下面聊的都是InnoDB的行锁。
4.1 两阶段锁协议
在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。
4.2 死锁
事务 A 和事务 B 在互相等待对方的资源释放,就是进入了死锁状态。MySQL有两种处理策略:
-
直接进入等待,直到超时。这个超时时间可以通过参数 innodb_lock_wait_timeout 来设置。在 InnoDB 中,innodb_lock_wait_timeout 的默认值是 50s,意味着如果采用第一个策略,当出现死锁以后,第一个被锁住的线程要过 50s 才会超时退出,然后其他线程才有可能继续执行。
-
发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数 innodb_deadlock_detect 设置为 on,表示开启这个逻辑。
但是上面的两种方法在实际项目开发中,都不太友好,个人经验: -
如果事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。
-
如果两个不同事务中需要锁多行,保持加锁的顺序顺序一致。
-
将热点行进行拆分,降低每行的并发度。
4.3 行锁
- 记录锁
锁住一行,所以叫记录锁,也是行锁 - 间隙锁
锁住记录之间的间隔,或者索引之前的范围,或者索引之后的范围。只有在重复读级别产生 - 临键锁
记录锁和间隙锁的组合,即锁住记录,又锁住了间隙,范围是前开后闭。