先来一波锁乱炖……
画了个导图,以后放
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. 悲观锁
每次取数据时都认为其他线程会修改,所以都会加锁。一旦加锁,不同线程同时执行时,只能有一个线程执行,其他的线程在入口处等待,直到锁被释放。