Mysql锁

Mysql锁

锁是计算机协调多个进程或线程并发访问某一资源的机制

我们认知中的行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁,这些锁统称为悲观锁

概述

MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制。比如,MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking);BDB存储引擎采用的是页面锁(page-level locking),但也支持表级锁;InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。

  • 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
  • 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
  • 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般

从锁的角度来说:

  • 表级锁更适合于以查询为主,只有少量按索引条件更新数据的应用,如Web应用;
  • 而行级锁则更适合于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统。

MyISAM锁

MySQL的表级锁有两种模式:表共享读锁(Table Read Lock)表独占写锁(Table Write Lock)

InnoDB锁

InnoDB与MyISAM的不同:

  • 是否支持行级锁 : MyISAM 只有表级锁(table-level locking),而InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁。
  • 是否支持事务和崩溃后的安全恢复: MyISAM 强调的是性能,每次查询具有原子性,其执行速度比InnoDB类型更快,但是不提供事务支持。但是InnoDB 提供事务支持事务,外部键等高级数据库功能。 具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。
  • 是否支持外键: MyISAM不支持,而InnoDB支持
  • 是否支持MVCC :仅 InnoDB 支持。应对高并发事务, MVCC比单纯的加锁更高效;MVCC只在 READ COMMITTED 和 REPEATABLE READ 两个隔离级别下工作;MVCC可以使用 乐观(optimistic)锁 和 悲观(pessimistic)锁来实现;各数据库中MVCC实现并不统一

InnoDB实现了两种类型的行锁。

InnoDB行锁是通过给索引上的索引项加锁来实现的。

InnoDB这种行锁实现特点:

  • 只有通过索引条件检索数据,InnoDB才使用行级锁,否则锁表
  • 不论条件是普通索引还是主键索引,都会锁当前记录
  • 建议操作普通字段,条件索引字段
  • 共享锁(S):允许一个事务去读一行,阻止其他事务获得相同的数据集的排他锁。

    select … lock in share mode
    
  • 排他锁(X):允许获得排他锁的事务更新数据,但是阻止其他事务获得相同数据集的共享锁和排他锁。

    select … for update
    insert
    update
    delete
    

可以这么理解:
共享锁就是我读的时候,你可以读,但是不能写。排他锁就是我写的时候,你不能读也不能写。其实就是MyISAM的读锁和写锁,但是针对的对象不同了而已。

这两种方式在事务(Transaction) 进行当中SELECT 到同一个数据表时,都必须等待其它事务数据被提交(Commit)后才会执行。而主要的不同在于LOCK IN SHARE MODE 在有一方事务要Update 同一个表单时很容易造成死锁 (所以实际项目中基本不使用共享锁IS)。
简单的说,如果SELECT 后面若要UPDATE 同一个表单,最好使用SELECT … for UPDATE (上面的悲观锁就是用for update来实现的)。

除此之外InnoDB还有两个表锁:

  • 意向共享锁(IS):表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先取得该表的IS锁
  • 意向排他锁(IX):类似上面,表示事务准备给数据行加入排他锁,说明事务在一个数据行加排他锁前必须先取得该表的IX锁。

间隙锁

间隙锁作用在索引记录之间的间隔,又或者作用在第一个索引之前,最后一个索引之后的间隙。不包括索引本身。

这是出现在RR隔离级别下的一种锁,目的是防止幻读,MySQL会锁住相应数据的临近范围而避免其他事务再去插入新的数据,我们把它叫做间隙锁。

很显然,在使用范围条件检索并锁定记录时,InnoDB这种加锁机制会阻塞符合条件范围内键值的并发插入,这往往会造成严重的锁等待。因此,在实际应用开发中,尤其是并发插入比较多的应用,我们要尽量优化业务逻辑,尽量使用相等条件来访问更新数据,避免使用范围条件。

所以在很多情况下,我们会直接为了避免出现间隙锁,直接放弃RR这个MySql默认隔离级别而采用RC。

邻键锁

Next-key锁实际上是Record锁和gap锁的组合。Next-key锁是在下一个索引记录本身和索引之前的gap加上S锁或是X锁(如果是读就加上S锁,如果是写就加X锁)。
默认情况下,InnoDB的事务隔离级别为RR,系统参数innodb_locks_unsafe_for_binlog的值为false。InnoDB使用next-key锁对索引进行扫描和搜索,这样就读取不到幻象行,避免了幻读的发生。

  • 查询过程中只要访问的数据都会加锁,加锁的基本单位是next-key lock,左开右闭
  • 唯一索引等值查询,next-key lock退化为行锁
  • 索引等值查询,需要访问到第一个不满足条件的值,此时的next-key lock会退化为间隙锁
  • 索引范围查询需要访问到不满足条件的第一个值为止
  • 原则 1:加锁的基本单位是 next-key lock。希望你还记得,next-key lock 是前开后闭区间。
  • 原则 2:查找过程中访问到的对象才会加锁。
  • 优化 1:索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。
  • 优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。
  • 一个 bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止。

意向锁

需要强调一下,意向锁是一种不与行级锁冲突表级锁,这一点非常重要。意向锁分为两种:

  • 意向共享锁

    (intention shared lock, IS):事务有意向对表中的某些行加共享锁(S锁)

    -- 事务要获取某些行的 S 锁,必须先获得表的 IS 锁。
    SELECT column FROM table ... LOCK IN SHARE MODE;
    
  • 意向排他锁

    (intention exclusive lock, IX):事务有意向对表中的某些行加排他锁(X锁)

    -- 事务要获取某些行的 X 锁,必须先获得表的 IX 锁。
    SELECT column FROM table ... FOR UPDATE;
    

即:意向锁是有数据引擎自己维护的,用户无法手动操作意向锁,在为数据行加共享 / 排他锁之前,InooDB 会先获取该数据行所在在数据表的对应意向锁。MySql InnoDB 中意向锁的作用

锁分类

  • 悲观锁
    • 行锁
      • 共享锁(S)/ 读锁 (innodb)
      • 排他锁(X)/ 写锁 (innodb)
      • 间隙锁
    • 表锁
      • 意向共享锁(IS)(innodb)
      • 意向排他锁(IX)(innodb)
      • 表共享读锁 (myisam)
      • 表共享写锁 (myisam)
  • 乐观锁
    • 版本号
    • CAS

加锁情况

针对当前读select ... for update

索引使用条件\索引类型 聚簇索引 二级唯一索引 二级非唯一索引
精确匹配(RC级别) 命中索引项记录锁 命中索引项、对应聚簇索引项记录锁 命中索引项、对应聚簇索引项记录锁
精确匹配(RR级别) 命中索引项记录锁 命中索引项、对应聚簇索引项记录锁 命中索引项临键锁,后一索引项间隙锁,对应聚簇索引项记录锁
范围匹配(RC级别) 所有命中索引项记录锁 所有命中索引项、对应聚簇索引项记录锁 所有命中索引项、对应聚簇索引项记录锁
范围匹配(RR级别) 所有命中索引项、后一索引项临键锁 所有命中索引项、后一索引项临键锁,对应聚簇索引项记录锁 所有命中索引项、后一索引项临键锁,对应聚簇索引项记录锁
索引未命中(RC级别) 不加锁 不加锁 不加锁
索引未命中(RR级别) 未命中索引与前后索引的间隔加gap锁 未命中索引与前后索引的间隔加gap锁 未命中索引与前后索引的间隔加gap锁
不使用索引(RC级别) 全表扫描,读到的每行加记录锁(释放不符合条件的锁)
不使用索引(RR级别) 全表扫描,读到的每一行加临键锁,直到事务结束释放

参考链接

posted @ 2022-06-16 09:18  Faetbwac  阅读(20)  评论(0编辑  收藏  举报