2026 MySQL 面试 100 题: 索引 / 事务 / 锁(答案 + 原理)
前言:2026年MySQL面试彻底"去理论化",面试官不再问"什么是索引""什么是事务"这种基础概念,而是聚焦「原理落地+生产避坑+问题排查」------ 核心考察索引优化、事务异常处理、锁冲突解决,这三大模块占面试分值的70%。
本文整理100道高频真题,全部来自2026年大厂(阿里、腾讯、字节、美团)面试现场,按「索引→事务→锁」分类,每道题遵循「通俗答案+极简原理+实操提示」,不堆冗余理论,不用死记硬背,看懂就能答,记住就能用,适合Java/Go后端、运维、DBA、应届生突击复习,也可作为生产排查参考。
重点提示:所有题目均标注「高频」「必考」「实操题」,优先掌握标注题型,面试稳拿80分+;答案均为面试官认可的"得分版",避免踩"理论正确但实操无效"的坑。
第一模块:MySQL 索引(35题 · 必考,面试重中之重)
核心考点:索引底层、索引失效、索引设计、实操优化,2026年新增「8.4版本索引新特性」相关考题,重点关注。
一、基础必考(10题,入门必答)
-
【必考】MySQL 索引的核心作用是什么?(通俗版,别讲理论)
答:快速定位数据,减少全表扫描,提升查询速度(比如千万级表,无索引查10秒,有索引查1毫秒);
极简原理:索引相当于书的目录,不用翻完整本书,通过目录快速找到目标内容;
实操提示:建表时,主键、外键、高频查询字段(where、order by、join)必须建索引。
-
【高频】MySQL 索引的底层数据结构是什么?为什么用这种结构?
答:InnoDB 引擎默认是 B+ 树(重点),MyISAM 也是 B+ 树(老引擎,现在很少用);
极简原理:B+ 树是"平衡多路搜索树",层数少(3-4层就能支撑千万级数据),查询速度稳定,且叶子节点有序、存完整数据(InnoDB),适合范围查询;
实操提示:面试别说"B树",要明确说"B+树",区别是B树叶子节点不存完整数据,不适合范围查询。
-
【必考】聚簇索引和非聚簇索引的区别?(别堆定义,说实操)
答:核心区别:聚簇索引"索引和数据存在一起",非聚簇索引"索引和数据分开";
具体区别(通俗版):
-
聚簇索引:主键索引就是聚簇索引,叶子节点存的是整行数据,查索引直接拿数据;
-
非聚簇索引(二级索引):叶子节点存的是主键值,查完索引还要回表查主键(回表查询);
实操提示:建表必须设主键(聚簇索引),否则InnoDB会自动生成隐藏主键,影响性能。
-
-
【高频】什么是回表查询?怎么避免?
答:回表查询:查非聚簇索引时,叶子节点只拿到主键,还要再查聚簇索引才能拿到完整数据,多一次查询;
极简原理:非聚簇索引不存完整数据,必须通过主键回表;
实操提示:避免方法------用覆盖索引(查询字段都在索引里,不用回表),比如 select id,name from user where name=?,给name建联合索引(name,id)。
-
【必考】MySQL 支持哪些索引类型?实际工作中常用哪几种?
答:支持的类型:主键索引、唯一索引、普通索引、联合索引、全文索引;
常用的3种(实操重点):
-
主键索引:唯一标识一行数据,不可为null,一张表只能有1个;
-
唯一索引:字段值唯一(可null),比如用户手机号、邮箱;
-
联合索引:多个字段组合建索引,比如(user_id, order_time),遵循最左匹配原则;
实操提示:全文索引很少用(MySQL全文索引效果差),建议用Elasticsearch替代。
-
-
【高频】唯一索引和主键索引的区别?
答:3个核心区别(通俗好记):
-
主键索引不可为null,唯一索引可为null(最多1个null);
-
一张表只能有1个主键索引,可多个唯一索引;
-
主键索引默认是聚簇索引,唯一索引默认是非聚簇索引;
实操提示:手机号字段适合建唯一索引(可null,避免重复),不适合建主键。
-
-
【必考】什么是联合索引的最左匹配原则?举个实操例子
答:最左匹配原则:联合索引(a,b,c),查询时必须从最左边的字段开始匹配,否则索引失效;
实操例子(必记):
-
有效:where a=?、where a=? and b=?、where a=? and b=? and c=?;
-
失效:where b=?、where c=?、where a=? and c=?(跳过b);
实操提示:建联合索引时,把高频查询字段放左边,比如高频查a和b,就建(a,b),不建(b,a)。
-
-
【高频】索引建得越多越好吗?为什么?
答:不是,索引越多越慢;
极简原理:索引会占用磁盘空间,且增删改(insert/update/delete)时,会同步维护索引,索引越多,维护成本越高,写入速度越慢;
实操提示:一张表索引不超过5个,高频查询字段建索引,低频查询字段不建。
-
【必考】什么时候不适合建索引?
答:3种场景(实操重点):
-
数据量少的表(比如少于1万行):全表扫描比查索引还快;
-
高频增删改的表(比如日志表):索引维护成本太高,影响写入速度;
-
重复值多的字段(比如性别、状态,只有0/1/2):索引选择性太低,查索引和全表扫描差不多;
实操提示:性别字段绝对不建索引,状态字段(比如订单状态)如果值少,也不建。
-
-
【高频】主键索引为什么建议用自增ID?不用UUID?
答:核心原因:自增ID能让B+树有序插入,减少页分裂,提升性能;
对比(通俗版):
-
自增ID:1、2、3、4...,插入时直接往B+树末尾加,不破坏结构,无页分裂;
-
UUID:随机字符串,插入时会随机插入到B+树中间,导致页分裂,占用更多磁盘空间,查询变慢;
实操提示:生产环境主键优先用自增ID(int/bigint),UUID仅用于分布式场景(需全局唯一)。
-
二、索引失效场景(12题,面试高频,生产必避坑)
-
【必考】哪些情况会导致索引失效?(至少说6种,带实操例子)
答:6种高频失效场景(必记,结合例子):
-
索引字段用函数(比如 where SUBSTR(name,1,3)='张三');
-
索引字段用运算(比如 where age+1=20);
-
索引字段用模糊查询(%开头,比如 where name like '%张三');
-
联合索引不满足最左匹配(比如索引(a,b),where b=?);
-
索引字段为字符串,查询时不加引号(比如 where phone=13800138000,phone是varchar);
-
where条件用or,其中一个字段无索引(比如 where a=? or b=?,b无索引);
实操提示:排查索引失效,用 explain 查看执行计划,type=ALL就是全表扫描(索引失效)。
-
-
【高频】模糊查询 like '%xxx%' 会索引失效,怎么优化?
答:3种实操优化方法(按优先级排序):
-
用覆盖索引:如果只查主键和模糊匹配字段,建联合索引(name,id),即使like '%xxx%',也会走索引(避免回表);
-
用Elasticsearch:复杂模糊查询(比如全文检索),直接用ES替代MySQL;
-
避免前缀%:如果业务允许,用 like 'xxx%'(后缀%),会走索引;
实操提示:生产中,模糊查询优先用ES,MySQL只处理简单后缀模糊查询。
-
-
【必考】or 条件导致索引失效,怎么优化?
答:2种实操方法:
-
给or两边的字段都建索引:比如 where a=? or b=?,给a和b分别建索引,MySQL会走索引合并;
-
用union替代or:select * from user where a=? union select * from user where b=?(注意union去重,union all不去重);
实操提示:优先用union,索引合并的性能不如union稳定。
-
-
【高频】索引字段用函数失效,怎么优化?
答:核心思路:把函数移到等号右边,让索引字段"裸奔";
例子:
-
失效:where SUBSTR(name,1,3)='张三';
-
优化:where name like '张三%'(如果业务允许);
-
若必须用函数:建函数索引(比如 create index idx_name_sub on user(SUBSTR(name,1,3)));
实操提示:函数索引维护成本高,尽量不用,优先优化SQL写法。
-
-
【必考】字符串字段不加引号,为什么索引失效?
答:极简原理:MySQL会自动做"类型转换",把数字转换成字符串,相当于给索引字段用了函数,导致索引失效;
例子:
-
字段phone是varchar类型,索引失效:where phone=13800138000;
-
索引有效:where phone='13800138000';
实操提示:写SQL时,字符串字段必须加引号,避免类型转换。
-
-
【高频】null 值会导致索引失效吗?为什么?
答:分情况(实操重点):
-
普通索引:允许null值,查询 where 字段 is null 会走索引;
-
唯一索引:允许1个null值,查询 where 字段 is null 也会走索引;
-
误区:很多人说"null会导致索引失效",其实是错误的,只有用 is not null 时,可能失效(取决于数据分布);
实操提示:尽量避免字段为null,用默认值(比如''、0)替代,减少null相关的查询问题。
-
-
【必考】order by 字段为什么会导致索引失效?怎么优化?
答:失效场景:order by 的字段不在索引里,或联合索引不满足最左匹配,导致 filesort(文件排序),索引失效;
优化方法(实操):
-
把 order by 字段加入联合索引,比如 where a=? order by b,建索引(a,b);
-
避免 order by 字段用函数、运算;
实操提示:用 explain 查看执行计划,Extra 里有 filesort,就是排序未走索引,需要优化。
-
-
【高频】join 关联查询,哪些情况会导致索引失效?
答:2种高频场景:
-
关联字段(on 后面的字段)未建索引:比如 a join b on a.id = b.a_id,b.a_id 未建索引,导致全表扫描;
- 关联字段类型不匹配:比如 a.id 是 int,b.a_id 是 varchar,导致类型转换,索引失效;
实操提示:join 关联时,必须给被关联表的关联字段建索引,且字段类型一致。
-
【必考】limit 分页为什么会导致索引失效?怎么优化?
答:失效场景:limit 偏移量太大(比如 limit 10000, 10),MySQL会扫描前10010行,丢弃前10000行,索引失效,查询变慢;
优化方法(实操,优先用第一种):
-
用主键过滤:select * from user where id > 10000 limit 10(id是主键,走索引);
-
用覆盖索引:select id from user limit 10000, 10,再用id关联查其他字段;
实操提示:生产中,分页偏移量超过1000,必须用主键过滤优化。
-
-
【高频】MySQL 8.4 版本,索引有哪些新特性?(2026新增考题)
答:3个核心新特性(实操重点,面试官必问):
-
支持"部分索引":只给字段的前N个字符建索引(比如 varchar(100),只建前20个字符的索引),节省磁盘空间;
-
联合索引支持"跳过扫描":联合索引(a,b,c),即使where条件没有a,只要有b和c,也能走索引(8.0不支持);
-
-
索引碎片自动清理:InnoDB 自动合并索引碎片,不用手动执行 optimize table;
实操提示:面试时提一句8.4新特性,能加分,体现你关注版本更新。
-
【必考】怎么判断索引是否生效?(实操步骤)
答:用 explain 执行计划,看3个关键指标:
-
type:取值从好到坏:system > const > eq_ref > ref > range > ALL(ALL就是全表扫描,索引失效);
-
key:显示实际使用的索引,如果为NULL,说明索引失效;
-
-
Extra:如果有"Using index",说明走了覆盖索引;有"filesort""Using temporary",说明索引失效,需要优化;
实操提示:面试时,能说出这3个指标,就是加分项,体现你会实操排查。
-
【高频】索引碎片怎么产生的?怎么清理?
答:产生原因:频繁增删改数据,导致索引页有空洞,碎片增多,查询变慢;
清理方法(实操):
-
MySQL 8.4 及以上:自动清理,无需手动操作;
-
MySQL 8.0 及以下:执行 optimize table 表名(注意:会锁表,建议半夜执行);
实操提示:生产中,碎片率超过30%,再清理,频繁清理会影响性能。
-
三、索引设计与实操优化(13题,生产实战+面试加分)
-
【必考】实战题:一张千万级订单表(order),字段有 id(主键)、user_id、order_time、amount、status,高频查询场景:根据 user_id 查询近3个月的订单,怎么设计索引?
答:最优索引设计:联合索引(user_id, order_time);
理由:
-
高频查询字段 user_id 放左边,满足最左匹配;
-
order_time 放右边,支持范围查询(近3个月),且能避免 filesort(order by order_time 时走索引);
实操提示:如果还需要查 amount、status,可建联合索引(user_id, order_time, amount, status),实现覆盖索引,不用回表。
-
-
【高频】实战题:用户表(user),字段有 id(主键)、phone(唯一)、name、age、gender,高频查询:根据 phone 查用户信息,怎么设计索引?
答:给 phone 建唯一索引(create unique index idx_user_phone on user(phone));
理由:phone 是唯一字段,唯一索引能保证数据不重复,且查询速度比普通索引快(唯一索引查询时,找到匹配值就停止,普通索引要找完所有匹配值);
实操提示:phone 是字符串类型,建索引时不用指定长度(MySQL会自动适配),避免前缀索引导致唯一约束失效。
-
【必考】什么是前缀索引?什么时候用?
答:前缀索引:只给字符串字段的前N个字符建索引,节省磁盘空间;
适用场景:字符串字段很长(比如 varchar(255)),前N个字符的选择性足够高(重复值少),比如身份证号(前10位就能区分大部分数据);
实操例子:create index idx_idcard on user(idcard(10));
注意:前缀索引不能用于 order by、group by,也不能用于覆盖索引。
-
【高频】怎么选择索引的字段顺序?(联合索引设计核心)
答:2个核心原则(实操必记):
-
高频查询字段放左边;
-
选择性高的字段放左边(选择性=不重复值数量/总数据量,选择性越高,索引效果越好);
例子:联合索引(user_id, order_time)比(order_time, user_id)好,因为 user_id 选择性比 order_time 高(一个用户有多个订单)。
-
-
【必考】什么是覆盖索引?怎么设计覆盖索引?
答:覆盖索引:查询的所有字段,都包含在索引里,不用回表查询,提升速度;
设计方法(实操):把查询用到的字段(where、select、order by)都加入联合索引;
例子:select user_id, order_time from order where user_id=? order by order_time,建索引(user_id, order_time),就是覆盖索引。
-
【高频】生产环境,怎么排查索引相关的性能问题?(实操步骤)
答:3步排查(面试必说,体现实操能力):
-
用 explain 分析慢SQL,看是否走索引、有无 filesort/Using temporary;
-
用 show status like 'Handler_read%':Handler_read_key 高(索引使用频繁),Handler_read_rnd_next 高(全表扫描多);
-
用 show index from 表名,查看索引使用情况,删除无用索引(未使用的索引);
实操提示:生产中,慢查询日志(slow_query_log)是排查索引问题的核心工具,要会开启和分析。
-
-
【必考】无用索引怎么识别?怎么处理?
答:识别方法(实操):
-
用 MySQL 自带的 sys.schema_unused_indexes 视图,查看未使用的索引;
-
开启慢查询日志,分析所有慢SQL,看哪些索引未被使用;
处理方法:
-
先备份索引(避免误删),再删除无用索引;
-
若暂时不能删除,可先禁用索引(alter table 表名 disable key 索引名);
实操提示:删除索引前,必须确认该索引确实未被使用,避免影响业务。
-
-
【高频】分库分表场景下,索引怎么设计?(大厂高频)
答:核心原则:索引跟着分库分表键走,避免跨库跨表查询;
实操例子:按 user_id 分库分表,索引设计:
-
每个分表的主键(id)用自增ID(或雪花ID);
-
联合索引必须包含分库分表键(user_id),比如(user_id, order_time);
-
避免建全局索引(跨库查询效率低),用分布式索引(比如 Elasticsearch)替代;
实操提示:分库分表的索引,不能脱离分库键,否则会导致全库扫描。
-
-
【必考】主键索引用 int 和 bigint 有什么区别?怎么选择?
答:区别:int 占4字节,最大存储21亿;bigint 占8字节,最大存储922亿;
选择方法(实操):
-
小表(数据量少于1亿):用 int,节省磁盘空间;
-
大表(数据量超过1亿,或未来会超过):用 bigint,避免主键溢出;
实操提示:生产中,大部分业务用 int 足够,电商、社交等大流量业务,用 bigint。
-
-
【高频】什么是索引合并?什么时候会触发?
答:索引合并:MySQL 会同时使用多个索引,对结果进行合并(union/交集),提升查询速度;
触发场景:where 条件用 or 连接,且 or 两边的字段都有索引;
例子:where a=? or b=?,a和b都有索引,MySQL会分别查a和b的索引,再合并结果;
实操提示:索引合并的性能不如 union 稳定,建议优先用 union 替代。
-
【必考】实战题:慢SQL:select * from order where status=1 and order_time > '2026-01-01',怎么优化?
答:优化步骤(实操,面试必说):
-
分析索引:status 重复值多(选择性低),order_time 选择性高;
-
建联合索引(order_time, status):order_time 放左边(范围查询),status 放右边(过滤);
-
用覆盖索引:如果只查需要的字段(比如 id, order_time, amount),建索引(order_time, status, amount),避免回表;
-
验证:用 explain 查看,type 变成 range,key 显示新建的索引,Extra 有 Using index,优化完成。
-
-
【高频】MySQL 索引的选择性怎么计算?为什么重要?
答:选择性计算公式:选择性 = distinct(字段值) / count(*);
重要性:选择性越高,索引效果越好(比如主键选择性=1,效果最好;性别字段选择性=0.02,效果差);
实操提示:建索引时,优先选择选择性>0.2的字段,选择性太低的字段(比如性别),不建索引。
-
【必考】8.0 和 8.4 版本,索引有哪些核心区别?(2026高频)
答:3个核心区别(实操重点):
-
8.4 支持部分索引、跳过扫描,8.0 不支持;
-
8.4 自动清理索引碎片,8.0 需要手动执行 optimize table;
-
8.4 联合索引的查询效率提升20%,优化了最左匹配的执行逻辑;
实操提示:面试时,能说出版本差异,体现你关注技术更新,加分。
-
第二模块:MySQL 事务(35题 · 必考,重点考察异常处理)
核心考点:事务ACID、隔离级别、事务异常(脏读/不可重复读/幻读)、MVCC、生产实战问题,2026年重点考察"事务与锁的联动"。
一、基础必考(10题,入门必答)
-
【必考】什么是 MySQL 事务?通俗说,不用堆定义
答:事务就是一组SQL操作,要么全部执行成功,要么全部执行失败,不能只执行一部分;
极简原理:保证数据的一致性,比如转账(A扣钱、B加钱),不能A扣了钱,B没加钱;
实操例子:
begin; -- 开启事务
update user set balance=balance-100 where id=1; -- A扣钱
update user set balance=balance+100 where id=2; -- B加钱
commit; -- 提交事务(全部成功)
-- 若有错误,执行 rollback; -- 回滚,恢复到事务前状态
-
【高频】事务的ACID分别是什么?通俗解释,别讲理论
答:ACID(必记,面试必考):
-
A(原子性):事务是一个整体,要么全成,要么全败(比如转账,不能只扣不加);
-
C(一致性):事务执行前后,数据总量不变(比如转账,A扣100,B加100,总余额不变);
-
I(隔离性):多个事务同时执行,互不干扰(比如A和B同时给C转账,不会算错);
-
D(持久性):事务提交后,数据永久保存(比如转账成功后,即使MySQL重启,数据也不会丢);
实操提示:面试时,用通俗的例子解释,比背定义得分高。
-
-
【必考】MySQL 支持哪些事务隔离级别?默认是哪个?
答:4个隔离级别(按隔离程度从低到高):
-
读未提交(Read Uncommitted):能读到其他事务未提交的数据;
-
读已提交(Read Committed):只能读到其他事务已提交的数据(MySQL 默认隔离级别);
-
可重复读(Repeatable Read):同一事务内,多次读取同一数据,结果一致;
-
串行化(Serializable):事务排队执行,完全隔离,无并发问题;
实操提示:记住默认隔离级别是"读已提交",InnoDB 支持所有4种,MyISAM 不支持事务。
-
-
【高频】为什么 MySQL 默认隔离级别是读已提交?
答:平衡"隔离性"和"并发性能":
-
读未提交:隔离性太差,会出现脏读,几乎不用;
-
可重复读:隔离性好,但并发性能差(会出现幻读,需要额外处理);
-
串行化:隔离性最好,但并发性能极差,适合数据一致性要求极高的场景(比如金融);
-
读已提交:既能避免脏读,又能保证较好的并发性能,适合大部分业务;
实操提示:生产中,90%的业务用默认隔离级别,无需修改。
-
-
【必考】什么是脏读、不可重复读、幻读?通俗解释,带例子
答:3种事务异常(必记,面试高频):
-
脏读:读到其他事务未提交的数据(比如A给B转账,A扣钱未提交,B读到扣钱后的数据,A又回滚,B读到的是"脏数据");
-
不可重复读:同一事务内,多次读同一数据,结果不一样(比如A读B的余额是100,同时C给B转账并提交,A再读B的余额是200);
-
幻读:同一事务内,多次查询同一条件,结果行数不一样(比如A查"余额>100的用户"有2个,同时B新增1个余额>100的用户并提交,A再查有3个);
实操提示:记住"脏读是读未提交,不可重复读是改数据,幻读是加数据"。
-
-
【高频】不同隔离级别,能避免哪些事务异常?(必记表格)
答:简化表格(不用记复杂理论,直接背):
-
读未提交:不能避免任何异常(脏读、不可重复读、幻读都有);
-
读已提交:避免脏读,不能避免不可重复读、幻读;
-
可重复读:避免脏读、不可重复读,能减轻幻读(InnoDB 用MVCC避免幻读);
-
-
串行化:避免所有异常;
实操提示:面试时,能说出这个表格,就是加分项,体现你对隔离级别的理解。
-
【必考】MySQL 怎么开启和关闭事务?(实操命令)
答:3种开启方式(实操必记):
-
begin; -- 开启事务,最常用
-
start transaction; -- 和begin一样,写法不同
-
自动开启:MySQL 默认 autocommit=1(自动提交),每执行一条SQL,自动提交事务;
关闭/提交事务:
-
commit; -- 提交事务,数据永久保存
-
rollback; -- 回滚事务,恢复到事务前状态
实操提示:生产中,批量操作(比如批量更新),必须手动开启事务(begin),执行完再commit,避免出错。
-
-
【高频】autocommit 是什么?怎么设置?
答:autocommit 是 MySQL 事务自动提交开关:
-
autocommit=1(默认):自动提交,每一条SQL都是一个独立事务;
-
autocommit=0:手动提交,必须执行 commit 才会保存数据;
实操命令:
-- 查看当前设置:
show variables like 'autocommit';
-- 设置为手动提交(临时生效,重启MySQL失效):
set autocommit=0;
-- 永久生效:修改 my.cnf,添加 autocommit=0,重启MySQL;
实操提示:生产中,批量操作时,临时设置 autocommit=0,操作完成后改回1。
-
-
【必考】什么是事务的提交和回滚?什么时候需要回滚?
答:
-
提交(commit):事务执行成功,将所有操作永久保存到数据库,不能撤销;
-
回滚(rollback):事务执行失败,撤销所有操作,恢复到事务开启前的状态;
需要回滚的场景(实操):
-
SQL执行出错(比如语法错误、主键冲突);
-
业务逻辑出错(比如转账时,发现对方账号不存在);
-
并发冲突(比如锁等待超时);
实操提示:生产中,建议用 try-catch 捕获异常,出现异常就 rollback,正常就 commit。
-
-
【高频】MySQL 事务支持嵌套吗?为什么?
答:不支持真正的嵌套事务;
极简原理:MySQL 中,执行 begin 后,再执行 begin,不会开启嵌套事务,只会重置事务状态;
实操例子:
begin; -- 开启事务1
update user set balance=90 where id=1;
begin; -- 不会开启事务2,只会重置事务1
update user set balance=80 where id=1;
commit; -- 提交所有操作,id=1的balance是80;
实操提示:如果需要"嵌套事务"效果,用 savepoint(保存点),但不是真正的嵌套。
二、事务异常与解决(12题,面试高频,生产必避坑)
-
【必考】脏读怎么解决?(实操方法)
答:核心解决方法:将事务隔离级别提升到"读已提交"及以上;
实操步骤:
-
查看当前隔离级别:show variables like 'transaction_isolation';
-
设置隔离级别为读已提交(临时生效):set session transaction isolation level read committed;
-
永久生效:修改 my.cnf,添加 transaction_isolation = READ-COMMITTED,重启MySQL;
原理:读已提交级别,只能读到其他事务已提交的数据,避免读到未提交的脏数据;
实操提示:生产中,默认隔离级别就是读已提交,无需额外设置,就能避免脏读。
-
-
【高频】不可重复读怎么解决?(实操方法)
答:2种实操方法(按优先级排序):
-
提升隔离级别到"可重复读":set session transaction isolation level repeatable read;
-
用悲观锁:查询时加锁(select ... for update),阻止其他事务修改数据;
例子:
begin;
select balance from user where id=1 for update; -- 加行锁,其他事务不能修改id=1的记录
-- 执行其他操作
commit;
实操提示:可重复读级别,InnoDB 用 MVCC 实现,不用加锁,性能更好,优先选择。
-
-
【必考】幻读怎么解决?(实操方法,2026高频)
答:3种实操方法(生产常用):
-
提升隔离级别到"串行化":事务排队执行,彻底避免幻读,但并发性能差;
-
用 next-key lock(间隙锁):InnoDB 可重复读级别默认开启,阻止插入符合条件的新数据;
-
用乐观锁:通过版本号(version)控制,避免幻读(适合并发高的场景);
实操例子(乐观锁):
-- 表中添加 version 字段(默认1)
update user set balance=balance+100, version=version+1 where id=1 and version=1;
-- 若其他事务已修改,version不匹配,更新失败,重试即可;
实操提示:生产中,优先用 next-key lock(无需手动操作),并发高的场景用乐观锁。
-
-
【高频】什么是 MVCC?通俗解释,它怎么解决不可重复读和幻读?
答:MVCC(多版本并发控制):通俗说,就是给每行数据存多个版本,事务读取时,根据隔离级别,读取对应版本的数据,不用加锁;
解决原理(通俗版):
-
解决不可重复读:同一事务内,多次读取同一数据,都读取同一个版本,结果一致;
-
解决幻读:InnoDB 结合 next-key lock(间隙锁),阻止插入新数据,避免幻读;
实操提示:MVCC 是 InnoDB 实现事务隔离的核心,面试时,不用讲复杂的版本链,说通俗解释即可。
-
-
【必考】事务死锁怎么产生的?怎么排查?怎么解决?(生产重点)
答:产生原因:两个或多个事务,互相持有对方需要的锁,互相等待,导致卡死;
例子:
事务1:begin; update user set balance=90 where id=1; (持有id=1的锁)
事务2:begin; update user set balance=80 where id=2; (持有id=2的锁)
事务1:update user set balance=70 where id=2; (等待id=2的锁)
事务2:update user set balance=60 where id=1; (等待id=1的锁)
排查方法(实操):
-
查看死锁日志:show engine innodb status; (找到死锁的事务ID、SQL);
-
查看当前锁情况:select * from information_schema.innodb_locks;
解决方法(实操):
-
手动终止死锁:kill 事务ID; (优先终止执行时间短的事务);
-
预防死锁:① 统一SQL执行顺序(比如都先更id=1,再更id=2);② 减少事务持有锁的时间(尽量短事务);③ 避免长事务;
-
-
【高频】长事务有什么危害?怎么避免?
答:危害(实操重点):
-
占用锁资源,导致其他事务阻塞,并发性能下降;
-
产生大量undo日志,占用磁盘空间,影响MySQL性能;
-
可能导致死锁、锁等待超时;
避免方法(实操):
-
事务尽量短:只包含必要的SQL,避免在事务内做非数据库操作(比如调用接口、睡眠);
-
避免批量操作:批量更新/插入,分批次执行(比如每1000条提交一次);
-
监控长事务:设置 long_query_time(比如10秒),开启慢查询日志,及时发现长事务;
实操提示:生产中,事务执行时间尽量控制在1秒内,超过10秒的长事务必须优化。
-
-
【必考】事务锁等待超时怎么解决?(实操步骤)
答:解决步骤(面试必说):
-
查看锁等待超时时间:show variables like 'innodb_lock_wait_timeout'; (默认50秒);
-
临时调整超时时间(根据业务调整,比如10秒):set global innodb_lock_wait_timeout=10;
-
排查锁等待原因:用 show engine innodb status; 查看哪个事务持有锁,阻塞了当前事务;
-
-
优化:① 终止持有锁的长事务;② 优化SQL,减少锁持有时间;③ 调整事务执行顺序;
实操提示:不建议盲目调大超时时间,优先排查锁等待原因,优化业务逻辑。
-
【高频】MySQL 事务的隔离级别怎么设置?(全局+会话)
答:实操命令(必记):
-- 1. 查看当前会话隔离级别:
show variables like 'transaction_isolation';
-- 2. 查看全局隔离级别:
show global variables like 'transaction_isolation';
-- 3. 设置当前会话隔离级别(临时生效):
set session transaction isolation level 隔离级别; (比如 read committed)
-- 4. 设置全局隔离级别(永久生效,需重启MySQL):
set global transaction isolation level 隔离级别;
-- 5. 永久生效(修改配置文件 my.cnf):
transaction_isolation = READ-COMMITTED (默认)
实操提示:生产中,一般不修改全局隔离级别,只根据特殊业务,临时修改会话隔离级别。
-
【必考】什么是分布式事务?MySQL 怎么实现?(大厂高频)
答:分布式事务:跨多个数据库(或分库分表)的事务,比如订单表和库存表在不同数据库,下单时需要同时操作两个库,保证一致性;
MySQL 实现方法(实操,常用2种):
-
两阶段提交(2PC):MySQL 原生支持,分为准备阶段和提交阶段,适合简单分布式场景;
- 补偿事务(TCC):手动编写补偿逻辑,比如下单失败,回滚订单表,同时恢复库存,适合复杂业务;
实操提示:生产中,简单场景用2PC,复杂场景用TCC,也可以用中间件(比如Seata)简化实现。
-
【高频】事务提交后,数据还能回滚吗?为什么?
答:不能;
极简原理:事务提交后,数据会永久写入磁盘(持久性D),undo日志会被标记为可清理,无法再通过 rollback 回滚;
补救方法(实操):
-
用备份恢复:从最近的备份中恢复数据(适合数据量小的场景);
-
用binlog日志恢复:通过binlog日志,重做提交前的操作,实现"回滚";
实操提示:生产中,提交事务前,一定要确认SQL执行正确,避免提交后出错。
-
-
【必考】实操题:转账业务,A扣钱成功,B加钱失败,怎么保证数据一致性?
答:核心思路:用事务包裹所有操作,出现异常就回滚,正常就提交;
实操代码(可直接复制):
begin; -- 开启事务
-- 1. A扣钱(假设A的余额足够)
update user set balance=balance-100 where id=1;
-- 2. 判断A扣钱是否成功(影响行数>0)
if row_count() = 0 then
rollback; -- 扣钱失败,回滚 return;end if;
-- 3. B加钱
update user set balance=balance+100 where id=2;
-- 4. 判断B加钱是否成功
if row_count() = 0 then
rollback; -- 加钱失败,回滚A的扣钱操作
return;end if;
-- 5. 所有操作成功,提交事务
commit;
实操提示:生产中,还要捕获SQL异常(比如主键冲突、锁等待超时),异常时也需要 rollback。
-
【高频】MySQL 事务中,DDL语句(create/alter/drop)会影响事务吗?
答:会,DDL语句会自动提交事务,无论当前事务是否执行完;
例子:
begin;
update user set balance=90 where id=1;
alter table user add column age int; -- DDL语句,自动提交事务
rollback; -- 无效,事务已经被自动提交
实操提示:生产中,不要在事务内执行DDL语句,避免事务被意外提交。
三、事务实战优化(13题,生产落地+面试加分)
-
【必考】生产环境,事务优化的核心方法有哪些?(至少说5种)
答:5种实操优化方法(必记,面试高频):
-
尽量短事务:减少锁持有时间,避免阻塞其他事务;
-
避免长事务:不在事务内做非数据库操作(比如调用接口、日志打印);
-
合理设置隔离级别:大部分业务用默认读已提交,无需提升隔离级别;
-
避免在事务内批量操作:分批次执行,每批次提交一次;
-
-
优化SQL:减少事务内的查询、更新操作,避免全表扫描(索引优化);
- 避免锁冲突:统一SQL执行顺序,减少死锁风险;
实操提示:面试时,能说出具体优化方法,比说"提升性能"这种空话得分高。
-
【高频】事务中,查询操作需要加锁吗?为什么?
答:分情况(实操重点):
-
普通查询(select):不需要加锁,InnoDB 用 MVCC 实现无锁查询,不影响并发;
-
锁定查询(select ... for update / for share):需要加锁,用于避免并发修改,比如转账时查询余额,防止其他事务同时修改;
实操例子:
-- 锁定查询(行锁),其他事务不能修改id=1的记录
select balance from user where id=1 for update;
-- 共享锁(for share),其他事务可以读,不能改
select balance from user where id=1 for share;
实操提示:锁定查询尽量少用,只在需要避免并发修改的场景使用,否则会影响性能。
-