根据加锁的范围,MySQL⾥⾯的锁⼤致可以分成全局锁、表级锁和⾏锁三类。
全局锁
全局锁即对整个数据库进行加锁,使整个数据库处于只读状态。
全局锁的典型使⽤场景是,做全库逻辑备份,也就是把整库每个表都select出来存成⽂本。
表级锁
MySQL⾥⾯表级别的锁有两种:⼀种是表锁,⼀种是元数据锁(meta data lock,MDL)。
- 表锁:它的语法是 lock tables … read/write。不常用,因为锁住的面积较大。
- 元数据锁(MDL):MDL不需要显式使⽤,在访问⼀个表的时候会被⾃动加上。.当对⼀个表做增删改查操作的时候,加MDL读锁;当要对表做结构变更操作的时候,加MDL写锁。读锁之间不排斥,读写锁之间、写锁之间是互斥的。
行锁
MySql中并不是所有引擎都支持行锁,如MyISAM引擎就不⽀持行锁,但InnoDB是⽀持⾏锁的。
⾏锁就是针对数据表中行记录的锁。比如事务A更新了一行,而这时候事务B也要更新同⼀行,则必须等事务A的操作完成后才能进⾏更新。
在InnoDB事务中,⾏锁是在需要的时候才加上的,但并不是不需要了就⽴刻释放,⽽是要等到事务提交后才释放。这个就是两阶段锁协议。
sql优化小技巧:如果事务中需要锁多个⾏,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放
死锁
表级锁是不会引发死锁的,但是行锁可能引发死锁。
由于两个事务都需要对多行进行操作,与此同时会对多行进行上锁,待事务提交之后才会释放行锁。因此在上图中都无法获取到自己所需要的行锁,两事务都无法正常提交事务,故而陷入死锁。
防止死锁的方式
- 通过参数innodb_lock_wait_timeout来设置死锁超时时间。但设置较大的话,服务性能仍然表现不佳;若设置较小的话,可能误杀正常的服务
- 主动死锁检测:会消耗大量的CPU资源,每一个新加入线程都要检测自己加入是否会导致死锁,时间复杂度为O(n2)