MySQL 中有哪些锁类型?
(一)按锁粒度划分
锁的粒度从大到小分为表锁、行锁,其中行锁又细分为记录锁、间隙锁、临键锁(Next-Key Lock),此外还有特殊的意向锁(辅助表锁与行锁的判断)。
表锁
锁定整个数据表,粒度最大,并发性能最差,分为表共享读锁(S 锁)和表排他写锁(X 锁)。
手动上锁 SQL:
表共享读锁:
LOCK TABLE xx READ;(其他事务可读,不可写)表排他写锁:
LOCK TABLE xx WRITE;(其他事务既不可读,也不可写)适用场景:无索引操作、全表扫描更新等,InnoDB 中极少主动使用,MyISAM 引擎的默认锁类型。
行锁
仅锁定数据表中指定的行记录,粒度最小,并发性能最优,仅 InnoDB 引擎支持(依赖索引实现,无索引时行锁退化为表锁)。
细分类型:
意向锁
辅助表锁的一种 “标记性锁”,用于快速判断数据表内是否存在行锁,避免全表遍历检查行锁,提升锁判断效率,仅 InnoDB 引擎支持。
细分类型:
意向共享锁(IS 锁):对表内某条记录上共享锁(S 锁)前,先为整个表上 IS 锁。
意向排他锁(IX 锁):对表内某条记录上排他锁(X 锁)前,先为整个表上 IX 锁。
核心规则:意向锁与表锁互斥,与其他意向锁兼容;仅用于锁判断,不直接阻塞业务操作。
(二)按锁模式划分
分为共享锁(S 锁,读锁)*和*排他锁(X 锁,写锁),是 MySQL 锁的基础模式。
共享锁(S 锁)
核心权限:允许持有锁的事务读取数据,不允许修改数据;允许多个事务同时持有同一资源的 S 锁(共享兼容)。
上锁 SQL:
SELECT ... FROM ... LOCK IN SHARE MODE;(行级共享锁);LOCK TABLE xx READ;(表级共享锁)。兼容性:与 S 锁兼容,与 X 锁互斥。
排他锁(X 锁)
核心权限:允许持有锁的事务读写数据,不允许其他事务持有该资源的任何锁(排他不兼容)。
上锁 SQL:
SELECT ... FROM ... FOR UPDATE;(行级排他锁);LOCK TABLE xx WRITE;(表级排他锁);DML 操作(INSERT/UPDATE/DELETE)会自动为涉及行上排他锁,无需手动指定。兼容性:与 S 锁、X 锁均互斥。
(三)特殊锁类型
元数据锁(MDL 锁)
自动触发,无需手动操作,用于保护数据表的元数据(表结构、字段定义等)一致性,防止并发 DDL 与 DML 操作冲突。
细分类型:
MDL 共享读锁:执行 SELECT、SHOW 等查询操作时自动获取,多个事务可同时持有;不阻塞其他事务的读操作,阻塞其他事务的 DDL 操作(ALTER/DROP TABLE 等)。
MDL 排他写锁:执行 ALTER、DROP、RENAME 等 DDL 操作时自动获取,仅允许一个事务持有;阻塞其他所有事务的读操作(SELECT)和写操作(DML/DDL)。
注意:长时间运行的查询会持有 MDL 读锁,导致后续 DDL 操作阻塞,需避免慢查询占用 MDL 锁。
Auto-Inc 锁
针对自增列(AUTO_INCREMENT)的特殊表级锁,用于保证自增序列的唯一性和连续性,仅 InnoDB 引擎支持。
锁释放机制:依赖
innodb_autoinc_lock_mode配置(MySQL 5.1 及以上版本支持),分为 3 种模式:模式 0:仅使用 Auto-Inc 锁,插入语句执行期间持有表锁,语句结束后释放,并发插入性能最差。
模式 1(默认):已知插入行数的操作(如 INSERT ... VALUES (),(),())使用轻量级互斥锁,插入完成即释放;未知插入行数的操作(如 INSERT ... SELECT)使用 Auto-Inc 锁,语句结束后释放,兼顾性能与自增序列一致性。
模式 2:所有插入操作均使用轻量级互斥锁,插入完成即释放,并发插入性能最优,但可能导致主从复制中自增序列不一致(仅适用于主从同步为 row 格式的场景)。
谓词锁
针对空间索引(如 GIS 数据的 SPATIAL 索引)的特殊锁类型,用于锁定空间数据的查询范围,防止并发修改导致数据不一致,仅 InnoDB 引擎支持空间索引时生效,日常业务中极少使用。
MySQL 事务的二阶段提交是什么?
(一)核心定义
MySQL 事务的二阶段提交是基于 Redo Log(重做日志) 和 Binlog(二进制日志) 的一致性保障机制,主要用于解决主从复制场景下的数据一致性问题,确保 Redo Log 与 Binlog 日志内容完全同步,避免单机崩溃或主从同步异常导致数据丢失 / 不一致。
(二)完整执行流程
二阶段提交将事务的提交过程分为 准备阶段(Prepare Phase) 和 提交阶段(Commit Phase),具体步骤如下:
事务执行阶段:执行 DML 操作(INSERT/UPDATE/DELETE),InnoDB 引擎将数据变更写入内存缓冲区(Buffer Pool),同时生成对应的 Redo Log 日志(仅写入 Redo Log Buffer,未刷盘)。
准备阶段(Prepare):
InnoDB 引擎将 Redo Log Buffer 中的日志刷写到磁盘(Redo Log 文件),并将 Redo Log 的事务状态标记为
prepare(准备完成)。此时事务并未真正提交,仅保证数据变更已持久化到 Redo Log,即使后续崩溃,也可通过 Redo Log 恢复数据。
提交阶段(Commit):
MySQL 上层(Server 层)将事务的变更记录写入 Binlog 日志,并将 Binlog 刷写到磁盘。
InnoDB 引擎读取 Binlog 中的事务 XID(全局唯一事务 ID),确认 Binlog 写入完成后,将 Redo Log 的事务状态从
prepare修改为commit(提交完成)。释放事务持有的所有锁资源,事务提交完成。
(三)崩溃恢复机制
当事务执行过程中(介于准备阶段与提交阶段之间)发生数据库崩溃,重启后 InnoDB 引擎会通过以下逻辑进行一致性恢复:
扫描磁盘上的 Redo Log 文件,筛选出状态为
prepare的事务(未完成提交的事务)。提取该事务的 XID,去 Binlog 日志中查询是否存在该 XID:
若 Binlog 中存在该 XID:说明 Binlog 已成功写入,重启后将 Redo Log 中该事务的状态修改为
commit,完成事务提交,保证数据一致性。若 Binlog 中不存在该 XID:说明 Binlog 未写入完成,重启后回滚该事务,丢弃 Redo Log 中的数据变更,避免数据不一致。
(四)优化:日志组提交
为提升并发性能,MySQL 对 Redo Log 和 Binlog 均实现了 组提交(Group Commit) 优化:
原理:将多个并发事务的日志写入操作批量合并,一次刷盘操作处理多个事务的日志,减少磁盘 I/O 次数(磁盘 I/O 是数据库性能瓶颈之一)。
效果:大幅提升高并发场景下的事务提交效率,降低日志刷盘对数据库性能的影响。
MySQL 中如果发生死锁应该如何解决?
死锁是指两个或多个事务互相持有对方需要的锁资源,且互相等待对方释放锁,导致所有事务都无法继续执行的一种阻塞状态(仅发生在持有排他锁的场景下)。InnoDB 引擎提供了自动处理机制,同时也支持手动干预解决。
(一)自动处理机制
死锁检测机制(默认开启)
依赖配置
innodb_deadlock_detect = ON(MySQL 5.7 及以上版本默认开启),InnoDB 引擎会实时检测事务之间的锁依赖关系,当发现循环依赖(死锁)时,会自动选择回滚代价最小的事务(通常是持有锁资源最少、执行步骤最少、影响数据量最小的事务),释放该事务持有的锁资源,打破死锁循环,让其他事务继续执行。优势:处理速度快,无需人工干预,适合大部分常规场景。
局限:高并发场景下,死锁检测会消耗大量 CPU 资源,可能影响数据库性能。
锁等待超时机制
依赖配置
innodb_lock_timeout = 50(默认 50 秒,单位:秒),当某个事务等待锁资源超过该超时时间时,InnoDB 引擎会自动回滚该事务,释放持有的锁资源。优势:作为死锁检测机制的补充,处理一些死锁检测未覆盖的阻塞场景。
局限:超时时间固定(可手动调整),等待超时期间事务无法执行,影响业务响应速度;可能误回滚正常的长事务。
(二)手动干预方案
当自动处理机制无法快速解决死锁(如高并发场景、死锁频繁发生),或需要快速恢复业务时,可通过手动干预解决,步骤如下:
查询死锁信息
方法 1:执行
SHOW ENGINE INNODB STATUS;,查看输出结果中的「LATEST DETECTED DEADLOCK」部分,获取死锁事务的 ID、持有锁、等待锁、执行 SQL 等关键信息。方法 2:查询 INFORMATION_SCHEMA 系统表,获取阻塞事务与锁资源信息:
查询锁等待情况:
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;查询当前持有锁:
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;查询当前事务:
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
终止死锁事务
根据上述查询结果,找到死锁循环中的关键事务(通常是回滚代价较小的事务),执行
KILL [事务ID];命令终止该事务,释放锁资源,打破死锁循环。注意:KILL 命令会强制回滚该事务,需确认该事务未执行关键业务操作,避免数据丢失或业务异常。
后续优化(避免死锁再次发生)
统一事务操作顺序:所有事务对多个表 / 行的操作遵循相同的顺序,避免循环依赖。
缩小事务粒度:尽量缩短事务执行时间,减少事务持有锁的时长,快速释放锁资源。
合理创建索引:避免无索引导致行锁退化为表锁,减少锁冲突概率。
调整隔离级别:将事务隔离级别从可重复读(RR)调整为读已提交(RC),减少间隙锁与临键锁的使用,降低死锁概率(需接受可能出现的幻读)。
关闭死锁检测(高并发场景):若死锁极少发生,可执行
SET GLOBAL innodb_deadlock_detect = OFF;关闭死锁检测,依赖锁等待超时机制处理,减少 CPU 消耗(需谨慎使用)。