Mysql锁详解

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用版 2核4GB 50GB
简介: mysql锁

 前言

MySQL中不同的存储引擎支持不同的锁机制。比如

  • MyISAMMEMORY存储引擎采用的是表级锁(table-level locking);
  • BDB 存储引擎采用的是页面锁(page-level locking),但也支持表级锁;
  • InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。

从对数据操作的类型来看,可以分为:

  1. 读锁(共享锁):事务T对数据对象A加上读锁,则事务T只可以读A不能修改,其他事务也只能对数据A加读锁。
  2. 写锁(排它锁)事务T对数据对象A加上写锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。

一、表级锁、行级锁、页面锁的区别

    • 表级锁:偏向MyISM存储引擎,开销小,加锁块;无死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
    • 行级锁:偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率最低,并发度最高。 InnoDB与MyISAM最大不不同有两点,一是支持事务,                         二 是采用了行级锁
    • 页面锁开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

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

    二、MyISAM表锁

    2.1 查看表锁争用情况

      • Table_locks_immediate 表示立即释放表锁数
      • Table_locks_waited 表示需要等待的表锁数

      如果waited值比较大,说明存在严重的表锁争用情况。

      20210226160526448.png

      2.2 如何加表锁

      MyISAM 在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATEDELETEINSERT 等)前,会自动给涉及的表加写锁,这个过程并不需要用户干预,如果需要显式加锁,必须同时取得所有涉及到表的锁。例如:

      Lock tables 表1 read local, 表2 read local;
      Select sum(total) from 表1;
      Select sum(total) from 表2;
      Unlock tables;

      image.gif2.3 MyISAM锁调度

      MyISAM 存储引擎的读锁和写锁是互斥的,读写操作是串行的。那么,一个进程请求某个 MyISAM 表的读锁,同时另一个进程也请求同一表的写锁,MySQL 如何处理呢?

      答案是写进程先获得锁。不仅如此,即使读请求先到锁等待队列,写请求后到,写锁也会插 到读锁请求之前!这是因为 MySQL 认为写请求一般比读请求要重要。这也正是 MyISAM 表 不太适合于有大量更新操作和查询操作应用的原因,因为,大量的更新操作会造成查询操作很难获得读锁,从而可能永远阻塞。

      三、InnoDB锁问题

      InnoDB默认使用的是行锁:行锁开销大,加锁慢,会出现死锁,锁定粒度最小,发生锁冲突的概率最低,并发度最高。

      可以通过检查 InnoDB_row_lock 状态变量来分析系统上的行锁的争夺情况

      20210227103438935.png

      除了读锁与写锁之外,为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB 还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁。

        • 意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的 IS 锁。
        • 意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的 IX 锁。

        示例SQL准备:

        CREATE TABLE demo_innodb_lock(
           id INT(11),
           name VARCHAR(16),
           sex VARCHAR(1)
        )ENGINE = INNODB DEFAULT CHARSET = utf8
        INSERT INTO demo_innodb_lock VALUES (1,'100','1');
        INSERT INTO demo_innodb_lock VALUES (1,'200','0');
        INSERT INTO demo_innodb_lock VALUES (2,'90','0');
        INSERT INTO demo_innodb_lock VALUES (3,'400','1');
        INSERT INTO demo_innodb_lock VALUES (4,'300','0');
        INSERT INTO demo_innodb_lock VALUES (5,'500','1');
        INSERT INTO demo_innodb_lock VALUES (6,'600','0');
        INSERT INTO demo_innodb_lock VALUES (7,'700','1');
        INSERT INTO demo_innodb_lock VALUES (8,'800','0');
        CREATE INDEX idx_id ON demo_innodb_lock(id);
        CREATE INDEX idx_name ON demo_innodb_lock(name);

        image.gif

        把自动提交关闭

        SET autocommit = 0;

        image.gif

        示例1:对于UPDATE、DELETE、INSERT语句,InnoDB会自动给涉及数据集加排他锁(X)  

        20210227140059274.png

        此时由于事务没有提交,再开一个窗口对同一行进行更新操作,会发现处于阻塞状态

        20210227140149371.png

        当第一个窗口中的事务执行commit;后,释放排它锁,如果第二个窗口中锁等待没有超时,则会立即执行

        示例2:行锁升级为表锁

        在示例准备中我们给id和name创建了索引,但是此时索引失效了,执行更新操作后不提交

        20210227143759549.png

        在第二个窗口执行更新并提交

        20210227144236185.png

        此时在第一个窗口中查询发现,id=4的数据并没有发生改变,问题是操作的不是同一行数据,说明此时已经升级为表锁了,原因就是索引失效。

        20210227144343887.png

        提交窗口一中的事务,释放表锁,再次查询

        20210227144534687.png

        3.1 InnoDB行锁的实现方式

          1. InnoDB行锁是通过给索引上的索引项加锁来实现的,InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。
          2. 由于 MySQL 的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。
          3. 当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论是使用主键索引、唯一索引或普通索引,InnoDB 都会使用行锁来对数据加锁。
          4. 即便在条件中使用了索引字段,但是否使用索引来检索数据是由 MySQL 通过判断不同执行计划的代价来决定的,如果 MySQL 认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下 InnoDB 将使用表锁,而不是行锁。

          3.2 间隙锁Next-Key 锁)

          当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB 会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙

          (GAP)”,InnoDB 也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-Key 锁)。

          示例:

          20210227145149662.png

          此时去执行这样一条更新语句时,由于数据库中没有id=2的数据,那么就意味着存在间隙。

          20210227145327306.png

          窗口1中事务没提交的情况下,在窗口2插入一条ID=2的数据,发现插入不进去,证明了是存在间隙锁的;提交窗口1中的事务后,窗口2中SQL执行成功。

          20210227145555529.png

          InnoDB 使用间隙锁的目的,一方面是为了防止幻读,以满足相关隔离级别的要求,另外一方面,是为了满足其恢复和复制的需要。

          相关实践学习
          基于CentOS快速搭建LAMP环境
          本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
          全面了解阿里云能为你做什么
          阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
          相关文章
          |
          8天前
          |
          SQL 关系型数据库 MySQL
          MySQL数据库进阶第五篇(锁)
          MySQL数据库进阶第五篇(锁)
          |
          13天前
          |
          存储 SQL 关系型数据库
          MYSQL--锁机制*
          MYSQL--锁机制*
          |
          1月前
          |
          关系型数据库 MySQL 数据库
          MySQL的行级锁锁的到底是什么?
          本文简述了InnoDB的行级锁机制,包括记录锁、间隙锁和Next-Key锁。记录锁锁定索引记录,防止其他事务对相同值的行进行操作;间隙锁锁定索引记录间的间隙,防止插入。Next-Key锁是两者的结合,锁定记录及其前后间隙。在可重复读(RR)隔离级别下,加锁策略涉及Next-Key锁,但会因查询条件退化为行锁或间隙锁。MySQL的加锁机制遵循两个原则和两个优化,例如唯一索引等值查询时退化为行锁。RR级别虽能防止幻读,但也可能降低并发并引发死锁,因此有些场景下会选择读已提交(RC)级别。
          MySQL的行级锁锁的到底是什么?
          |
          13天前
          |
          存储 SQL 关系型数据库
          【MySQL技术内幕】6.3-InnoDB中的锁
          【MySQL技术内幕】6.3-InnoDB中的锁
          146 57
          |
          2天前
          |
          存储 关系型数据库 MySQL
          深入浅出MySQL事务管理与锁机制
          MySQL事务确保数据一致性,ACID特性包括原子性、一致性、隔离性和持久性。InnoDB引擎支持行锁、间隙锁和临键锁,提供四种隔离级别。通过示例展示了如何开启事务、设置隔离级别以及避免死锁。理解这些机制对优化并发性能和避免数据异常至关重要。【6月更文挑战第22天】
          81 3
          |
          13天前
          |
          存储 关系型数据库 MySQL
          深入研究MySQL意向锁
          MySQL意向锁是一种特殊的表级锁,由InnoDB存储引擎在操作数据之前自动添加,无需用户干预。它分为意向共享锁(IS)和意向排他锁(IX)两种。意向锁的主要作用是协调行锁和表锁的关系,优化加锁策略,避免全表扫描判断是否存在行锁。意向锁之间不会冲突,但会与表级别的排他锁冲突,从而确保数据库并发访问的一致性和完整性。简而言之,意向锁提高了数据库并发操作的性能和效率。
          144 5
          |
          13天前
          |
          存储 SQL 关系型数据库
          【MySQL技术内幕】6.5-锁问题、阻塞、死锁、锁升级
          【MySQL技术内幕】6.5-锁问题、阻塞、死锁、锁升级
          37 2
          |
          13天前
          |
          SQL 存储 算法
          【MySQL技术内幕】6.4-锁的算法
          【MySQL技术内幕】6.4-锁的算法
          23 1
          |
          5天前
          |
          SQL NoSQL 关系型数据库
          Mysql锁及适用场景
          Mysql锁及适用场景
          12 0
          |
          13天前
          |
          存储 SQL 关系型数据库
          【MySQL技术内幕】6.1-锁、lock和latch
          【MySQL技术内幕】6.1-锁、lock和latch
          22 0