MySQL学习——锁

加锁策略

  • 乐观锁:乐观锁总是假设最好的情况,乐观锁常见实现:

    • 版本号控制,数据行保存 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的结合。可解决幻读问题。

  • 页锁,开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

相关知识点:

  1. innodb对于行的查询使用next-key lock
  2. Next-locking keying为了解决Phantom Problem幻读问题
  3. 当查询的索引含有唯一属性时,将next-key lock降级为record key
  4. Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生
  5. 有两种方式显式关闭gap锁:(除了外键约束和唯一性检查外,其余情况仅使用record lock) A. 将事务隔离级别设置为RC B. 将参数innodb_locks_unsafe_for_binlog设置为1

锁可写分类

  • 共享锁(S),如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不 能加排他锁。获取共享锁的事务只能读数据,不能修改数据。

  • 排他锁(X),如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获取排他锁的事务既能读数据,又能修改数据。

  • 意向共享锁(IS): 表示事务准备给数据行记入共享锁,事务在一个数据行加共享锁前必须先取得该表的IS锁。

  • 意向排他锁(IX): 表示事务准备给数据行加入排他锁,事务在一个数据行加排他锁前必须先取得该表的IX锁。