千家信息网

什么是MySQL锁机制

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,本篇内容主要讲解"什么是MySQL锁机制",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"什么是MySQL锁机制"吧!无论什么时候,只要存在多个连接在同一时刻
千家信息网最后更新 2026年11月01日什么是MySQL锁机制

本篇内容主要讲解"什么是MySQL锁机制",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"什么是MySQL锁机制"吧!

无论什么时候,只要存在多个连接在同一时刻修改数据,都会涉及到并发控制的问题。MySQL实现了两个层面的并发控制:服务层和引擎层。

锁分类

按照使用场景分类

共享锁:共享锁(shared lock)也称为读锁(read lock)。共享锁是共享的,或者说是相互不阻塞的。多个连接在同一时刻可以同时读取同一个资源,而不相互干扰。

排他锁:排他锁(exclusive lock)也称为写锁(write lock)。写锁是排他的,也就是一个写锁会阻塞其他的写锁和读锁。

按照加锁思想分类

悲观锁:对数据被外界修改保持悲观的态度,在整个数据处理过程中,数据都处于锁定状态。

乐观锁:它认为数据一般情况下不会造成冲突,在数据更新的时候才会对数据的冲突与否进行校验。

按照锁粒度分类

全局锁:对整个数据库实例加锁,它将整个数据库实例处于只读的状态。

表级锁:对整个表进行加锁的方式。MySQL表级锁分为表锁和元数据锁。

行级锁:行锁可以最大程度的支持并发处理,行锁是存储引擎层实现的,而MySQL服务层并没有实现行锁。InnoDB存储引擎行级锁类型:Record Lock、Gap Lock、Next-key Lock。

读写锁

读锁

共享锁:共享锁(shared lock)也称为读锁(read lock)。共享锁是共享的,或者说是相互不阻塞的。多个连接在同一时刻可以同时读取同一个资源,而不相互干扰。

加锁命令

select ...... lock in share mode;

测试

测试时,设置事务手动提交:set autocommit = 0,后续如果没有明确的提示,autocommit都是0。

测试时,大家开启两个窗口,建立两个连接,窗口1和窗口2分别对应事务A和事务B。

  • 窗口1:查询id=6的行数据并添加读锁,正确返回数据。

  • 窗口2:依然查询id=6的行数据并添加读锁,正确返回数据。读读不冲突。

  • 窗口1:对id=6的行执行写操作(update语句),在窗口2的事务提交之前,写操作阻塞,并可能会超时退出。

如果写锁等待时间过长,则会超时退出。

窗口1 mysql> update user set age = 20 where id =6; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

如果在窗口1中的事务在执行写操作等待期间,窗口2的事务也执行同一行数据的写操作,则会导致死锁错误。

窗口2 mysql> update user set age = 30 where id =6; ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

写锁

排他锁(exclusive lock)也称为写锁(write lock)。写锁是排他的,也就是一个写锁会阻塞其他的写锁和读锁。

加锁命令

select ...... for update;

测试

  • 窗口1:查询id=6的行数据并添加写锁,正确返回数据。

  • 窗口2:依然查询id=6的行数据并添加写锁,阻塞。写写冲突。

  • 窗口1:对id=6的行执行写操作(update语句),写操作未阻塞。

  • 窗口1事务提交之后,窗口2的查询语句返回结果。

悲观锁和乐观锁

不论是乐观锁还是悲观锁都是人们定义的一种概念,并不是一种锁实现,它是一种思想。乐观锁比较适用于读多写少的场景,悲观锁适用于写多读少的场景。

悲观锁

当我们对数据库的某一条数据进行修改操作时,为了避免同时有其他人对同一行数据进行修改,通过对数据进行加锁的方式以防止并发问题。这种借助了数据库的锁机制,在修改数据之前先锁定再修改的方式称为悲观锁(Pessimistic Lock)。

悲观锁具有强烈的独占性和排他性,在整个数据的写操作过程,都将数据处于锁定状态。悲观锁的实现往往需要数据库提供的锁机制。

悲观锁的实现:

  • 数据库的锁机制如行锁、表锁、读锁和写锁都是在操作之前先加锁的操作,都属于悲观锁。

  • Java中学习的synchronized关键字也是悲观锁。

乐观锁

乐观锁是相对于悲观锁的概念,乐观锁是假设数据一般情况下都不会存在并发冲突,在数据进行更新的时候才会对数据的冲突与否进行验证。如果存在冲突,则告诉用户结果,由用户决定下一步该怎么做。

乐观锁是一种宽松的加锁方式,它不需要使用数据库本身的锁机制。

乐观锁的实现:

  • MVCC,数据库多版本控制利用版本号控制数据更新的并发问题,是乐观锁的实现。

  • CAS,比较并交换是Java中乐观锁的实现。

全局锁

全局锁:对整个数据库实例加锁,它将整个数据库实例处于只读的状态。

加锁命令 (FTWRL)

Flush tables with read lock;

应用场景

全局锁常用来对整个数据库实例进行逻辑备份。

全局锁加锁期间,业务的数据更新操作(DML)和 表结构的修改操作(DDL)都是会被锁住的。

此时你是不是有个疑问:开发中备份都是直接使用mysqldump,什么时候使用FTWRL呢?

官方自带的逻辑备份工具mysqldump 使用参数-single -transaction的时候,在导出数据的时候就会启动一个事务,来确保拿到一致性视图,在学习事务隔离的时候我们了解到,基于MVCC的一致性视图,这个过程中的数据是可以正常更新的。但是我们要知道事务是引擎层的实现,并不是每个存储引擎都支持事务。我们在开发中大部分的备份使用的是mysqldump,主要是因为我们的存储引擎大部分情况都是使用的默认的引擎InnoDB。

表级锁

表级锁:即是对整张表进行加锁。表的定义包含两个部分:数据和结构,所以表级锁也分为两类:表锁和元空间锁。

表锁

表锁是MySQL中最基本的锁策略,并且也是开销最小的策略。表锁会锁住整张表,在对表进行写操作(插入、删除、更新等)前,需要先获取写锁,它会阻塞其他用户对该表的所有读写操作。只有没有写锁时,其他读取的用户才能获取读锁。读锁之前互相不会造成阻塞。

写锁的优先级高于读锁,因此一个写锁的请求可能会被插入到读锁队列前面,但是读锁是不能插入到写锁的前面的。

加解锁命令

-- 对表加读锁 lock tables ...... read; -- 对表加写锁 lock tables ...... write; -- 释放锁 unlock tables;

测试

表锁读锁测试

  • 窗口1:对user表加读锁。

  • 窗口2:对user表全表读取,正常返回全表数据。

  • 窗口2:修改user表中id=6的数据,阻塞。读写冲突。

  • 窗口1:修改user表中id=6的数据,报错。

  • 窗口1:释放读锁,窗口2的更新数据执行成功。

表锁写锁测试

  • 窗口1:对user表添加写锁。

  • 窗口2:全表查询user表,阻塞。

  • 窗口1:更新user表中的id=6的数据,更新成功。

  • 窗口1:释放锁,窗口2的全表查询返回最新数据。

元空间锁

MySQL5.5版本中引入了元空间锁(matadata lock),当对一个表做增删改查操作的时候,会添加MDL读锁。当对表结构变更操作的时候,会添加MDL写锁。MDL的作用是防止DDL和DML并发的冲突。

MDL锁是系统默认添加的,不需要显式的添加。

测试

  • 窗口1:查询表中的一条数据,这个时候在执行查询语句,会添加MDL读锁。

  • 窗口2:添加字段,此时执行的是alter语句,会添加MDL写锁。这个时候窗口1的读锁没有释放,所以alter语句会阻塞。

  • 窗口3:查询表中的一条数据,由于窗口2导致的阻塞,在窗口3申请MDL读锁的时候也会造成阻塞。

  • 窗口1:提交事务,窗口3获取了MDL读锁,返回查询结果。

  • 窗口3:提交事务释放了读锁,窗口2获取写锁,添加字段成功。

行级锁

MySQL的行锁是在引擎层由各个存储引擎实现的。并不是所有的存储引擎都支持行锁,比如MyISAM引擎是不支持行锁的。不支持行锁意味着并发控制的时候只能使用表锁,这也意味着同一个时刻同一个表只有一个更新执行,严重影响了并发。InnoDB是支持行锁的,这也是InnoDB能替代MyISM的重要原因之一。

两阶段锁协议

在InnoDB事务,行锁是需要的时候加上的,但并不是不需要了就立刻释放的,而是需要等到事务结束后才会释放锁。这个就是两阶段锁协议。

InnoDB是采用的两阶段锁协议。在事务执行的过程中,随时都可以锁定,锁只有在执行commit或者rollback的时候才会释放,并且所有的锁是在同一个时刻释放的。

事务A执行了两条update语句之后,事务B也执行update语句,但是事务B阻塞直到事务A提交事务。

行锁

我们创建一张简单表t,其中id为主键,a为索引,插入6条数据。

CREATE TABLE `t1` (   `id` int(11) NOT NULL,   `a` int(11) DEFAULT NULL,   `b` int(11) DEFAULT NULL,   PRIMARY KEY (`id`),   KEY `idx` (`a`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into t1 values(0,0,0),(5,5,5),(10,10,10),(15,15,15),(20,20,20),(25,25,25);

间隙锁(Gap Lock)

间隙锁,锁的是两个值的间隙。

对于表t1,6条数据就产生了7个间隙,如下图所示:

我们看前面学习的写锁的例子:

begin; select * from t1 where b = 5 for update; commit;
  • 加了写锁的select查询是当前读,读取的是最新的数据值。

  • b字段不是表的索引字段,它会扫描全表将满足条件的行都加上写锁,也会给满足条件的行两边的间隙加上了锁。

间隙锁之间并不会冲突,与间隙锁存在冲突关系的是往这个间隙中插入数据的操作。

next-key lock

行锁和间隙锁的合称为next-key lock每个next-key lock都是一个前开后闭的区间。这里不要混淆了,间隙锁是一个开区间,间隙锁加上行锁生成的next-key lock是一个前开后闭的区间。

间隙锁和next-key lock的引入帮助我们解决了幻读的问题。

间隙锁是可重复隔离级别下才生效的,如果我们把隔离级别设置为读提交,那就没有间隙锁了。

测试

前面我们在学习其他锁的时候,为了省事把autocommit设置为了0,现在我们需要把autocommit设置为1;

  • 窗口1:使用显式事务,通过for update加写锁,对满足条件的行和间隙加锁。

  • 窗口2:更新id为0的行,此行没有加锁更新成功。

  • 窗口3:插入数据,满足了(0,5]的next-key lock,阻塞等待。

总结

锁按照锁粒度分为:全局锁、表锁和行锁。

行锁是引擎层的实现,我们文中描述的行锁都是基于InnoDB存储引擎的实现。

InnoDB的行锁采用的两阶段锁协议,锁在需要的时候添加,只有在事务commit或者rollback时才会一次性释放所有锁。

可重复度隔离级别下存在间隙锁,如果设置为其他的隔离级别下就没有间隙锁了。间隙锁即是对行数据的两边间隙进行加锁,间隙锁加上行锁合称为next-key lock,它是一个前开后闭的区间。间隙锁解决了幻读的问题。

大家在学习锁的时候还需多动手实践。

到此,相信大家对"什么是MySQL锁机制"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

数据 事务 间隙 时候 阻塞 悲观 引擎 查询 更新 乐观 数据库 冲突 测试 语句 机制 存储 全局 学习 支持 两个 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 上海睿民互联网科技有限公司 中资互联网科技 期待互联网科技 极限挑战互联网科技 电子科技大学互联网 友谊互联网络科技 苏州大垚互联网科技 上海卓越科技 互联网 互联网科技公司名称 互联网科技公司需要的职位 金融科技与互联网金融相同和不同 岳阳华臻互联网科技有限公司 上海创道互联网络科技有限公司 宁波跨境商互联网科技有限公司 珠海东科互联网科技有限公司 陕西九四连强互联网科技有限公司 科技和互联网行业的特点 互联网科技创业计划的风险分析 广东猫王互联网科技有限公司电话 六子台科技互联网展会 中智农互联网科技 百单网互联网科技有限公司怎么样 华服互联网科技怎么样 互联网科技公司经营范围 喔噻互联网科技 远信互联网科技 互联网科技公司招聘 世界互联网科技公司市值排名 广州新华互联网科技学校电竞方面 风投北京互联网科技
0