根据锁的粒度分
全局锁
对整个数据库实例加锁,命令是 Flush tables with read lock。使这个库处于只读状态,数据更新语句(DML)、数据定义语句(DDL)以及更新类事务的提交语句都会被阻塞。
全局锁的典型应用场景是做全库逻辑备份,官方自带的逻辑备份工具mysqldump。
mysqldump使用-single-transaction的时候,导数据之前就会启动一个事务,来确保拿到一致性视图。由于MVCC的支持,这个过程数据是可以正常更新。
mysqldump中重要的点(只适用于使用innoDB引擎的库):
- FLUSH /*!40101 LOCAL */ TABLES
- FLUSH TABLES WITH READ LOCK
执行完1,再执行2的目的就是为了避免长事务操作造成FLUSH TABLES WITH READ LOCK操作获取不到锁,同时又阻塞其他客户端的操作。
- SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ
设置当前事务的事务隔离级别为RR,避免不可重复读和幻读。
- START TRANSACTION /*!40100 WITH CONSISTENT SNAPSHOT */
start transaction 和 start transaction with consistent snapshot 区别
1)start transaction 时,是第一条语句的执行时间点,就是事务开始的时间点,第一条select语句建立一致性读的snapshot;
2)start transaction with consistent snapshot 时,则是立即建立本事务的一致性读snapshot,同时开启事务
- UNLOCK TABLES
释放全局锁
为什么不使用set global readonly=true的方式使全库只读?
1.readonly参数用于主从库判断逻辑使用
2.全局锁在客户端发生异常后,会自动释放;而将整个库设置成readonly,会导致整个库不可写
表级锁
- 表锁
语法:lock tables XXX read/write
- 元数据锁(MetaData Lock)
MDL不需要显式使用,在访问表的时候会被自动加上。
MySQL5.5引入MDL,当对一个表做增删改查操作时,加MDL读锁;当要对表做结构变更操作的时候,加MDL写锁。(读锁之间不互斥;读写锁和写锁之间是互斥的)
如何安全地给表加字段?
解决事务占据MDL读锁不释放的问题;alter table里面设定等待时间,避免事务阻塞。
行锁
innoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,innoDB才使用行锁,否则使用表级锁。
在innoDB事务中,行锁是在需要的时候才加上,在事务结束时才释放的。可以提供较高的并发,但存在死锁问题-----两阶段锁协议:如果事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。
一次性锁协议:在事务开始时,一次性申请所有的锁。在事务结束时,一次性释放所有的锁。不存在死锁问题。
出现死锁的解决策略:
- 直接进入等待,直到超时。由innodb_lock_wait_timeout设置
- 主动发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。由innodb_deadlock_detect设置,为on表示开启死锁检测。