Mysql锁的类型原理学习


注意:内容来源是其他网友的总结,个人进行的总结学习,忘记记录原来的网址了,之后有机会我找出并进行标记,抱歉。

一、Mysql锁的类型

  1. 共享/排它锁(Shared and Exclusive Locks)
    • 设置共享锁:select * from user where id = 1 LOCK IN SHARE MODE; 读取某行数据
    • 设置排他锁:select * from user where id = 1 FOR UPDATE;修改某些数据
    • S锁和X锁
      • S shared lock 共享锁 读锁
        其他只能增加S锁,不能增加X锁,只允许读,S锁解除之前,不允许修改。
      • X exclusive lock 排他锁 写锁
        其他不能操作,得到锁的对象可以增删改查。
  2. 意向锁(Intention Locks) 使得行锁和表锁能够共存
    意向锁是表级别的锁,用来说明事务稍后会对表中的数据行加哪种类型的锁(共享锁或独占锁)
  3. 记录锁(Record Locks) 索引记录上的锁
    锁定索引记录,没有索引记录,就去锁定隐式(聚集索引)
  4. 间隙锁(Gap Locks)
    间隙锁是一个在索引记录之间的间隙上的锁
  5. 临键锁(Next-key Locks)
    Next-Key Locks是行锁与间隙锁的组合。当InnoDB扫描索引记录的时候,会首先对选中的索引记录加上记录锁(Record Lock),然后再对索引记录两边的间隙加上间隙锁(Gap Lock)
  6. 插入意向锁(Insert Intention Locks)
    插入意向锁是在数据行插入之前通过插入操作设置的间隙锁定类型
  7. 自增锁(Auto-inc Locks)
    自增锁是事务插入到有自增列的表中而获得的一种特殊的表级锁

二、锁的实现方式

  • InnoDB行锁是通过给索引加锁来实现的,如果没有索引,InnoDB会通过隐藏的聚簇索引来对记录进行加锁(全表扫描,也就是表锁)
  • 语句操作,有主键的列(锁主键索引),无主键的列(先锁无主键索引,再锁主键索引)

三、隔离级别

  1. 读未提交(Read Uncommitted)
    无锁
  2. 读提交(Read Committed, RC)
    select是快照读,update\delete 显示加锁的select
    1、外键约束检查和重复键封锁区间
    2、其他记录锁
  3. 可重复读(Repeated Read, RR)
    select是快照读,update\delete 显示加锁的select
    1、索引上使用唯一的查询条件,使用记录锁
    2、唯一索引上使用范围查询,使用间隙锁和临键锁
  4. 串行化(Serializable)
    此时所有select语句都会被隐式加锁:select … in share mode.

四、快照读、当前读

  • 快照读,读取的是记录的可见版本 (有可能是历史版本),不用加锁。
  • 当前读,读取的是记录的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录。

五、MVCC:multi-version concurrency control

  1. MVCC定义:多版并发控制系统。可认为是行级锁的一个变种,它能够避免更多情况下的加锁操作。
  2. 作用:避免一些加锁操作,提升并发性能
  3. 实现:通过在每行记录的后面保存行的创建时间和过期时间或删除时间(它们是隐藏的),这两个时间实际都是系统的版本号。每开始一个新的事务,版本号都会自动增加
  4. 具体原理
    4.1) select:innoBD查询时会检查以下两个条件:一个是数据行的版本号早于当前事务的版本号;另一个是行的删除版本号,要么没有,要么大于当前事务的版本号。
    4.2)insert/delete:innoDB将当前的系统版本号作为新插入(删除)的数据行的版本号。
    4.3)update:先新插入一行数据,并将当前系统版本号作为行的版本号,同时将当前系统版本号作为原来行的删除版本号。更新主键时,聚集索引和普通索引都会产生两个版本;而更新非主键时,只有普通索引会产生两个版本。
  5. 注意:MVCC只在read committed和repeatable read两个隔离级别下工作
    快照读:RR 重复读 select
    当前读 insert into update delete 显示加锁的select

六、行级锁的争用情况

  • 显示争用情况
    show status like ‘InnoDB_row_lock%’;
  • 设置监视器:mysql> create table InnoDB_monitor(a INT) engine=InnoDB;
  • 查看:mysql> show engine InnoDB status;
  • 停止查看:mysql> drop table InnoDB_monitor;

七、死锁:

  • 什么是死锁:一般是由于两个事务同时操作两个表,但加锁的顺序是不一致出现的。比如A事务先锁a表,B事务先锁b表;当A去锁b表的时候发现b表被B事务锁住了,要等待;当B事务去锁a表的时候发现a表被A锁住了。于是出现了死锁
  • 解决方式:回滚较小的那个事务
    当产生死锁的场景中涉及到不止InnoDB存储引擎的时候,InnoDB是没办法检测到该死锁的,这时候就只能通过锁定超时限制参数InnoDB_lock_wait_timeout来解决。

八、优化行级锁

  1. 要想合理利用InnoDB的行级锁定,做到扬长避短,我们必须做好以下工作:
    a)尽可能让所有的数据检索都通过索引来完成,从而避免InnoDB因为无法通过索引键加锁而升级为表级锁定;
    b)合理设计索引,让InnoDB在索引键上面加锁的时候尽可能准确,尽可能的缩小锁定范围,避免造成不必要的锁定而影响其他Query的执行;
    c)尽可能减少基于范围的数据检索过滤条件,避免因为间隙锁带来的负面影响而锁定了不该锁定的记录;
    d)尽量控制事务的大小,减少锁定的资源量和锁定时间长度;
    e)在业务环境允许的情况下,尽量使用较低级别的事务隔离,以减少MySQL因为实现事务隔离级别所带来的附加成本。

  2. 由于InnoDB的行级锁定和事务性,所以肯定会产生死锁,下面是一些比较常用的减少死锁产生概率的小建议:
    a)类似业务模块中,尽可能按照相同的访问顺序来访问,防止产生死锁;
    b)在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;
    c)对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率。
    查看sql的锁信息

  3. 查看sql句的锁信息前,需要做如下几件事:

    • 查看事务的隔离级别:

      • 通过show global variables like “tx_isolation”; 命令查看。
      • 可通过执行set session transaction isolation level repeatable read;
      • 更改成我们想要隔离级别,隔离级别取值如下:
        • read uncommitted、read committed、repeatable read、serializable
    • 保证事务为手动提交:

      • 通过show global variables like “autocommit”;查看。
      • 如果为ON,则通过执行set session autocommit=0;改为手动提交。
    • 保证间隙锁开启:

      • 通过show global variables like “innodb_locks%”;查看
        OFF时表示开启。默认是OFF

九、MVCC讲解

  • Mysql有三种级别的锁定:表级锁定、页级锁定、行级锁定
    每次锁定的是一行数据的锁机制就是行级别锁定(row-level)。行级锁定不是MySQL自己实现的锁定方式,而是由其他存储引擎自己所实现的

  • 优缺点:由于锁粒度小,争用率低,并发高
    实现复杂,开销大,加锁慢、容易出现死锁
    使用行级锁定的主要有InnoDB存储引擎,以及MySQL的分布式存储引擎NDBCluster

  • 行级锁 共享锁和排他锁

推荐阅读

(原始引用位置已经丢失,抱歉,推荐找到的好的路径)
[1] mysql 查询通过时间预定_阿里达摩院推荐Mysql面试题答案,助你“过五关斩六将”

猜你喜欢

转载自blog.csdn.net/u011008832/article/details/113740008