MySQL的锁
一. 全局锁
-
Flush tables with read lock (FTWRL):这个命令可以加一个全局锁,让整个数据库处于只读状态,这样所有的DDL、DML和更新事务的操作都会被阻塞。
-
全局锁的典型使用场景,是做全库逻辑备份。
-
让这个库只读的风险:
- 如果在主库上备份,那么在备份期间都不能执行更新,业务基本上就要停摆;
- 如果在从库上备份,那么备份期间从库不能执行主库同步过来的 binlog,会导致主从同步延迟。
-
使用FTWRL全局锁进行数据备份不是一个很好的方式。最好使用MySQL官方提供的mysqldump。mysqldump 使用参数–single-transaction 的时候,导数据之前就会启动一个事务,来确保拿到一致性视图。而由于 MVCC 的支持,这个过程中数据是可以正常更新的。**但是single-transaction 方法只适用于所有的表使用了支持事务的存储引擎的库。**如果有的表使用了不支持事务的引擎,那么备份就只能通过 FTWRL进行数据备份。这往往是 DBA 要求业务开发人员使用 InnoDB 替代MyISAM的原因之一。
二. 表级锁
-
表锁:语法:lock tables … read/write。**需要注意,lock tables 语法除了会限制别的线程的读写外,也限定了本线程接下来的操作对象。**比如线程A对表T加了读锁,那么对表T的修改会报错。
-
MDL(metadata lock)元数据锁:在 MySQL 5.5 版本中引入了 MDL。MDL是表结构级别的锁。当对一个表中的数据做增删改查操作是,加MDL读锁;当对一个表的结构进行修改时,加MDL写锁。这个加锁是自动的,无需人工干预。
- MDL读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查。
- MDL读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性。因此,如果有两个线程要同时给一个表加字段,其中一个要等另一个执行完才能开始执行。
-
MariaDB支持了MDL锁超时的功能,在超时时间后仍获取不到MDL锁就会放弃,不会一直阻塞。
三. 行级锁
- 行级锁是在引擎层实现的。某些引擎支持行级锁(如InnoDB),有些则不支持(MyISAM)。
- **两阶段锁协议:在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。**因此,如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。
- 死锁:当并发系统中不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态,称为死锁。
- 死锁自动检测:设置innodb_deadlock_detect = on。当MySQL检测到死锁是,会主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。