MySQL中的锁,分为全局锁、表级锁、行锁
全局锁
全局锁的意思就是,对整个数据库实例加锁,它的命令是FTWRL
Flash tables with read lock
这个命令的语义是,使整个库处于一种只读的状态,使用这个命令后,以下语句会被阻塞:数据的更新、数据结构的定义、更新类事务的提交。
全局锁通常被用于全库逻辑备份,但是让整个库只读,会有两个问题:
- 主库上的备份,会使整个业务停摆。
- 从库上的备份,会造成备份期间,从库无法执行主库同步过来的bin log而造成主从延迟。
官方自带的逻辑是mysqldump,导数据之前,会开启一个事务,目的是拿到一致性视图,由于MVCC(数据多版本并发控制),备份的过程中,数据是可以正常更新的。但是由于只有InnoDB支持事务,所以只有InnoDB可以使用mysqldump来对数据库进行逻辑备份。这也是为什么现在都建议使用InnoDB的原因之一。
表级锁
MySQL里面的表级锁有两种,一种是表锁,一种是MDL元数据锁。(Meta data lock)
表锁的语法是:
lock tables ...read/write
整张表上锁,不仅限制其他线程的操作,同时也限制本线程的操作。
MDL是一种隐式锁。他的所用是保证读写操作的正确性,MDL是MySQL5.5版本之后引入的,当对一个表进行增删改查的时候,会加MDL读锁。对表结构进行修改的时候,会加MDL写锁。读写锁之间、写锁之间是互斥的。(所以写锁也叫排它锁,读锁也叫共享锁)。
锁只有在事务提交的时候,才会释放。所以长事务会一致占用锁。所以修改表结构的时候,要注意,不要阻塞线上正在执行的增删改查操作。
行锁
行锁是存储引擎层实现的,所以这也是为什么要用InnoDB引擎来代替MyISAM引擎的原因之一。
有两个事务都在更改同一条热点数据。这个时候,会出现阻塞现象。
在InnoDB引擎中,行锁是需要的时候加上的,但是释放锁的时候,却是事务结束的时候才释放。所以如果一个事务要锁多个行,要把最可能造成锁冲突的语句放到后面,这样可以减少阻塞的时间。
死锁和死锁检测
并发系统中,不同线程出现循环依赖资源,涉及到的线程都在等待其他线程释放资源,导致无限等待的状态,称为死锁。
有两种思路可以解决死锁问题:
- 设置超时等待,innodb_lock_wait_timeout,默认是50s,显然这个时间太长了,但是这个值的大小很难确定,会误伤正常的锁等待。
- 发起死锁检测,发生死锁后,主动回滚死锁链条中的某一事务,让其他事务可以正常执行。参数是innodb_deadlock_detect
但是死锁检测是需要额外负担的,我们可以做并发控制,减少共享资源的争抢。