全局锁

场景:

做全库逻辑备份。

使用:

  1. Flush tables with read lock ,让数据库处于只读的状态,其他的线程的更新、建库改表的语句被阻塞(对于不支持事务的存储引擎:MyISAM等)
  2. 使用mysqldump逻辑备份工具设置参数-single-transaction参数,会开启一个事务,来确保拿到一致性视图。由于MVCC的支持,这个过程数据可以正常更新。(支持事务的存储引擎:InnoDB等)

补充:

  • DML:增删改数据
  • DDL:加字段等修改表结构的操作

表级锁

表锁:

  • 共享锁(锁):不会限制线程读,会限制线程写(所说的线程包含:自身线程以及其他线程)。
  • 独占锁(锁):不会限制本线程的读写,会限制其他线程读写。

  • 加锁:lock tables 表名 read、write
  • 释放:unlock tables 主动释放锁、或者客户端断开的时候自动释放。

意向锁

作用:为了快速判断表里是否有记录被加锁
  • 意向共享锁:在InnoDB引擎的表里对某些记录加上共享锁之前,需要先在表级别加上一个意向共享锁;
  • 意向独占锁:在InnoDB引擎的表里对某些记录加上独占锁之前,需要先在表级别加上一个意向独占锁;

总结:

  • 意向共享锁和意向独占锁是表级锁,不会和行级的共享锁和独占锁发生冲突,而且意向锁之间也不会发生冲突,只会和共享表锁和独占锁发生冲突
  • 有了意向锁,由于在对记录加独占锁前,先会加上表级别的意向独占锁,那么在加独占锁时,直接查该表是否有意向独占锁,如果有就意味着表里已经有记录被加了独占锁,这样就不用去遍历表里的记录。

元数据锁、MDL锁(metadata lock)

不需要显式的使用,在访问一个表的时候会自动加上。

作用:会保证读写的安全。

  • 当一个表做增删改查操作的时候,会加上MDL读锁。读锁之间不互斥,多个线程可以同时对一张表进行增删改查。
  • 当一个表做结构变更的时候,会加上MDL写锁。读写互斥,写写互斥,需要一个等待另一个完成后才执行。
补充:
  • 事务中的MDL锁,在语句执行开始时申请,但是语句结束后不会马上释放,会等整个事务提交后再释放。
  • 也就是说,不应该出现DDL与DML同时在一个事务里,这样会导致DDL之后的语句阻塞,数据库的线程很快会被爆满。因为DDL会加上MDL写锁,执行之后不会释放。而DDL之后操作会尝试获取DML读锁,读写互斥。所以会一直等待。

如果非要在表上增加字段,做DDL操作如何处理?

可以在语句设置等待时间,如果在这个指定的等待时间里能拿到MDL写锁最好,拿不到也不要阻塞后面的业务语句,先放弃。之后开发人员在通过重试命令重复这个过程。

行锁

行锁是两阶段锁,所以介绍一下两阶段锁。

两阶段锁协议:

InnoDB事务中,行锁是在需要的时候加上的,但是并不是不需要了就立即释放,而是等待事务结束后才释放。

所以,如果事务中需要锁多行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。这样可以减少事务之间锁的等待,提高了并发度。

个人总结:相当于读写锁(ReadWriteLock)、主要包括记录锁(对索引下面的数据上锁)、间隙锁(对可以插入的间隙上锁、防止幻读)


死锁

个人总结:就是两个事务想要获取彼此的锁。
避免死锁:使用中间件,对于相同的行的更新,在进入引擎之前排队。防止有大量的死锁检查工作占用资源。