数据库-锁
一、排他锁
排他锁(Exclusive Lock),简称 X锁,又称为写锁或独占锁。X锁分为表级排他锁和行级排他锁。
如果事务 T1 对 数据行对象 A 加上了行级排他锁,那么T1 可以对 A 进行读取和更新操作; 其他事务对 A 只能进行读取操作,不能做更新操作,并且其他事务不能再对 A 加任何类型的锁, 直到 T1 释放了行级排他锁。
MySQL 的 InnoDB 引擎默认的修改语句 update、delete、insert 会自动给涉及到的数据加上行级排他锁, 而 select 语句默认不会加任何锁,若查询的时候加行级排他锁可以使用 select ... for update 语句, 加行级共享锁可以使用 select ... lock in share mode 语句。所以加过行级排他锁的数据,其他事务就 不能通过 for update 和 lock in share mode 方式查询数据,但可以直接通过 select ... from ... 查询数据,因为普通查询没有加任何锁。
如果 T1 对某张表加了表级排他锁,则 T1 可以对该表的所有记录进行查找和修改,而其他事务对该表只能 查询不能修改,并且其他事务不能再往这张表加任何类型的表级锁,也不能给表中的数据行加任何的行级锁。
二、共享锁
共享锁(Shared Lock),简称 S锁,又称读锁。S锁发为表级共享锁和行级共享锁。
S锁就是多个事务对于同一数据可以共享一把锁,都可以访问到数据,但是只能读不能修改,即S锁不阻塞其他 事务的读操作,但阻塞写操作。
当事务 T1 为数据行对象 A 加上行共享锁后,T1 可以对 A 进行读操作,但不能进行写操作,并且事务 T2 可以再次对 A 加行级共享锁,但是不能加行级排他锁,也不能加表级排他锁。大家都可以正常的读取A,在 A 上所有共享锁释放之前,任何事务不能对A进行写操作。 数据行对象 A 可以共存多个行级共享锁,这被称为行级共享锁兼容。加了行级共享锁的A 不能再加行级排 他锁,所以行级排他锁和行级共享锁是不兼容的。
当事务 T1 为某张表添加了表级共享锁,T1 可以查看表中的所有记录,但是不能修改,而且其他事务也 只能查看数据不能修改数据。但是其他事务可以再往这张表添加表级共享锁和意向共享锁,其他事务也可以 往这张表的记录添加行级共享锁;但是其他事务不能再往这张表添加表级的排他锁,也不能添加意向排他锁, 其他事务也不能往这张表的记录添加行级排他锁。
三、更新锁
更新锁(Update Lock),简称 U锁。更新锁只有行级的更新锁。
当事务 T1 对数据行对象 A 加上更新锁后,表示 A 在稍后被更新,更新锁允许其他事务在 T1 操作更新 之前读取 A,但不可以修改。其他事务修改 A 之前会先往数据行对象 A 加行级排他锁,但是发现A 已存在U锁, 所以加行级排他锁失败,只能等待 U 锁释放。
更新锁与行级共享锁兼容;更新锁与表级共享锁不兼容;更新锁与更新锁不兼容;更新锁与行级排他锁不兼容, 更新锁与表级排他锁不兼容。
因此数据行对象 A 不可以再添加更新锁,但是可以添加行级共享锁,但是添加行级共享锁的意义不大, 因为 T1 找到需要更新的数据时,更新锁直接转为行级排他锁,开始更新数据,不需要等待其他事务释放行 级共享锁,所以在有更新锁的数据资源上加行级共享锁毫无意义。
四、意向锁
在数据行对象上加 U(更新锁)、X(排他锁)、S(共享锁)之前都会为表或页加意向锁。意向锁是一种表级锁, 表示"某个事务对表中的记录加了某种锁或准备加某种锁"。
常用的意向锁有三种:
1、意向共享锁(Intent Share Lock),简称IS锁 事务 T1 在给数据行对象添加S锁前,要先获得IS锁。
如果表被添加了IS锁,说明某个事务对这个表中的 某些数据加了行级S锁。当其他事务想要在这个表加一个表级排他锁时,发现这个表已经加了IS锁,那么就不可以 再加表级排他锁了。
2、意向排他锁(Intent Exclusive Lock),简称IX锁 事务在请求行级X锁时,要先获得IX锁。
事务 T1 修改 user 表的数据行对象A,会给数据行对象A 上一把 行级的排他锁,但是在给数据行对象 A 上行级排他锁之前会先给 user 表上一把意向排他锁,这时事务 T2 要给 user 表上一个表级的排他锁就会被阻塞。
3、共享意向排他锁(Share Intent Exclusive Lock)
是一种组合锁,它结合了共享锁(S)和排他锁(X)的特性。它是一种意向锁, 意味着它覆盖了多个可能的目标对象,这些对象可以是表、索引、数据行等。
共享锁部分(S锁):SIEX Lock的共享锁部分允许并发事务读取数据库对象,而不阻塞其他事务的读取操作。 这意味着多个事务可以同时持有共享锁,并且彼此之间不会互斥。
排他锁部分(X锁):SIEX Lock的排他锁部分禁止其他事务获取该数据库对象的任何锁, 包括共享锁和排他锁。这确保了当前持有SIEX Lock的事务在执行期间不会被其他事务干扰。 需要注意的是,SIEX Lock是数据库管理系统(DBMS)内部使用的锁机制,开发人员通常不需要直接与它进行交互。 在实际应用中,开发人员通常通过使用数据库的事务、锁定语句和相关API来管理并发访问和锁定。
五、锁的粒度
锁的粒度指的是锁生效的范围,即行锁、页锁、表锁等。锁的粒度一般由数据库自主管理,不同的事务 隔离级别,数据库会有不同的加锁策略。
六、数据库自动加锁
锁在大多数情况下都是数据库自动加的,数据库会根据隔离级别的不同,按照策略来加锁。
比如:
update user set name = 'ydy';
通过性能分析工具 Profiler 跟踪 sql 发现,数据库系统会逐行先获取 U 锁, 然后转为 X 锁,对数据行进行更新,更新完后并不释放 X 锁,接着继续获取下一行的 U 锁, 转为 X 锁,更新数据…,不断重复以上流程,直到全部数据行更新完成后,再逐行释放掉所有的 X 锁。
如果加上 where条件:
update user set name = 'ydy' where age = 20;
如果字段 age 没有索引,则逐行获取 U 锁,如果符合条件,转为 X 锁,执行更新,不释放 X 锁; 如果不符合条件,释放 U 锁。
如果有索引的话,则不需要全表扫描逐行处理,而是直接定位到要更新的数据行,然后逐行加上 X 锁, 接着执行更新,更新后不释放 X 锁,直到所有定位到的数据行更新完成后,再逐行释放 X 锁。
七、手动加锁
手动加行级排他锁:
begin; -- 开始事务
select * from user where id = 5 for update; -- 查询到的数据行全部加上行级的排他锁
手动加行级共享锁:
begin;
select * from user where id = 5 lock in share mode;
手动给表加表级的排他锁:
lock tables user write; -- 给表account加排他锁,即写锁
对表 user 加上表级排他锁后,其它会话不能对该表 user 进行查询和修改操作
会话 S1 对表 user 加上任何表级锁后,该会话不能对其它非锁表进行任何的操作。
手动给表加表级的共享锁:
lock tables user read; -- 给表account 加共享锁,即读锁
给多个表加表级锁:
lock table user write,user write;
手动解锁:
unlock tables; 各种锁之间的兼容问题:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南