MySQL-锁(LOCK)

文章目录

    • 1. 锁是什么?
    • 2. 全局锁
      • 2.1 相关语法
      • 2.2 特点
    • 3. 表级锁
      • 3.1 表锁
        • 3.1.1 共享读锁(S)
        • 3.1.2 排它写锁(X)
      • 3.2 元数据锁(MDL)
      • 3.2 意向锁(IS、IX)
    • 4. 行级锁
      • 4.1 行锁
    • 5. 死锁
      • 5.1 死锁检测
      • 5.2 避免死锁

1. 锁是什么?

  • 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算资源(CPU、RAM、I/O)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。
  • MySQL中的锁,为了尽可能提高数据库的并发量,每次锁定的数据范围越小越好,越小的锁其耗费的系统资源越多,系统性能下降。为在高并发响应和系统性能两方面进行平衡,这样就产生了“锁粒度”的概念。 按照锁的粒度分,分为以下三类:
    1. 全局锁:锁定数据库中的所有表。
    2. 表级锁:每次操作锁住整张表。
    3. 行级锁:每次操作锁住对应的行数据。
  • 从锁的角度来说,表级锁适合以查询为主,只有少量按索引条件更新数据的应用,如 Web 应用。而行级锁更适合于有大量按索引条件,同时又有并发查询的应用,如一些在线事务处理( OLTP)系统

2. 全局锁

  • 全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句都将被阻塞。典型的使用场景是做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的完整性。

    MySQL-锁(LOCK)_第1张图片

    对数据库进行进行逻辑备份之前,先对整个数据库加上全局锁,一旦加了全局锁之后,其他的DDL、DML全部都处于阻塞状态,但是可以执行DQL语句,也就是处于只读状态,而数据备份就是查询操作。那么数据在进行逻辑备份的过程中,数据库中的数据就是不会发生变化的,这样就保证了数据的一致性和完整性。

2.1 相关语法

  1. 加全局锁:

    flush tables with read lock ;
    
  2. 数据备份:

    mysqldump -uroot –p1234 db_name > db_name.sql
    
  3. 释放锁:

    unlock tables ;
    

2.2 特点

  • 数据库中加全局锁,是一个比较重的操作,存在以下问题:

    • 如果在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆

    • 如果在从库上备份,那么在备份期间从库不能执行主库同步过来的二进制日志(binlog),会导致主从延迟

      在InnoDB引擎中,我们可以在备份时加上参数 --single-transaction 参数来完成不加锁的一致性数据备份。

      mysqldump --single-transaction -uroot –p123456 db_name > db_name.sql
      

3. 表级锁

3.1 表锁

  • 对于表锁,分为两类:表共享读锁(read lock)表排它写锁(write lock)

  • 语法:

    • 加锁:

      lock tables 表名... read/write
      
    • 释放锁:

      unlock tables
      
3.1.1 共享读锁(S)
  • 共享锁的代号是 S,是 Share 的缩写,也可称为读锁。是一种可以查看但无法修改和删除的数据锁。

  • 共享锁的锁粒度是行或者元组(多个行)。一个事务获取了共享锁之后,可以对锁定范围内的数据执行读操作。会阻止其它事务获得相同数据集的排他锁。简单来说就是获得读锁的事务A与其他事务B都可以对锁范围内的数据进行读操作,但都不能进行写操作

    MySQL-锁(LOCK)_第2张图片

3.1.2 排它写锁(X)
  • 排他锁的代号是 X,是 eXclusive 的缩写,也可称为写锁,是基本的锁类型。

  • 排他锁的粒度与共享锁相同,也是行或者元组。一个事务获取了排他锁之后,可以对锁定范围内的数据执行写操作。允许获得排他锁的事务更新数据,阻止其它事务取得相同数据集的共享锁和排他锁。简单来说就是获得写锁的事务A可以对锁范围内的数据进行读和写,但其他事务B不能对锁范围内的数据进行读和写

    MySQL-锁(LOCK)_第3张图片

3.2 元数据锁(MDL)

  • meta data lock , 元数据锁,简写MDL。MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。为了避免DML与DDL冲突,保证读写的正确性。这里的元数据,大家可以简单理解为就是一张表的表结构。 也就是说,某一张表涉及到未提交的事务时,是不能够修改这张表的表结构的

  • 在MySQL5.5中引入了MDL,当对一张表进行增删改查的时候,加MDL读锁(共享);当对表结构进行变更操作的时候,加MDL写锁(排他)。

  • 常见的SQL操作时,所添加的元数据锁:

    对应SQL 锁类型 说明
    lock tables xxx read / write SHARED_READ_ONLY / SHARED_NO_READ_WRITE
    select 、select … lock in share mode SHARED_READ 与SHARED_READ、 SHARED_WRITE兼容,与 EXCLUSIVE互斥
    insert 、update、 delete、select … for update SHARED_WRITE 与SHARED_READ、 SHARED_WRITE兼容,与 EXCLUSIVE互斥
    alter table … EXCLUSIVE 与其他的MDL都互斥

3.2 意向锁(IS、IX)

  • 为了允许行锁和表锁共存,实现多粒度锁机制InnoDB 还有两种内部使用的意向锁。意向锁是一种表锁,锁定的粒度是整张表,分为**意向共享锁( IS)意向排他锁( IX)**两类。

  • 添加意向共享锁:

    select ... lock in share mode
    
  • 添加意向排它锁:

    insert、update、delete、select...for update
    
  • 其中共享锁排他锁意向共享锁意向排他锁相互之间的兼容/互斥关系如下表所示,其中 Y 表示相容, N 表示互斥。

    参数 X S IX IS
    X(排他锁) N N N N
    S(共享锁) N Y N Y
    IX(意向排他锁) N N Y Y
    IS(意向共享锁) N Y Y Y

    如果一个事务请求的锁模式与当前的锁兼容, InnoDB 就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要等待锁释放。

4. 行级锁

  • 在 MySQL 中, InnoDB 行锁通过给索引上的索引项加锁来实现,如果没有索引, InnoDB 将通过隐藏的聚簇索引来对记录加锁。

  • InnoDB的行锁是针对于索引加的锁,如果不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,此时就会升级为表锁

  • InnoDB 支持 3 种行锁定方式:

    1. 行锁( Record Lock):直接对索引项加锁。

      select * from student where id = 1 for update;
      
    2. 间隙锁( Gap Lock):锁加在索引项之间的间隙,也可以是第一条记录前的“间隙”或最后一条记录后的“间隙”。

      select * from student where id > 2 and id < 5 for update;
      
    3. Next-Key Lock:行锁与间隙锁组合起来用就叫做 Next-Key Lock。 前两种的组合,对记录及其前面的间隙加锁

      select * from student where id > 4 for update;
      

4.1 行锁

  • InnoDB实现了以下两种类型的行锁:

    1. 共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排它锁。
    2. 排他锁(X):允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁。

    MySQL-锁(LOCK)_第4张图片

  • 常见的SQL语句,在执行时,所加的行锁如下:

    SQL 行锁类型 说明
    INSERT … 排他锁 自动加锁
    UPDATE … 排他锁 自动加锁
    DELETE … 排他锁 自动加锁
    SELECT(正常) 不加任何 锁
    SELECT … LOCK IN SHARE MODE 共享锁 需要手动在SELECT之后加LOCK IN SHARE MODE
    SELECT … FOR UPDATE 排他锁 需要手动在SELECT之后加FOR UPDATE

5. 死锁

  • 定义:死锁是指两个或两个以上的事务在执行过程中,因争夺资源而造成的一种互相等待的现象。 就是所谓的锁资源请求产生了回路现象,即死循环,此时称系统处于死锁状态或系统产生了死锁。常见的报错信息为“Deadlock found when trying to get lock…”。
  • 解决方案:死锁发生以后,只有部分或完全回滚其中一个事务,才能打破死锁。多数情况下只需要重新执行因死锁回滚的事务即可。

5.1 死锁检测

  • InnoDB的并发写操作会触发死锁,同时 InnoDB 也提供了死锁检测机制。通过设置 innodb_deadlock_detect 参数的值来控制是否打开死锁检测。
  1. innodb_deadlock_detect = ON默认值,打开死锁检测。数据库发生死锁时,系统会自动回滚其中的某一个事务, 让其它事务可以继续执行。

  2. innodb_deadlock_detect = OFF:关闭死锁检测。发生死锁时,系统会用锁等待来处理。

    锁等待是指在事务过程中产生的锁,其它事务需要等待上一个事务释放锁,才能占用该资源。如果该事务一直不释放,就需要持续等待下去,直到超过了锁等待时间。 当超过锁等待允许的最大时间,就会出现死锁,然后当前事务执行失败,自动执行回滚操作。

5.2 避免死锁

  • 在实际应用中,我们要尽量防止锁等待现象的发生,下面介绍几种避免死锁的方法:
    1. 如果不同程序会并发存取多个表,或者涉及多行记录时,尽量约定以相同的顺序访问表,这样可以大大降低死锁的发
      生。
    2. 业务中要及时提交或者回滚事务,可减少死锁产生的概率。
    3. 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率。
    4. 对于非常容易产生死锁的业务部分,可以尝试使用升级锁粒度,通过表锁定来减少死锁产生的概率(表级锁不会产生死锁)。

你可能感兴趣的:(MySQL,mysql,数据库)