MySQL数据库由于自身架构的特点,存在多种数据储存引擎,MySQL中冈的储存引擎支持不同的锁机制。
MylSAM 和 MEMORY 存储引擎采用的是表级锁。
InnoDB储存引起既支持行级锁,也支持表级锁,默认情况下采用行级锁。
按照数据的操作类型分:
读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响。
写锁(排他锁):写操作没有完成前。它会阻断其他写锁和读锁。
按照数据操作的粒度分:
表级锁:开销小,加锁快,不会出现死锁。锁定粒度大,发生锁冲突的概率最高,并发度最低。
行级锁:开销大,枷锁慢,不会出现死锁。锁定粒度小,发生锁定冲突的概率最低,并发度最高。
页面锁:开销和加锁时间介于表锁和行锁之间,会出现死锁,锁定粒度界于表锁和行锁之间,并发度一般。
操作性能分为 乐观锁和 悲观锁:
乐观锁:实际上并没有加锁,只是锁一种思想。乐观的意思说,我每次拿数据的时候,认为别人不会修改,所以不会上锁,但是在提交的时候才会判断一下别人有没有修改过这个数据 。乐观锁适用于多读少写的场景。这样可以提高 吞吐量。通过在行数据上添加版本号,如果版本号跟预期的不一致则更新失败。(对于java来说,CAS,CurrentHashMap,atomic 都是 乐观锁)
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
- 取出记录时,获取当前 version
- 更新时,带上这个 version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果 version 不对,就更新失败
悲观锁:顾名思义,就是很悲观,每次去拿数据都认为别人去改数据,所以每次拿数据的时候都会上锁,这样别人想拿这个锁就会 block。(在java中的syschronized就是悲观锁)
乐观锁比较适用于读多写少的情况(多读场景),悲观锁比较适用于写多读少的情况(多写场景)。
行为锁分为共享锁和排他锁:
行锁的是mysql锁中粒度最小的一种锁,因为锁的粒度很小,所以发生资源争抢的概率也很少,并发性能很大,但是 也会造成死锁,每次加锁和释放锁的开销也会很大。
使用MySQL行级锁的两个前提:
使用innoDB引擎
开启事务(隔离级别为Repeatale Read)
InnoDB行锁的类型:
共享锁(S):当事务对数据加上共享锁后,其他用户可以并发读取数据,但任务事务都不能对数据进行修改,直到已释放所有的共享锁。
排他锁(X):如果事务T对数据A加上排他锁后,则其他事务不能再对数据A加任何类型的封锁。获取排他锁的事务可以 读数据,也可以写锁。
加锁的方式:
InnoDB引擎默认更新语句,update,delete,insert 都会自动 给涉及的数据 加上排他锁,select语句不会任何类型的锁,如果要加可以使用下面的方式:
加共享锁(S):select * from table_name where lock in share mode;
加排他锁(X): select * from table_name where for update;
锁兼容:
共享锁只能兼容共享锁,不兼容排他锁。
排他锁互斥 共享锁 和其他排他锁。
InnoDB行锁是通过对索引页上的记录加锁实现的。主要实现算法有3中:Record Lock,Grp Lock 和Next - key Lock.
RecordLock锁:锁定单个行记录的锁。(记录锁,RC,RR 隔离级别 都支持)。
GapLock锁:间隙锁,锁定索引记录间隙,确保索引的间隙不变。(外围锁,RR隔离级别支持)。
Next-key-Lock锁: 记录锁 和间隙锁组合,同时锁住数据,并且锁住数据前后范围。(记录锁+范围锁,RR隔离级别锁支持)