锁
加锁策略
乐观锁:乐观锁总是假设最好的情况,乐观锁常见实现:
版本号控制,数据行保存 version 字段,修改时会变化。MySQL-InnoDB-MVCC多版本并发控制
CAS 算法(compare and swap),CAS算法涉及到三个操作数,需要读写的内存值 V,进行比较的值 A,拟写入的新值 B。 当且仅当 V 的值等于 A 时,CAS 通过原子方式用新值 B 来更新 V 的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。
CAS 常见问题
- ABA问题:一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值。在这段时间它的值可能被改为其他值,然后又改回A
- 循环时间长开销大:自旋CAS(也就是不成功就一直循环执行直到成功)如果长时间不成功,会给CPU带来非常大的执行开销。
- 只能保证一个共享变量的原子操作:CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效。
悲观锁:总是假设最坏的情况,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程
锁粒度分类
表锁,粒度最大,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁 。
行锁,粒度最小 的一种锁,只针对当前操作的行的索引进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。
InnoDB支持的行级锁:
Record Lock: 对索引项加锁,锁定符合条件的行。
Gap Lock: 对索引项之间的“间隙”加锁,不包括记录本身。其他事务不能在锁范围内插入数据,这样就防止了别的事务新增幻影行。
Next-key Lock: 锁定索引项本身和索引范围。即Record Lock和Gap Lock的结合。可解决幻读问题。
页锁,开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
相关知识点:
- innodb对于行的查询使用next-key lock
- Next-locking keying为了解决Phantom Problem幻读问题
- 当查询的索引含有唯一属性时,将next-key lock降级为record key
- Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生
- 有两种方式显式关闭gap锁:(除了外键约束和唯一性检查外,其余情况仅使用record lock) A. 将事务隔离级别设置为RC B. 将参数innodb_locks_unsafe_for_binlog设置为1
锁可写分类
共享锁(S),如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不 能加排他锁。获取共享锁的事务只能读数据,不能修改数据。
排他锁(X),如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获取排他锁的事务既能读数据,又能修改数据。
意向共享锁(IS): 表示事务准备给数据行记入共享锁,事务在一个数据行加共享锁前必须先取得该表的IS锁。
意向排他锁(IX): 表示事务准备给数据行加入排他锁,事务在一个数据行加排他锁前必须先取得该表的IX锁。