1. MySql 锁

1.1. 锁概述

mysql相较于其他数据库而言,其所比较简单,不同存储引擎支持的锁也不同

  • MyISAM 和MEMORY存储引擎采用的是表级锁(table-level locking)
  • InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,默认情况采用的是行级锁

    1.2. 行锁、表锁特点

  1. 存储引擎支持的锁

MyISAM:只支持表级锁
INNODB:支持行级锁、表级锁

  1. 针对锁的粒度,一般划分为:行锁、表锁

行锁:访问数据库的时候,锁定表中的行数据,防止并发错误
表锁:访问数据库的时候,锁定整表数据,防止并发错误

  1. 行锁和表锁的区别

行锁具有开销大,加锁慢的特点。会出现死锁,锁的粒度小,发生锁冲突的概率小。并发高
表锁具有开销小,加锁快的特点。不会出现死锁,因为会一次性获取所需的全部锁。锁的粒度大,发生锁冲突概率高。并发低

1.3. 表锁使用场景

对于InnoDB存储引擎,在绝大部分情况下都是使用的行锁,因为InnoDB支持事务且行锁并发高是我们选择InnoDB存储引擎的原因。在个别情况下也可以考虑使用表锁。

  • 情况一:事务需要更新表中绝大部分数据或全部表中数据,表的内容又比较多。如果使用行级锁,不仅执行效率低,而且可能造成其他事务长时间的等待和锁冲突,这种情况下可以考虑使用表锁来提高该事务的执行效率
  • 情况二:一个事务中涉及的表多,比较复杂,可能会引起死锁导致大量事务回滚。这种情况可以考虑一次性锁定事务所设计的表,避免表死锁减少数据库的事务回滚带来的开销

1.4. 行锁使用场景

行锁适用于高并发的场景中,对事务完整性要求高的系统。

1.5. 悲观锁和乐观锁

  1. 悲观锁:顾名思义,就是很悲观,每次操作数据时都会认为其他人会去修改,所以在操作数据时都会上锁,这样其他人想操作这个数据必须等待我操作完成才能拿到锁。传统关系型数据库中的表锁、行锁、读写锁等都是采用这种悲观锁
  2. 乐观锁:顾名思义,就是很乐观的态度,每次操作数据时都会认为其他人不会修改数据,所以在操作数据时不会上锁,只会在自己修改时做一次判断其他人是否修改了数据,其执行效率高于悲观锁。著名的CAS机制就是典型的乐观锁。
  3. 悲观锁和乐观锁的区别

因为乐观锁的执行效率高于悲观锁,所以乐观锁适用于读多写少的场景。如果是写多的情况下,为了避免锁冲突导致上层应用不断的retry降低吞吐性能,而采用悲观锁就比乐观锁合适

1.6. 共享锁

共享锁指多个事务对同一资源共享同一把锁,相当于一扇门有多把钥匙,每把钥匙都能打开这扇门,这就是所谓的共享锁

对于悲观锁,mysql关系型数据库都已经实现了,共享锁也是属于悲观锁的一种。在mysql中可以通过在执行语句后面加上lock in share mode 就表示对这些资源加上共享锁

2. 死锁

2.1. 死锁的原因

死锁的原因是指两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,此时处于死锁状态或者系统产生了死锁,这些永远在互相等待的进程称为死锁进程。表级锁不会产生死锁,所以解决死锁的主要还是针对InnoDB存储引擎

2.2. 死锁案例

2.2.1. 情况一

image.png
两个session分别通过一个sql持有一把锁,然后互相访问对方加锁的数据产生死锁。

2.2.2. 情况二

image.png
两个单条的sql语句涉及到的加锁数据相同,但是加锁顺序不同,导致了死锁。