MySQL 常用存储引擎有哪些?区别是什么?
InnoDB(默认):支持事务、行级锁,表锁,适合高并发、数据一致性要求高的场景(比如订单、用户表);
MyISAM:不支持事务和行锁,只支持表锁,查询快,适合读多写少的场景(比如日志、统计报表);
核心区别:InnoDB 注重事务和并发,MyISAM 注重查询性能;InnoDB 用聚簇索引,MyISAM 用非聚簇索引。
事务的ACID 原则分别是什么?
事务是一组原子性的 SQL 操作,要么全执行,要么全不执行。
A(原子性):操作不可拆分,要么全成,要么全回滚;
C(一致性):事务执行前后,数据符合业务规则(比如转账后总金额不变);
I(隔离性):多个事务并发执行,互相不干扰;
D(持久性):事务提交后,数据永久保存到磁盘。
MySQL 事务隔离级别有哪些?默认是什么?
读未提交(脏读):能读到其他事务未提交的数据;
读已提交(解决脏读,存在不可重复读):只能读到其他事务已提交的数据,但同一事务内多次读结果可能不同;
可重复读(解决不可重复读,存在幻读):MySQL 默认级别,同一事务内多次读结果一致,I可重复读级别通过 MVCC(多版本并发控制)+ 间隙锁 解决了幻读问题;
串行化(Serializable):完全串行执行,无并发问题,性能最差;
InnoDB 如何解决可重复读下的幻读?
MVCC 解决了 “查询层面的幻读”,Next-Key Lock(间隙锁 + 行锁)解决了 “写操作层面的幻读”
- MVCC(多版本并发控制)
原理:InnoDB 为每行数据维护多个版本,事务读取时会根据 “事务 ID” 读取自己可见的版本 - Next-Key Lock(间隙锁)
原理:这是 InnoDB 的行锁升级,不仅锁定记录本身,还锁定记录之间的 “间隙”(比如 age <25 会锁定 0-25 的所有间隙)。
索引是什么?作用是什么?
索引是 MySQL 优化查询的数据结构(B + 树为主)
作用:
- 加快查询速度;
- 减少数据扫描行数;
- 辅助排序分组。
类型
分为四个类型
B-Tree索引(默认的类型),哈希索引(Memory引擎专用),全文索引,组合索引
注意
覆盖索引只需包含 SELECT 和 WHERE 中的字段即可
每个索引占用额外磁盘空间,会增加维护成本
建立的索引不能过多,否则会降低 写入的速度
索引有哪些类型?
按数据结构:B + 树索引(主流)、哈希索引(Memory 引擎)、全文索引(MyISAM/InnoDB);
按功能:主键索引(唯一非空)、唯一索引(唯一可空)、普通索引、联合索引;
按物理存储:聚簇索引(InnoDB 主键,数据和索引存在一起)、非聚簇索引(MyISAM,数据和索引分开)。
为什么 MySQL 索引用 B + 树而不是 B 树 / 红黑树?
对比 B 树:B + 树叶子节点用链表连接,范围查询更高效;非叶子节点只存索引,内存能存更多节点,IO 更少;
对比红黑树:树高度高(百万数据要 20 层),磁盘 IO 次数多,查询慢;B + 树高度低(百万数据 3-4 层),IO 少。
InnoDB 行锁和表锁的区别?什么时候会升级为表锁?
行锁:只锁行数据,并发高,InnoDB 默认;
表锁:锁整张表,并发低,MyISAM 默认;
行锁升级表锁的情况:
- 没有走索引,InnoDB 会全表扫描并锁全表;
- 锁的行太多,MySQL 自动升级为表锁。
什么是死锁?如何避免和解决?
死锁:多个事务互相持有对方需要的锁,互相等待,导致卡住。
避免:1 按固定顺序加锁;2 控制事务大小,缩短锁持有时间;3 加锁时设置超时时间;
解决:1 用 show engine innodb status 查看死锁日志;2 手动 kill 掉卡住的事务;3 开启 innodb_deadlock_detect 自动检测死锁并回滚。
MySQL内连接,外连接是什么
核心区别
内连接
只返回两个表中满足连接条件的公共数据SELECT columns FROM table1 INNER JOIN table2 ON table1.key = table2.key;
外连接
左外连接(LEFT JOIN)
定义:返回左表的所有记录,以及右表中匹配的记录。如果右表无匹配,则用 NULL 填充SELECT columns FROM table1 LEFT JOIN table2 ON table1.key = table2.key;
右外连接(RIGHT JOIN)
定义:返回右表的所有记录,以及左表中匹配的记录。如果左表无匹配,则用 NULL 填充SELECT columns FROM table1 RIGHT JOIN table2 ON table1.key = table2.key;
MySQL中UNION 操作符有什么用
MySQL UNION 操作符用于连接两个以上的 SELECT 语句的结果组合到一个结果集合中
SELECT `字段名1`,`字段名2`, ... `字段名n` FROM `表名1`
where `条件`
UNION [ALL | DISTINCT]
SELECT `字段名1`,`字段名2`, ... `字段名n` FROM `表名2`
where `条件`;
-- DISTINCT: 可选,删除结果集中重复的数据。默认情况下 UNION 操作符已经删除了重复数据,所以 DISTINCT 修饰符对结果没啥影响。
-- ALL: 可选,返回所有结果集,包含重复数据。
where和having的区别?
where是对分组前的条件进行限定。having是对分组后的内容进行限定。
where后面不能加聚合函数,having后可以跟聚合函数。
主键ID生成方案
数据库中主键id的生成方案,主要有三种
数据库自增ID
采用随机数生成不重复的ID
采用jdk提供的uuid
主键id性能排列
自增ID > 雪花算法生成的ID >uuid生成的ID。
实际使用
在实际使用过程中,推荐使用主键自增ID和雪花算法生成的随机ID。
- delete,drop,truncate 都有删除表的作用,区别在于:
- delete 和 truncate 仅仅删除表数据,drop 连表数据和表结构一起删除,打个比方,delete 是单杀,truncate 是团灭,drop 是把电脑摔了。
- delete 是 DML 语句,操作完以后如果没有不想提交事务还可以回滚,truncate 和 drop 是 DDL 语句,操作完马上生效,不能回滚,打个比方,delete 是发微信说分手,后悔还可以撤回,truncate 和 drop 是直接扇耳光说滚,不能反悔。
- 执行的速度上,drop>truncate>delete,打个比方,drop 是神舟火箭,truncate 是和谐号动车,delete 是自行车。
数据库连接池参数怎么调?(比如 Druid)
核心参数:① initialSize(初始连接数);② maxActive(最大连接数);③ minIdle(最小空闲数);④ maxWait(获取连接超时时间);
调优原则:① 最大连接数不要超过 MySQL 的 max_connections;② 初始连接数设为业务峰值的 50%;③ 空闲连接回收时间合理(避免频繁创建销毁连接)。
MySQL 磁盘满了怎么处理?
① 先删除无用日志(慢查询日志、binlog);② 清理大表数据(归档后删除);③ 分表分库,迁移部分数据;④ 扩容磁盘。
如何恢复误删的数据?
① 有备份:用备份 + binlog 恢复到误删前的时间点;
② 无备份:用 binlog2sql 解析 binlog,找回删除的 SQL,反向执行;
③ 预防:开启 binlog,定期备份,操作前先备份。
性能优化
如何优化 MySQL 写入性能?
- 批量插入(insert into … values (…),(…));
- 关闭自动提交(begin/commit);
- 减少索引(写入时少维护索引);
- 调大 innodb_buffer_pool_size,减少磁盘 IO;
- 分库分表,分摊写入压力。
如何优化 MySQL 查询性能?
- 加合适的索引(避免冗余索引、无效索引);
- 优化 SQL:避免 select *、减少 join、不用子查询;
- 分库分表(数据量大时);
- 读写分离(主库写,从库读);
- 配置优化:调大缓存(innodb_buffer_pool_size)、优化连接数。
MySQL 索引优化,常用的索引优化方法有哪些,怎么判断一个 SQL 该加索引,加索引有啥注意事项?
首先看 SQL 的 where 条件、联表字段,高频查询的字段适合加;
注意事项就是别给高频更新的字段加,也别加太多,不然增删改会变慢,复合索引要注意最左匹配原则
什么是最左前缀匹配原则?
以联合索引 idx_abc(a,b,c) 为例
- 完全匹配(最理想)
-- 匹配 a → b → c,索引全生效 SELECT * FROM table WHERE a=1 AND b=2 AND c=3; - 匹配最左前缀(部分生效)
-- 只匹配 a,索引生效(仅用 a 列索引) SELECT * FROM table WHERE a=1; -- 匹配 a → b,索引生效(用 a+b 列索引) SELECT * FROM table WHERE a=1 AND b=2; - 跳过左侧列(索引失效)
-- 跳过 a,直接查 b,索引完全失效(全表扫描) SELECT * FROM table WHERE b=2; -- 跳过 b,查 a+c,仅 a 列索引生效,c 列无法使用索引 SELECT * FROM table WHERE a=1 AND c=3; -- 直接查 c,索引完全失效 SELECT * FROM table WHERE c=3; - 特殊情况:条件顺序不影响(MySQL 会优化)
联合索引的匹配只看是否包含最左列,不看 where 条件的书写顺序,因为 MySQL 会自动调整条件顺序:
– 等价于 WHERE a=1 AND b=2,MySQL 优化后命中索引-- 虽然顺序是 b,a,但包含 a(最左列),索引仍生效 SELECT * FROM table WHERE b=2 AND a=1;
索引失效场景
- 查询条件用了函数 / 运算(比如 where id+1=10);
- 用了 like % xxx(模糊查询左通配);
- 联合索引不满足最左前缀;
- 用了 or(两边字段都无索引);
- 数据类型隐式转换(比如字符串字段传数字)。
- 使用 is null / is not null(除非字段是唯一索引);
- 使用 != / <> / not in(大概率失效);
索引太多会有什么问题?如何优化?
问题:1 写入 / 更新 / 删除变慢,因为要维护多个索引;2 占用更多磁盘空间;3 MySQL 优化器选择索引的耗时增加;
优化:1 删除冗余索引(比如联合索引 a,b 和单独索引 a);2 删除未使用的索引;3 合并相似索引。
MySQL 聚簇索引和非聚簇索引的区别?
聚簇索引(主键)的索引与数据存储在一起,叶子节点是数据行;非聚簇索引叶子节点是主键值,查询需回表;项目中建联合索引减少回表操作。
高并发下 MySQL 写操作怎么保证一致性?
答:通过事务保证操作原子性;库存扣减等场景用 Redisson 分布式锁;修改配置表 / 商品信息用乐观锁(加版本号),防止并发数据覆盖。
慢查询优化步骤
开启慢查询日志:
set global slow_query_log=1; -- 临时开启
set global long_query_time=1; -- 记录超过1s的SQL
用 explain 分析执行计划:
重点看 type(最好是 ref/range,最差是 all 全表扫描);
看 key(是否用到索引)、rows(扫描行数);
优化手段:加索引、改写 SQL(如子查询改 JOIN)、拆分大 SQL
分库分表
MySQL分库分表后,动态切换数据源是如何实现的
这块是MyBatis-Plus 插件,mybatis-mate-sharding-dynamic自动完成路由和连接切换;
在配置文件 中配置多个数据源,并指定默认数据源;在类或方法上使用@DS注解
什么时候分库分表
单表触发分表的核心阈值(实战硬指标)
数据量:InnoDB 引擎单表数据量超过 500 万行(或数据文件超过 10GB),B + 树索引会深度过大(超过 3 层),查询时磁盘 IO 次数剧增,响应时间大幅变慢;
并发量:单表写并发超过 1000 QPS、读并发超过 5000 QPS,数据库连接池占满,写操作出现阻塞,锁竞争加剧;
性能指标:单表查询响应时间超过 2 秒,即使做了索引优化、SQL 调优也无法改善;日常增删改操作频繁出现锁等待、死锁。
单库触发分库的核心阈值(实战硬指标)
单库数据量:超过 200GB,数据库备份 / 恢复时间过长,运维成本剧增;
单库并发量:超过 3000 QPS,数据库服务器 CPU、IO、内存资源被占满,无法通过升级硬件解决;
业务隔离:不同业务模块(如库存、订单、用户)共用一个库,相互影响(如订单模块高并发写导致用户模块查询变慢),需要按业务分库。
分库分表的方式有哪些?适用场景?
水平分表(按行分):比如用户表按 id 哈希分到多个表;适合单表数据量过大(比如千万级);
垂直分表(按列分):比如用户表拆成基础信息表和详情表;适合字段多、大字段多的表;
分库:按业务模块分(比如订单库、用户库);适合单库压力过大。
MySQL 主从复制原理?主从延迟怎么解决?
原理:① 主库把写操作记录到 binlog;② 从库连主库,拉取 binlog;③ 从库执行 binlog,同步数据;
主从延迟解决:① 升级硬件;② 减少从库压力(比如从库只负责读,不做统计);③ 用半同步复制;④ 分库分表,分摊同步压力。
数据迁移
如果两张表结构一样,要把表 B 的 100 万条数据插到表 A 里,我一般这么做:
首先用 insert into … select 这种方式,直接在数据库里批量导数据,
会分批处理,比如每次插 1 万条,避免锁表时间太长、数据库压力过大。
如果追求更快,我会先把表 A 的索引、外键先临时关掉,插完再重新开启,这样插入速度会快很多。
业务允许的话,也可以先清空表 A,再一次性导入,效率更高。
如果是跨服务器迁移,就用 mysqldump 导出再导入,或者用 CSV 文件导入的方式。
整体思路就是:尽量数据库层批量操作,分批插入,减少锁表和性能问题。