Skip to content

MySQL 8.0 面试题集 ​

生成时间:2026-06-08
覆盖主题:InnoDB架构、索引优化、事务隔离级别、MVCC机制、锁机制、SQL优化、主从复制、分库分表、窗口函数、CTE、JSON支持、性能调优


题目1:InnoDB存储引擎架构 ​

难度: ⭐⭐⭐

题目: 请详细描述 InnoDB 存储引擎的架构组成,包括内存结构和磁盘结构,并说明各组件的作用。

标准答案:

InnoDB 架构分为内存结构和磁盘结构两大部分:

内存结构:

  1. Buffer Pool(缓冲池):缓存表数据和索引数据,是 InnoDB 性能的关键。采用 LRU 算法管理页。
  2. Change Buffer(变更缓冲区):缓存对非唯一索引的 DML 操作,减少随机 I/O。
  3. Log Buffer(日志缓冲区):缓存 redo log 的写入,定期刷盘。
  4. Adaptive Hash Index(自适应哈希索引):InnoDB 自动为热点页建立的哈希索引,加速等值查询。

磁盘结构:

  1. 表空间(Tablespace):
    • System Tablespace(ibdata1):存储数据字典、双写缓冲区、Change Buffer、Undo Logs
    • File-Per-Table Tablespace(.ibd 文件):每个表独立的数据文件和索引
    • General Tablespace:共享表空间
    • Undo Tablespace:存储 Undo Log(MySQL 8.0 默认独立)
    • Temporary Tablespace:临时表空间
  2. Redo Log(重做日志):保证事务的持久性,用于崩溃恢复。
  3. Doublewrite Buffer(双写缓冲区):防止页部分写入导致的数据损坏。

延伸思考:

  • 为什么 InnoDB 默认页大小是 16KB?
  • 如何调整 Buffer Pool 大小以优化性能?
  • Doublewrite Buffer 对性能的影响是什么?

题目2:索引优化与覆盖索引 ​

难度: ⭐⭐⭐

题目: 什么是覆盖索引(Covering Index)?它如何提升查询性能?在什么情况下无法使用覆盖索引?

标准答案:

覆盖索引是指查询的所有列都包含在索引中,无需回表(无需访问聚簇索引的数据页)。

提升性能的原理:

  1. 减少随机 I/O:避免回表操作,减少磁盘访问次数。
  2. 利用索引的有序性:索引页通常比数据页小,缓存命中率更高。
  3. 减少 Buffer Pool 占用:只需缓存索引页,不占用数据页空间。

使用条件(EXPLAIN 中 Extra 列显示 Using index):

  • SELECT、WHERE、ORDER BY、GROUP BY 中的列都包含在索引中。

无法使用覆盖索引的情况:

  1. 查询列包含 SELECT *(无法全部覆盖)
  2. 查询列包含索引未覆盖的列
  3. 使用 LIKE '%xxx'(前导模糊查询无法利用索引)
  4. 索引列上使用函数或表达式
  5. 查询涉及 TEXT、BLOB 类型列(这些列不会存储在二级索引中)

示例:

sql
-- 有覆盖索引
CREATE INDEX idx_cover ON users(name, age);
SELECT name, age FROM users WHERE name = 'Alice'; -- Using index

-- 无覆盖索引
SELECT name, age, email FROM users WHERE name = 'Alice'; -- 需要回表查 email

延伸思考:

  • 如何设计联合索引以实现覆盖索引?
  • 覆盖索引对 Write 操作的影响是什么?(写入时需要维护更多索引)
  • MySQL 8.0 的不可见索引(Invisible Index)如何影响覆盖索引?

题目3:事务隔离级别与并发问题 ​

难度: ⭐⭐⭐

题目: MySQL 支持哪些事务隔离级别?请详细说明每个隔离级别能解决哪些并发问题(脏读、不可重复读、幻读)。

标准答案:

MySQL(InnoDB)支持四种标准隔离级别:

隔离级别脏读不可重复读幻读
READ UNCOMMITTED(读未提交)❌ 可能❌ 可能❌ 可能
READ COMMITTED(读已提交)✅ 解决❌ 可能❌ 可能
REPEATABLE READ(可重复读,默认)✅ 解决✅ 解决❌ 可能(InnoDB 通过间隙锁解决)
SERIALIZABLE(串行化)✅ 解决✅ 解决✅ 解决

详细说明:

  1. READ UNCOMMITTED:事务可以读取其他事务未提交的数据。可能产生脏读、不可重复读、幻读。

  2. READ COMMITTED:事务只能读取其他事务已提交的数据。解决脏读,但可能产生不可重复读(同一事务内两次读取结果不同)和幻读。

  3. REPEATABLE READ(InnoDB 默认):

    • 通过 MVCC(多版本并发控制) 保证同一事务内读取结果一致。
    • InnoDB 通过 间隙锁(Gap Lock) 解决了幻读问题(标准 SQL 定义中 RR 不保证解决幻读)。
    • 可能产生幻读的场景:其他事务插入新行,当前事务再次查询时看到新行。
  4. SERIALIZABLE:最高隔离级别,通过强制事务串行执行解决所有并发问题,但性能最差。

MySQL 8.0 设置隔离级别:

sql
-- 全局设置
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- 会话级别设置
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 查看当前隔离级别
SELECT @@transaction_isolation;

延伸思考:

  • 为什么 InnoDB 的 RR 级别能解决幻读?(间隙锁 + Next-Key Lock)
  • 什么时候应该选择 READ COMMITTED 而不是默认的 RR?
  • 隔离级别与锁的关系是什么?

题目4:MVCC 多版本并发控制机制 ​

难度: ⭐⭐⭐⭐

题目: 请详细解释 InnoDB 的 MVCC 实现原理,包括隐藏列、Undo Log、Read View 的作用。

标准答案:

MVCC(Multi-Version Concurrency Control) 是 InnoDB 实现高并发的核心机制,通过保存数据在某个时间点的快照来实现非锁定读。

核心组件:

  1. 隐藏列(Hidden Columns):

    • DB_TRX_ID(6字节):最后一次插入或更新该行的事务 ID。
    • DB_ROLL_PTR(7字节):回滚指针,指向 Undo Log 中的历史版本。
    • DB_ROW_ID(6字节):行 ID,表没有主键时自动生成聚簇索引。
  2. Undo Log(回滚日志):

    • 存储数据旧版本,形成版本链。
    • 通过 DB_ROLL_PTR 指针串联多个版本。
    • 用于事务回滚和 MVCC 快照读。
  3. Read View(一致性视图):

    • 事务开始时创建,定义当前事务能看到哪些版本的数据。
    • 包含:
      • m_ids:当前活跃的事务 ID 列表
      • min_trx_id:最小的活跃事务 ID
      • max_trx_id:下一个将被分配的事务 ID
      • creator_trx_id:创建该 Read View 的事务 ID

判断数据可见性的规则:

if (DB_TRX_ID < min_trx_id):
    可见(事务开始前已提交)
elif (DB_TRX_ID >= max_trx_id):
    不可见(事务开始后启动)
elif (DB_TRX_ID in m_ids):
    不可见(事务开始时活跃)
else:
    可见(事务开始前已提交)

如果当前版本不可见,通过 DB_ROLL_PTR 查找 Undo Log 中的历史版本,直到找到可见版本。

RR 与 RC 的区别:

  • RR:事务开始时创建一次 Read View,整个事务期间都用同一个视图。
  • RC:每次读取都创建新的 Read View,所以能读到其他事务已提交的修改。

延伸思考:

  • 为什么长事务会导致 Undo Log 膨胀?
  • MVCC 如何支持幻读防护?(需要配合间隙锁)
  • 如何查看当前事务的事务 ID?

题目5:InnoDB 锁机制详解 ​

难度: ⭐⭐⭐⭐

题目: InnoDB 支持哪些类型的锁?请详细说明每种锁的作用、加锁时机和适用场景。如何避免死锁?

标准答案:

InnoDB 锁分类:

1. 按锁的粒度分类 ​

表级锁:

  • 表锁(Table Lock):LOCK TABLES ... READ/WRITE,MyISAM 主要使用,InnoDB 很少用。
  • 意向锁(Intention Lock):
    • 意向共享锁(IS):事务打算给表中的某些行加共享锁。
    • 意向排他锁(IX):事务打算给表中的某些行加排他锁。
    • 作用:快速判断表中是否有行被锁定,避免逐行检查。意向锁之间是兼容的,但 IX 与 S、IS 与 X 冲突。

行级锁:

  • 记录锁(Record Lock):锁定索引记录本身。
    sql
    SELECT * FROM users WHERE id = 1 FOR UPDATE; -- 锁定 id=1 这一行
  • 间隙锁(Gap Lock):锁定索引记录之间的间隙,防止其他事务插入数据。
    • 仅在 RR 隔离级别下生效。
    • 防止幻读。
    sql
    SELECT * FROM users WHERE id > 10 AND id < 20 FOR UPDATE; -- 锁定 (10, 20) 间隙
  • 临键锁(Next-Key Lock):Record Lock + Gap Lock,锁定一个左开右闭区间。
    • InnoDB 默认行锁算法。
    • 例如:索引有值 1, 5, 10, 15,Next-Key Lock 锁定 (-∞, 1], (1, 5], (5, 10], (10, 15], (15, +∞)
  • 插入意向锁(Insert Intention Lock):插入操作前设置的间隙锁,表示打算在某个间隙插入记录。

2. 按锁的模式分类 ​

  • 共享锁(S 锁,Shared Lock):SELECT ... LOCK IN SHARE MODE,允许其他事务读,但不允许修改。
  • 排他锁(X 锁,Exclusive Lock):SELECT ... FOR UPDATE,不允许其他事务读或写。

加锁规则:

  1. 普通 SELECT:快照读,不加锁(MVCC)。
  2. SELECT ... LOCK IN SHARE MODE:加 S 锁。
  3. SELECT ... FOR UPDATE:加 X 锁。
  4. UPDATE / DELETE:加 X 锁。
  5. INSERT:加插入意向锁。

死锁(Deadlock)及避免:

死锁产生条件:

  • 互斥
  • 持有并等待
  • 不可剥夺
  • 循环等待

避免死锁:

  1. 以固定顺序访问表和行(避免循环等待)。
  2. 尽量使用索引(减少锁的范围,避免表锁)。
  3. 控制事务大小(减少持有锁的时间)。
  4. 设置锁超时:innodb_lock_wait_timeout(默认 50 秒)。
  5. 使用 SHOW ENGINE INNODB STATUS 查看最近一次死锁信息。

延伸思考:

  • 为什么 InnoDB 在 RR 级别下使用 Next-Key Lock?
  • 无索引的 UPDATE 会锁表吗?(会,锁定所有行)
  • 如何查看当前 InnoDB 的锁信息?(performance_schema.data_locks)

题目6:SQL 优化与执行计划分析 ​

难度: ⭐⭐⭐

题目: 如何使用 EXPLAIN 分析 SQL 执行计划?请解释各列的含义,并说明常见的最佳索引使用场景和反模式。

标准答案:

EXPLAIN 各列含义:

列名含义
id查询的标识符,表示查询中 SELECT 的顺序
select_type查询类型(SIMPLE、PRIMARY、SUBQUERY、DERIVED、UNION 等)
table当前行访问的表
partitions匹配的分区(如有分区表)
type访问类型(性能从好到坏:system > const > eq_ref > ref > range > index > ALL)
possible_keys可能使用的索引
key实际使用的索引
key_len使用的索引长度
ref与索引比较的列或常量
rows预估需要扫描的行数
filtered表示存储引擎返回的数据在经过过滤后,剩下多少满足查询条件的比例
Extra附加信息(Using index、Using where、Using temporary、Using filesort 等)

访问类型(type)详解:

  • system/const:主键或唯一索引等值查询,最多返回一行。
  • eq_ref:联表查询中,主键或唯一索引等值匹配。
  • ref:非唯一索引等值查询。
  • range:索引范围扫描(BETWEEN、IN、>、< 等)。
  • index:全索引扫描(比 ALL 好,因为索引通常比数据小)。
  • ALL:全表扫描(最差,需要优化)。

最佳索引使用场景:

  1. WHERE 条件列建立索引。
  2. ORDER BY / GROUP BY 列建立索引(避免 Using filesort)。
  3. 覆盖索引(避免回表)。
  4. 联合索引遵循最左前缀原则。

索引使用反模式:

  1. 索引列上使用函数或表达式:
    sql
    -- 反模式
    SELECT * FROM users WHERE YEAR(create_time) = 2024;
    -- 优化后
    SELECT * FROM users WHERE create_time >= '2024-01-01' AND create_time < '2025-01-01';
  2. 隐式类型转换:字符串列用数字查询,导致索引失效。
  3. 前导模糊查询:LIKE '%xxx' 无法使用索引。
  4. OR 条件:可能导致索引合并或全表扫描。
  5. 不符合最左前缀:联合索引 (a, b, c),查询条件只有 b 和 c 无法使用索引。

延伸思考:

  • Using filesort 一定意味着性能差吗?(不一定,如果排序数据量小,在内存中完成也很快)
  • 如何优化 ORDER BY rand() 随机排序?(使用应用层随机或主键随机)
  • MySQL 8.0 的直方图(Histogram)统计信息如何帮助优化器?

题目7:MySQL 主从复制原理与实战 ​

难度: ⭐⭐⭐

题目: 请详细描述 MySQL 主从复制的原理和配置步骤。如何解决主从复制延迟问题?

标准答案:

主从复制原理(基于 Binlog):

MySQL 主从复制分为三个步骤:

  1. 主库记录 Binlog:

    • 主库将数据变更写入二进制日志(Binary Log)。
    • Binlog 有三种格式:
      • STATEMENT:记录 SQL 语句(可能主从数据不一致,如 NOW()、UUID())。
      • ROW(默认,推荐):记录每行数据的变更(安全但日志量大)。
      • MIXED:混合模式,自动选择。
  2. 从库 I/O 线程拉取 Binlog:

    • 从库 I/O 线程连接主库,读取 Binlog 并写入本地的中继日志(Relay Log)。
  3. 从库 SQL 线程回放 Relay Log:

    • 从库 SQL 线程读取 Relay Log,在从库上重放这些变更。

配置步骤:

主库配置(my.cnf):

ini
[mysqld]
server-id = 1
log-bin = mysql-bin
binlog_format = ROW
binlog_row_image = FULL

创建复制用户:

sql
CREATE USER 'repl'@'%' IDENTIFIED BY 'password';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;

获取主库 Binlog 坐标:

sql
FLUSH TABLES WITH READ LOCK;
SHOW MASTER STATUS;
-- 记录 File 和 Position
UNLOCK TABLES;

从库配置(my.cnf):

ini
[mysqld]
server-id = 2
relay-log = relay-bin
read-only = 1

从库启动复制:

sql
CHANGE MASTER TO
  MASTER_HOST='master_host',
  MASTER_USER='repl',
  MASTER_PASSWORD='password',
  MASTER_LOG_FILE='mysql-bin.000001',
  MASTER_LOG_POS=154;

START SLAVE;
SHOW SLAVE STATUS\G
-- 检查 Slave_IO_Running 和 Slave_SQL_Running 是否为 Yes

主从复制延迟问题:

原因:

  1. 从库单线程回放(MySQL 5.6 之前)。
  2. 主库写入压力大,从库追不上。
  3. 从库硬件性能差。
  4. 大事务导致从库回放慢。
  5. 网络延迟。

解决方案:

  1. 并行复制(MySQL 5.7+):
    ini
    [mysqld]
    slave_parallel_workers = 8
    slave_parallel_type = LOGICAL_CLOCK
  2. 降低主库写入压力:分库分表、读写分离。
  3. 从库硬件升级:SSD、更多 CPU。
  4. 避免大事务:分批提交。
  5. 使用 GTID 复制:简化故障切换。
  6. 监控延迟:SHOW SLAVE STATUS 中的 Seconds_Behind_Master。

延伸思考:

  • 半同步复制(Semi-Sync Replication)如何保证数据不丢失?
  • MySQL 8.0 的 Group Replication 与传统主从复制的区别?
  • 如何在线添加 MySQL 从库?

题目8:分库分表策略与实践 ​

难度: ⭐⭐⭐⭐

题目: 什么情况下需要分库分表?有哪些分片策略?分库分表后带来了哪些问题,如何解决?

标准答案:

什么情况下需要分库分表:

  1. 单表数据量过大:通常 MySQL 单表数据量超过 1000 万~2000 万行时,性能明显下降。
  2. 数据库写入压力大:单库写入成为瓶颈。
  3. 数据库成为系统瓶颈:CPU、内存、磁盘 I/O 达到上限。

分片策略:

1. 垂直拆分 ​

垂直分库:按业务模块拆分(用户库、订单库、商品库)。 垂直分表:按列拆分,将不常用的字段、大字段(TEXT/BLOB)拆分到扩展表。

2. 水平拆分 ​

范围分片(Range Sharding):

  • 按时间范围、ID 范围分片。
  • 优点:易于扩展,可以按需增加分片。
  • 缺点:数据分布不均匀,可能产生热点。

哈希分片(Hash Sharding):

  • 对分片键取模:shard_id = hash(key) % shard_count。
  • 优点:数据分布均匀。
  • 缺点:扩容时需要数据迁移(使用一致性哈希可以缓解)。

一致性哈希(Consistent Hashing):

  • 将分片节点映射到哈希环上,数据按哈希值顺时针找到最近的节点。
  • 优点:扩容时只迁移少量数据。
  • 缺点:实现复杂,可能有数据倾斜。

分库分表带来的问题:

  1. 分布式事务:跨库事务难以保证 ACID。

    • 解决方案:
      • 尽量避免跨库事务(业务层面规避)。
      • 使用最终一致性(消息队列 + 重试)。
      • 使用 Seata、ShardingSphere 的 XA 或柔性事务。
  2. 跨分片查询:无法使用 JOIN、聚合函数。

    • 解决方案:
      • 使用 ShardingSphere、Vitess 等中间件。
      • 冗余数据(空间换时间)。
      • 将数据汇总到数仓(OLAP)。
  3. 分布式 ID:自增 ID 无法跨分片唯一。

    • 解决方案:
      • 雪花算法(Snowflake):时间戳 + 机器 ID + 序列号。
      • UUID:性能差(索引碎片化)。
      • 号段模式:从数据库批量获取 ID 段。
      • Redis 生成:INCR。
  4. 扩容问题:

    • 预分片(提前规划足够多的分片)。
    • 使用一致性哈希减少迁移数据量。

延伸思考:

  • ShardingSphere 的绑定表(Binding Table)如何解决跨分片 JOIN?
  • 如何在不停机的情况下完成分库分表的扩容?
  • NewSQL(TiDB、CockroachDB)如何避免分库分表的复杂性?

题目9:窗口函数与 CTE(公共表表达式) ​

难度: ⭐⭐⭐

题目: MySQL 8.0 引入了窗口函数和 CTE,请举例说明它们的用法,并解释它们如何解决之前版本中难以编写的查询。

标准答案:

窗口函数(Window Functions) ​

窗口函数在不合并行的情况下,对一组相关行进行计算。

语法:

sql
function() OVER (
  [PARTITION BY column]
  [ORDER BY column [ASC|DESC]]
  [frame_clause]
)

常用窗口函数:

  1. 排名函数:

    • ROW_NUMBER():连续排名(1, 2, 3, 4)
    • RANK():并列排名,有跳号(1, 2, 2, 4)
    • DENSE_RANK():并列排名,无跳号(1, 2, 2, 3)
  2. 取值函数:

    • LAG(column, n, default):取前 n 行的值
    • LEAD(column, n, default):取后 n 行的值
    • FIRST_VALUE(column):取第一行的值
    • LAST_VALUE(column):取最后一行的值
  3. 聚合窗口函数:SUM()、AVG()、COUNT() 等,配合 OVER 子句使用。

示例:查询每个部门工资最高的前 3 名员工

sql
WITH ranked_employees AS (
  SELECT
    dept_name,
    emp_name,
    salary,
    ROW_NUMBER() OVER (PARTITION BY dept_name ORDER BY salary DESC) AS rn
  FROM employees e
  JOIN departments d ON e.dept_id = d.dept_id
)
SELECT dept_name, emp_name, salary
FROM ranked_employees
WHERE rn <= 3;

MySQL 5.7 及之前版本的替代方案(繁琐):

sql
-- 使用变量模拟窗口函数
SET @row_number = 0;
SET @current_dept = '';

SELECT dept_name, emp_name, salary
FROM (
  SELECT
    dept_name,
    emp_name,
    salary,
    @row_number := IF(@current_dept = dept_name, @row_number + 1, 1) AS rn,
    @current_dept := dept_name
  FROM employees e
  JOIN departments d ON e.dept_id = d.dept_id
  ORDER BY dept_name, salary DESC
) t
WHERE rn <= 3;

CTE(公共表表达式) ​

语法:

sql
WITH cte_name AS (
  SELECT ...
)
SELECT * FROM cte_name;

优势:

  1. 提高可读性:复杂查询可以拆分成多个 CTE。
  2. 支持递归:可以处理层级数据(组织架构、评论回复等)。

递归 CTE 示例:查询组织架构(员工及其下属)

sql
WITH RECURSIVE org_chart AS (
  -- 锚定成员:找到顶级领导
  SELECT id, name, manager_id, 1 AS level
  FROM employees
  WHERE manager_id IS NULL

  UNION ALL

  -- 递归成员:查找下属
  SELECT e.id, e.name, e.manager_id, oc.level + 1
  FROM employees e
  INNER JOIN org_chart oc ON e.manager_id = oc.id
)
SELECT * FROM org_chart;

延伸思考:

  • 窗口函数与 GROUP BY 的区别是什么?
  • 递归 CTE 如何避免无限递归?(使用 OPTIMIZE FOR N RECURSIVE 或限制深度)
  • CTE 与子查询、临时表的性能对比?

题目10:MySQL 8.0 JSON 支持与性能调优 ​

难度: ⭐⭐⭐

题目: MySQL 8.0 提供了哪些 JSON 功能?如何高效地查询 JSON 数据?在生产环境中使用 JSON 需要注意哪些问题?

标准答案:

MySQL 8.0 JSON 功能 ​

1. JSON 数据类型:

sql
CREATE TABLE users (
  id INT PRIMARY KEY,
  profile JSON
);

2. JSON 插入与更新:

sql
-- 插入 JSON
INSERT INTO users VALUES (1, '{"name": "Alice", "age": 30, "skills": ["Java", "SQL"]}');

-- 修改 JSON(JSON_SET、JSON_INSERT、JSON_REPLACE、JSON_REMOVE)
UPDATE users
SET profile = JSON_SET(profile, '$.age', 31, '$.email', 'alice@example.com')
WHERE id = 1;

3. JSON 查询:

sql
-- 提取 JSON 值
SELECT profile->>'$.name' AS name FROM users;  -- 返回字符串
SELECT profile->'$.age' AS age FROM users;     -- 返回 JSON(带引号)

-- 路径表达式
SELECT JSON_EXTRACT(profile, '$.skills[0]') FROM users;

-- 判断 JSON 包含
SELECT * FROM users WHERE JSON_CONTAINS(profile, '"Java"', '$.skills');
SELECT * FROM users WHERE profile->>'$.name' = 'Alice';

4. JSON 函数:

  • JSON_ARRAY()、JSON_OBJECT():构造 JSON。
  • JSON_TABLE():将 JSON 转换为关系型数据(类似 ETL)。
  • JSON_SCHEMA_VALID():验证 JSON 是否符合 JSON Schema。

高效查询 JSON 数据:

问题:对 JSON 字段的查询无法使用索引。

解决方案:生成列(Generated Columns)+ 索引

sql
-- 创建生成列
ALTER TABLE users
ADD COLUMN name VARCHAR(100) GENERATED ALWAYS AS (profile->>'$.name') VIRTUAL,
ADD COLUMN age INT GENERATED ALWAYS AS (profile->>'$.age') VIRTUAL;

-- 在生成列上创建索引
CREATE INDEX idx_name ON users(name);
CREATE INDEX idx_age ON users(age);

-- 查询时使用生成列(可以利用索引)
SELECT * FROM users WHERE name = 'Alice';

MySQL 8.0.21+ 的多值索引(Multi-Valued Index):

sql
-- 对 JSON 数组创建索引
ALTER TABLE users
ADD INDEX idx_skills ((CAST(profile->'$.skills' AS CHAR(10) ARRAY)));

-- 查询
SELECT * FROM users WHERE JSON_CONTAINS(profile->'$.skills', '"Java"');

生产环境使用 JSON 的注意事项 ​

  1. 索引限制:JSON 字段本身无法建索引,必须使用生成列。
  2. 存储空间:JSON 文本存储比规范化表占用更多空间。
  3. 查询性能:复杂 JSON 路径查询比普通列查询慢。
  4. 数据完整性:JSON 无法使用外键约束,需在应用层保证。
  5. 适用场景:
    • ✅ 适合:半结构化数据、动态属性、日志、配置。
    • ❌ 不适合:需要频繁 JOIN、聚合的核心业务数据。

性能调优通用建议:

  1. 选择合适的数据类型:使用最小的满足需求的数据类型。
  2. 建立合适的索引:避免过多索引(影响写入性能)。
  3. 优化 SQL 语句:避免 SELECT *、避免子查询嵌套。
  4. 调整 InnoDB 参数:
    • innodb_buffer_pool_size:设置为物理内存的 70%~80%。
    • innodb_log_file_size:增大 redo log,减少刷盘频率。
  5. 使用连接池:避免频繁建立连接。
  6. 监控慢查询:slow_query_log = ON,定期优化慢查询。

延伸思考:

  • MySQL 的 JSON 与 MongoDB 的文档模型相比有哪些优势和劣势?
  • 什么情况下应该选择 JSON 列而不是 EAV(Entity-Attribute-Value)模型?
  • MySQL 8.0 的直方图(Histogram)如何提升 JSON 查询性能?

总结 ​

以上 10 道面试题覆盖了 MySQL 8.0 的核心知识点,包括:

主题题目编号
InnoDB 架构1
索引优化2
事务隔离级别3
MVCC 机制4
锁机制5
SQL 优化6
主从复制7
分库分表8
窗口函数与 CTE9
JSON 支持与性能调优10

学习建议:

  1. 理解原理,而不仅仅是记忆结论。
  2. 动手实践(搭建主从复制、测试锁机制、分析执行计划)。
  3. 阅读 MySQL 官方文档(dev.mysql.com)。
  4. 学习《高性能 MySQL(High Performance MySQL)》、姜承尧的《MySQL 技术内幕:InnoDB 存储引擎》。

生成工具:OpenClaw AI Assistant
生成时间:2026-06-08 00:53