Mysql锁分类:一文搞懂各种锁及使用场景

一,Mysql锁分类

在这里插入图片描述

二、按粒度分

1,全局锁

是对整个数据库实例加锁,加锁后整个实例处于只读状态,后续的DML、DDL语句以及已经执行更新操作的事务提交语句都将被阻塞。一般用于数据库备份、恢复等操作。

--加全局锁
flush tables with read lock; 
-- 执行数据库备份命令,比如使用mysqldump
mysqldump -u username -p database_name > backup.sql
--释放锁
unlock tables;

2,表级锁

每次操作锁住整张表。开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率高,并发度低,MyISAM、InnoDB、BDB等存储引擎都会应用。

具体表现为三种锁:表锁、元数据锁、意向锁

2.1 表锁

表锁分为表读锁和写锁两种(注意和行锁的"共享锁"“独占锁”有区别):

  • 读锁:
    1. 所有线程都可对该表进行读操作,但不允许进行写操作,包括当前线程也不行;
    2. 当锁住了A表之后,就只能对A表进行读操作,不能对其他表进行操作;
  • 写锁:只有当前线程可以对表进行读、写操作,其他线程的读、写操作都会等待,直到锁被释放为止;
-- 读锁
LOCK TABLE table_name [ AS alias_name ] READ
# sql
UNLOCK tables

-- 写锁
LOCK TABLE table_name [AS alias_name] [ LOW_PRIORITY ] WRITE
# sql
UNLOCK tables
2.2 元数据锁

元数据锁( MDL)锁定数据库对象的元数据,如表结构;

MDL锁主要作用是维护表元数据的数据一致性,当某一张表有未提交的事务时,其他的连接不能修改表的结构,加锁过程是系统自动控制,无需显式使用。

  • 当对一张表进行增删改查的时候,会加元数据共享锁( SHARED_READ 或 SHARED_WRITE );
  • 当对表结构进行变更操作的时候,会加元数据排他锁( EXCLUSIVE )。
--查看数据库中的元数据锁情况:
select object_type,object_schema,object_name,lock_type,lock_duration
from performance_schema.metadata_locks;

2.3 意向锁

为了解决行锁与表锁的冲突, 在 InnoDB 引擎中引入了意向锁, 加行锁时同时也会给该表加上意向锁,这样加表锁时就不需要去检查每行是否有行锁了;

  • 意向共享锁( IS ):由语句select … lock in share mode添加,与表读锁是兼容的;
  • 意向排他锁( IX ):由insert、update、delete、select…for
    update添加,与表读锁、写锁都是互斥的。
--查看数据库中的意向锁和行锁情况:
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data 
from performance_schema.data_locks;

3,页级锁

由数据库底层数据存储结构可知,一张表可以存储成百上千万条数据,而数据库底层是以页为单位的,一页大小为16KB,所以一张数据库的表有可能会有很多很多页的数据。

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

BDB存储引擎采用的是页面锁(page-level locking),但也支持表级锁;

“页锁”现实业务中使用较少,仅做了解即可。

4,行级锁

行级锁是Mysql中锁定粒度最细的一种锁,仅 InnoDB支持,MyISAM不支持;

行级锁能大大减少数据库操作的冲突,数据库并发能力极高,但由于其粒度小,加锁的开销大,且容易出现死锁的情况。

  • 共享锁(S):对某一资源加共享锁,自身可以进行读写操作,其他事务可以读,但不能写。
  -- 加共享锁
  select * from table lock in share mode
  -- for share是Mysql8.0新增特性,是lock in share mode的替代品, 支持nowait、skip locked和of tbl_name选项
  select * from table for share
  • 排他锁(X):对某一资源加排他锁,自身可以进行读写操作,其他事务不能再在其上加其他的锁(共享锁或排它锁)。
   -- 加排它锁
   select * from table for update

Mysql的"当前读"都会加行锁,"insert、update、delete"自动加,“select for update”、 "select lock in share mode"在事务中手动加。

而"快照读"不会加锁,如普通"select * from *"

注意:排他锁指的是其他事务不能再在其上加其他的锁,而不是说其它事务不能读。select语句默认不会加任何锁类型,所以其它事务是可以通过普通select语句查询到“排它锁”的数据的。

注意:行级锁都是基于索引的,如果一条SQL语句没有用到索引,是不会使用行级锁的,会自动升级为表锁锁住所有数据。

-- 查看意向锁和行锁情况
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data 
from performance_schema.data_locks;

行级锁可以细分为记录锁、间隙锁、临键锁:

4.1 记录锁( Record Lock )

又叫“行锁”,锁定单个行记录,防止其他事务对此行进行update和delete。在RC、RR隔离级别下都支持。

4.2 间隙锁( Gap Lock )

锁定索引记录之间的一个范围(开区间,不包括双端端点),用于防止其他事务在范围内插入数据产生“幻读” ,但不包含记录本身。在RR隔离级别下支持。

4.3 临键锁( Next-Key Lock )

结合了行锁和间隙锁的特性,同时锁住数据,并锁住数据前后的间隙(左开右闭区间)。在RR隔离级别下支持。

默认情况下,innodb使用临键锁( Next-Key Lock )来锁定记录。

各种锁触发条件:

  1. 使用普通索引检索时,不管是何种查询,只要加锁,都会产生间隙锁(Gap Lock)。
  2. 针对唯一索引进行等值匹配检索时:对已存在的记录加锁,会自动优化为行锁;对不存在的记录加锁时,会优化为间隙锁(Gap Lock)。
  3. 针对唯一索引进行范围匹配检索时:存在的记录会产生行锁,对于满足查询条件但不存在的数据产生间隙锁,加在一起就是临键锁(Next-Key Lock)。

针对唯一索引进行范围匹配检索,8.0.17 版本是前开后闭,而 8.0.18 版本及以后,修改为了前开后开区间;

三、按兼容性分

  • 读锁、共享锁(S);
  • 写锁、独占锁(X);

在按粒度分时,各粒度锁都分别说明此两个类型锁了,此处不在赘述,唯一要再次强调的一点是:表级锁的"读锁"和行级锁的"共享锁",表级锁的"写锁"和行级锁的"独占锁",冲突检测并不一样!!!

所以,很多人喜欢把"共享锁"叫做"读锁",“独占锁"叫做"写锁”,个人并不赞同这么叫,容易照成概念混淆。

四、按加锁态度分

乐观锁与悲观锁是一个抽象的概念描述,是对锁的一种态度,并不是一种真正的锁。可以简单理解为:乐观锁是人工手动控制冲突的一种技术实现手段,而悲观锁就是上文中讲到的mysql中自带的各种真正的锁。

1,乐观锁

总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不加锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用“版本号机制”来实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量。

2,悲观锁

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。上文说到的全局锁、表锁、页锁、行锁等等,都是在做操作之前先上锁,也就是所谓的悲观锁。

五、死锁

死锁也不是一种真正的锁,是指两个或两个以上的事务在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等的进程称为死锁进程。

遇到死锁可以执行如下的查询语句观察等待的事务:

-- 查看当前的事务
select * from information_schema.innodb_trx;
 
-- 查看当前锁定的事务
select * from information_schema.innodb_locks;
 
-- 查看当前等锁的事务
select * from information_schema.innodb_lock_waits;

-- 删除事务
kill 进程号;

-- 未提交事务自动回滚时间
set innodb_lock_wait_timeout = 10;

我的另一篇文章《MySQL锁机制》对部分锁进行了更加深入和全面的描述,感兴趣的可以去看下:

链接地址:https://blog.csdn.net/bt_xxx/article/details/89553991

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值