《MySQL系列》锁笔记

223 阅读3分钟

先来一波锁乱炖……
画了个导图,以后放

1. 行锁

锁一行或者多行记录,InnoDB默认采用行级锁,采用自动加锁机制。

  • 对于UPDATE、DELETE、INSERT语句,InnoDB会自动给涉及到的数据集加排它锁;
  • 对于普通SELECT语句,InnoDB不会加任何锁,但是可以显式加锁:
    • 共享锁:lock in share more
    • 排它锁:for update
      • 如果有匹配的数据,且用到了索引(主键、唯一),则为行锁,否则为表锁。
      • 如果未查到数据则无锁;索引实效,则为表锁 ——仅适用于InnoDB存储引擎,且必须在事务块中才能生效

优势: 锁的粒度小,并发能力强。

劣势: 开销大,锁表慢,死锁。

1)记录锁

记录锁锁的是表中的某一条记录,记录锁的出现条件必须是精准命中索引并且索引是唯一索引。

2)间隙锁

又称区间锁,每次锁定都是锁定一个区间。查询条件命中索引,并且没有查询到符合条件的记录,此时就会将查询条件中的范围数据进行锁定(即使是范围库中不存在的数据也会被锁定)。

间隙锁只会出现在可重复读的事务隔离级别中,且不只在二级索引上有,有间隙的地方就可能有间隙锁。

3)临键锁

查询条件命中索引,有匹配到数据库记录。mysql的行锁默认就是使用的临键锁,临键锁是由记录锁和间隙锁共同实现的。

2. 表锁

锁定一整张表,在表被锁定期间,其他事务不能对该表进行操作,必须等当前表的锁被释放后才能进行操作。

加锁方式:

  • lock tables ... read/write,可以用unlock tables主动释放锁。
  • MDL(metadata lock)不需要显式使用,在访问一个表的时候会被自动加上。

优势: 开销小,加锁快,无死锁。

劣势: 锁的粒度大,发生锁冲突的概率高,并发处理能力低。

3. 全局锁

对整个数据库实例加锁。

加锁方式: Flush tables with read lock

4. 页锁

开销和加锁时间介于表锁和行锁之间;会出现死锁;锁定粒度介于表锁和行锁之间,并发处理能力一般。

5. 排它锁

也称写锁、独占锁,当前写操作没有完成前,它会阻断其他写锁和读锁。

6. 共享锁

也称读锁,多用于判断数据是否存在,多个读操作可以同时进行而不会互相影响。当如果事务对读锁进行修改操作,很可能会造成死锁。

7. 乐观锁

认为不会产生并发问题,因此不会上锁。但是在更新时会判断其他线程在这之前有没有对数据进行修改,如果有修改则操作失败进行回滚。

实现方式:

  • Version:对数据库表增加version字段,更新时修改version,操作完后检验version是否被修改过。
  • CAS(compare and swap)算法:当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。

8. 悲观锁

每次取数据时都认为其他线程会修改,所以都会加锁。一旦加锁,不同线程同时执行时,只能有一个线程执行,其他的线程在入口处等待,直到锁被释放。