mysql面试题

基础

NOSQL和SQL的区别

SQL(关系型数据库,Relational Database)
基于表结构行列模型存储数据,使用 SQL(结构化查询语言) 操作数据,数据之间有严格的关系约束。
代表:MySQL、Oracle、PostgreSQL、SQL Server

NoSQL(非关系型数据库,Not Only SQL)
用于应对 高并发、大数据量、灵活结构 的场景,数据模型灵活,不一定用表格结构。
代表:Redis(键值型)MongoDB(文档型)Elasticsearch(搜索引擎型)HBase(列族型)

数据库三大范式

第一范式:数据表中的每一列都必须是不可再分的最小数据单元,也就是说字段的值必须是原子值

第二范式:在满足 1NF 的前提下,每个非主属性必须完全依赖于主键,而不能依赖主键的一部分

第三范式:非主属性只能依赖主键,不能依赖于另一个非主属性。

业务系统中(如电商、CRM、点餐系统),通常不会强行做到 3NF。
因为过度拆表会影响查询性能,导致频繁 JOIN。

常见做法:

  • 核心业务表遵循 3NF;
  • 查询频繁的表可适当反范式化(冗余字段、缓存冗余);

数据库的表连接

INNER JOIN(内连接): 只保留两张表中匹配的记录。最常用,查询性能比较高。

LEFT JOIN(左连接):左外连接返回左表中的所有行,即使在右表中没有匹配的行。未匹配的右表列会包含NULL。

RIGHT JOIN(右连接):右外连接返回右表中的所有行,即使左表中没有匹配的行。未匹配的左表列会包含NULL。

FULL JOIN(全连接):MySQL 不直接支持 FULL JOIN!但可以用 UNION 模拟。

CROSS JOIN(交叉连接):笛卡尔积,即两表所有组合。基本上不用。

MySQL的关键字in和exist

IN 适合子查询结果小的场景,EXISTS 适合外层数据量小的场景。NOT EXISTS 优于 NOT IN。”

对比项 IN EXISTS
语法 WHERE column IN (子查询) WHERE EXISTS (子查询)
核心逻辑 检查值是否在子查询结果集中 检查子查询是否返回至少一行(不关心具体值)
子查询执行次数 1 次(先执行子查询,缓存结果) 主查询每一行触发一次(找到匹配行后终止)
结果集要求 子查询必须返回单列结果 子查询可返回任意列(常用SELECT 1)
NULL 处理 若结果集含NULL可能导致意外结果(如NULL IN (1,2,NULL)返回NULL) 不受NULL影响,仅判断存在性
索引依赖 依赖子查询结果集的索引 依赖关联字段的索引(如orders.user_id)
适用场景 子查询结果集小(如枚举值列表)或静态查询 子查询依赖主查询字段(如关联条件),或只需判断存在性
性能特点 结果集大时内存占用高,逐行比较开销大 无需缓存结果集,适合大数据量关联(需索引支持)

MySQL中常用的基本函数

一、字符串函数
函数 说明 示例
CONCAT(s1, s2, …) 拼接字符串 CONCAT('A','B') → 'AB'
LENGTH(s) 返回字符串长度(字节) LENGTH('abc') → 3
CHAR_LENGTH(s) 返回字符数(区别于字节数) CHAR_LENGTH('你好') → 2
UPPER(s) / LOWER(s) 转为大写 / 小写 LOWER('Hello') → 'hello'
SUBSTRING(s, start, len) 截取字符串 SUBSTRING('abcdef',2,3) → 'bcd'
TRIM(s) / LTRIM(s) / RTRIM(s) 去除空格 TRIM(' abc ') → 'abc'
REPLACE(s, a, b) 替换字符串 REPLACE('abc','a','x') → 'xbc'
LOCATE(substr, str) 查找子串位置 LOCATE('b','abc') → 2

💡 实战技巧CONCAT_WS(',', col1, col2, col3) 用于拼接带分隔符的字段,非常常用。


二、数值函数
函数 说明 示例
ABS(x) 绝对值 ABS(-5) → 5
ROUND(x, d) 四舍五入到 d 位 ROUND(3.14159,2) → 3.14
CEIL(x) / FLOOR(x) 向上 / 向下取整 CEIL(3.1) → 4
MOD(a,b) 取模(求余) MOD(10,3) → 1
RAND() 随机数(0~1) RAND() → 0.4783
TRUNCATE(x, d) 截断小数位 TRUNCATE(3.14159,2) → 3.14

三、日期时间函数
函数 说明 示例
NOW() 当前日期和时间 '2025-10-27 10:23:00'
CURDATE() / CURTIME() 当前日期 / 当前时间 '2025-10-27'
DATE_FORMAT(date, format) 日期格式化 DATE_FORMAT(NOW(),'%Y-%m-%d')
YEAR(date) / MONTH(date) / DAY(date) 提取年月日 YEAR('2025-10-27') → 2025
DATEDIFF(d1, d2) 日期差(天) DATEDIFF('2025-10-27','2025-10-20') → 7
TIMESTAMPDIFF(unit, d1, d2) 按指定单位求差 TIMESTAMPDIFF(HOUR, '2025-10-20', '2025-10-21') → 24
ADDDATE(d, n) / SUBDATE(d, n) 日期加减 ADDDATE('2025-10-20',7) → '2025-10-27'

💡 实战场景
查本月注册用户 → WHERE MONTH(create_time)=MONTH(CURDATE())


四、聚合函数(统计汇总类)
函数 说明 示例
COUNT(*) 计数 COUNT(*) → 100
SUM(col) 求和 SUM(price)
AVG(col) 平均值 AVG(score)
MAX(col) / MIN(col) 最大 / 最小 MAX(age)

💡 聚合函数通常要配合 GROUP BY 使用:

SELECT department, COUNT(*) AS num 
FROM employee
GROUP BY department;

五、条件控制函数
函数 说明 示例
IF(expr, t, f) 条件判断 IF(score>60,'及格','不及格')
IFNULL(expr1, expr2) NULL 值处理 IFNULL(nickname, '匿名')
CASE WHEN ... THEN ... END 多条件分支 CASE WHEN age>60 THEN '老年' WHEN age>18 THEN '成年' ELSE '未成年' END

SQL查询语句的执行顺序是怎么样的?

FROM 确定要查询的表

ON 连接条件

JOIN 执行连接操作

WHERE 过滤行

GROUP BY 分组

HAVING 分组后过滤

SELECT 选择输出列

DISTINCT 去重

ORDER BY 排序结果

LIMIT 限制返回条数

drop、delete与truncate的区 别

DELETE:删除表中的部分或全部数据,但保留表结构。可以配合where筛选数据,逐行删除,可回滚,性能较慢

TRUNCATE :快速删除表中的全部数据,保留表结构。不可使用where,直接释放数据页,不可回滚,主键自增会重置为初始值

DROP****完全删除表(结构和数据),或其他数据库对象(如视图、索引等)。不可逆,不触发触发器,释放所有资源

存储结构与架构原理(核心底层)

如何理解存储引擎

存储引擎是 MySQL 中负责数据存储和读取的底层模块

存储引擎层:它负责执行命令、读写磁盘、管理索引、控制事务。

常见的存储引擎对比(innodb与MyISAM)

1️⃣ 事务支持(Transaction)

  • InnoDB: 支持事务(ACID),有 Redo Log(重做日志)+ Undo Log(回滚日志),崩溃可恢复。
  • MyISAM: 不支持事务,也没有日志机制,崩溃后数据可能丢失。

2️⃣ 锁机制(Locking)

  • InnoDB: 行级锁(支持 MVCC,多版本并发控制),并发性能高。
  • MyISAM: 表级锁,读写互斥,并发性能差。

3️⃣ 外键支持(Foreign Key)

  • InnoDB: 支持外键约束(Foreign Key)。
  • MyISAM: 不支持外键。

4️⃣ 索引与数据存储结构

  • InnoDB: 聚簇索引(Clustered Index),主键索引的叶子节点存储整行数据。
  • MyISAM: 非聚簇索引(索引与数据分离),索引指向数据文件。

5️⃣ 缓存机制

  • InnoDB: Buffer Pool,缓存数据页+索引页。
  • MyISAM: 只缓存索引,不缓存数据。

6️⃣ 崩溃恢复能力

  • InnoDB: 有日志系统(Redo/Undo),支持自动恢复。
  • MyISAM: 容易损坏,需要手动修复(myisamchk)。

7️⃣ 文件存储结构

文件类型 InnoDB MyISAM
数据文件 .ibd / ibdata .MYD
索引文件 存在 .ibd .MYI
表定义文件 .frm .frm

表空间、段、区、页的概念

📘 表空间 (tablespace)
├── 段 segment(数据段 / 索引段)
│ ├── 区 extent(每个1MB)
│ │ ├── 页 page(每个16KB)
│ │ │ ├── 行 row(实际记录数据)

聚簇索引、二级索引结构(B+Tree 存储)

B+Tree 是什么?

InnoDB 的索引底层不是哈希表、不是红黑树,而是 B+Tree(多路平衡查找树)

它的特点:

  1. 所有数据都存在叶子节点(非叶子节点只存键,不存值);
  2. 叶子节点通过 双向链表 连接,方便范围查询;
  3. 树高通常很低(3层能存上千万行),所以查找只需几次磁盘 I/O。

聚簇索引(Clustered Index)和 非聚簇索引(Non-Clustered Index)

聚簇索引的本质是数据的物理存储顺序与索引的逻辑排序顺序完全一致

​ 唯一性,叶子节点即数据,依赖排序

非聚簇索引的索引逻辑顺序与数据物理存储顺序无关。索引和数据是分开存储的,索引仅作为 “指针” 指向数据的位置。

​ 可多个存在,叶子节点是“指针”,数据无序

什么是“回表查询”

当查询使用的是 二级索引(非主键索引),而 SQL 需要的字段不在该索引中时,MySQL 会先通过二级索引找到主键值,再根据主键去 聚簇索引 中取完整数据的过程,就叫做“回表查询”。

覆盖索引(Covering Index)不需要回表,查询的字段都在索引上面

Redo Log、Undo Log、Binlog 的区别与作用

三个的区别:

日志类型 产生层 作用 持久化对象
Redo Log InnoDB 引擎层 实现 **事务的持久性 (Durability)**,用于崩溃恢复 物理日志(记录“数据页修改了什么”)
Undo Log InnoDB 引擎层 实现 **事务的原子性 (Atomicity)**,用于回滚、MVCC 逻辑日志(记录“怎么撤销这次操作”)
Binlog Server 层 实现 主从复制、数据恢复 逻辑日志(记录 SQL 语句或行变更)

Redo Log

记录事务对数据页的物理修改,保证“即使 MySQL 崩溃,已提交的事务也不会丢”。物理日志。

当事务执行修改时:

  1. 修改 InnoDB 缓冲池(内存页)
  2. 将修改内容记录到 Redo Log Buffer
  3. 事务提交时,Redo Log 写入磁盘(ib_logfile0 / ib_logfile1);
  4. 若系统崩溃,启动时根据 Redo Log 进行重放恢复。

Undo Log

记录数据被修改前的旧值,用于:

  • 事务回滚;
  • MVCC(多版本并发控制)中的一致性读。

InnoDB 会:

  1. 在执行前,记录旧值到 Undo Log;
  2. 执行更新;
  3. 若事务回滚,则根据 Undo Log 恢复旧值;
  4. Undo Log 保留一段时间,用于 快照读

Binlog(逻辑日志)

记录所有数据库更改操作,用于:

  • 主从复制;
  • 数据恢复(Point-in-Time Recovery)。

MVCC(多版本并发控制)实现原理

MVCC 通过“保存历史版本”实现了非阻塞读,即事务读取的是旧版本的数据,不需要等锁。

MVCC 的核心在于:隐藏的系统字段 + Undo Log + Read View(读视图)

1. 每行数据的隐藏字段

InnoDB 在每行数据中隐藏维护了三个字段:

  • DB_TRX_ID:最后一次修改该行的事务 ID;
  • DB_ROLL_PTR:指向 undo log(回滚日志)的指针;
  • DB_ROW_ID:行的唯一标识(如果没有主键时才会用)。

这些字段让系统知道:这行是谁改的、何时改的、上一个版本在哪。

2. Undo Log(回滚日志)

每当一个事务对数据进行 UPDATEDELETE 时,InnoDB 都会在 Undo Log 中记录旧版本数据。
Undo Log 本质上是“时间倒流的链条”,让系统可以回溯出任意历史版本的数据。

换句话说:Undo Log = 旧版本的快照
这也是 MVCC 能读到“历史数据”的关键。

Read View 是事务在执行“快照读”时生成的视图,用来判断:

当前事务能看到哪些版本的数据。

Read View 主要包含:

  • 当前系统中活跃的事务 ID 列表(未提交的事务);
  • 视图中最小的事务 ID;
  • 当前事务自己的 ID。

当事务执行查询时,InnoDB 根据 Read View 规则判断一行是否可见:

  1. 如果数据的 DB_TRX_ID < Read View 中最小事务 ID ⇒ 可见;
  2. 如果数据的 DB_TRX_ID 在活跃事务列表中 ⇒ 不可见;
  3. 否则,可见的是最近一次提交前的版本(通过 Undo Log 回溯)。

索引与查询优化(面试高频)

什么是索引

在 MySQL 中,索引是存储引擎(比如 InnoDB)在表数据之外维护的一种特殊数据结构,它会保存某些列的值以及这些值对应的物理数据位置,从而使查找更高效。

索引的类型有哪些

MySQL 的索引可以从四个角度分类:
① 按数据结构:B+Tree 索引、Hash 索引、Full-Text 索引;
② 按物理存储:聚簇索引(主键索引)与二级索引(辅助索引);
③ 按字段特性:主键索引、唯一索引、普通索引、前缀索引;
④ 按字段个数:单列索引、联合索引。
其中 InnoDB 默认使用 B+Tree 实现索引,主键索引是聚簇索引,其他都是二级索引。
合理设计索引能极大提升查询性能,但也要权衡写入成本和维护开销。

索引的优缺点

优点:

  • 提高检索速度:索引可以显著加快数据检索的速度
  • 加速排序和分组操作
  • 加快多表连接操作的执行效率

缺点:

  • 占用存储内存
  • 维护成本增加
  • 不适用于所有查询

索引的使用场景

  • 主键和外键字段
  • 离散度比较高的字段
  • 存储空间小的字段
  • 经常出现在查询条件的字段
  • 用于排序和分组的字段
  • 表连接的字段

B+ 树为什么比 B 树更适合数据库索引?

B 树(Balanced Tree)
每个节点既存键(key)**也存**数据(data)**,
所以查找时可能在
中间节点**就能命中结果。

B+ 树
只有叶子节点存数据;
非叶子节点只存键值,用于索引路径;
所有叶子节点通过双向链表连接。

MySQL 采用 B+ 树作为索引结构,是因为它更适合磁盘存储特性。与 B 树相比,B+ 树的非叶节点不存储数据,单页能容纳更多索引键,树高更低,减少磁盘 IO;所有数据都存放在叶子节点,查询路径长度一致,性能更稳定;叶子节点通过链表相连,支持高效的范围查询和顺序扫描;同时符合磁盘预读机制,非常适合数据库场景。

索引下推

当查询涉及 索引列和非索引列的条件 时,InnoDB 会尽量在 索引扫描阶段 先把过滤条件应用到索引列上,而不是把所有索引查到的行都读取到主存后再过滤,从而减少 回表(访问行数据) 的次数。

ICP 优势:

  • 减少回表行数:尤其是索引命中行很多,但最终返回行少时。
  • 减少 IO:回表操作通常比索引扫描成本高。
  • 提高查询性能:在大表、宽表(列多)中优势明显。

索引失效的场景

“MySQL 索引失效的常见场景包括:OR 条件、LIKE 前置 %、联合索引未使用最左前缀、索引列被函数或表达式处理、不等操作符、NOT IN/NOT EXISTS、索引列存在大量 NULL 值、表数据量少导致优化器选择全表扫描、以及隐式类型转换。优化方法包括拆分 OR 查询、前缀匹配、调整联合索引或单列索引、使用范围查询、保证类型一致等。”

查询条件包含or,可能导致索引失效

使用like模糊查询 以百分号开头 可能导致索引失效

联合索引时 违反了最左前缀匹配原则

索引列上使用了函数或表达式

索引上进行列运算

索引字段上使用!=或者 <>

使用NOT IN 或者NOT EXISTS

索引列上存在过多的null 或使用null is not null 可能导致索引失效

表中数据量较少 直接全表扫描而不采用索引

查询条件中列与索引列的类型不匹配 数据库隐式了类型转换时 可能导致索引失效

MySQL慢查询怎么解决?

首先需要确认哪些 SQL 语句执行缓慢。通过配置 MySQL 的慢查询日志来捕获执行时间超过阈值的 SQL。

-- 临时开启慢查询日志(重启MySQL后失效)
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1; -- 设置阈值为1秒
SET GLOBAL log_queries_not_using_indexes = 'ON'; -- 记录未使用索引的查询

-- 查看慢查询日志文件位置
SHOW VARIABLES LIKE 'slow_query_log_file';

使用EXPLAIN关键字分析慢查询的执行计划,查看是否使用了索引、扫描行数、连接类型等:

1. id

  • 含义:查询中每个操作的唯一标识符,代表执行顺序。

  • 规则:

    • id 相同:执行顺序由上至下(通常是表连接)。
    • id 不同:值越大优先级越高,先执行。
    • 存在 NULL:表示这是一个结果集,不对应实际表操作(如临时表聚合)。

2. select_type

  • 含义:表示查询类型,判断是简单查询还是复杂查询。

  • 常见值:

    • SIMPLE:简单查询,无 UNION 或子查询。
    • PRIMARY:复杂查询中的主查询。
    • UNION:UNION 后的第二个及后续查询。
    • SUBQUERY:子查询中的第一个 SELECT。
    • DERIVED:派生表(如 FROM 子句中的子查询)。
    • MATERIALIZED:物化子查询(结果被临时存储)。

3. table

  • 含义:当前行正在访问的表名(或别名)。

  • 特殊值:

    • :表示引用 id=M 和 id=N 的 UNION 结果。
    • :表示引用 id=N 的派生表。

4. partitions

  • 含义:查询涉及的分区(仅对分区表有效)。
  • 作用:判断是否有效利用了分区 pruning(分区裁剪)。

5. type(重要)

  • 含义:表连接类型(访问类型),最关键的字段之一,决定查询效率。

  • 性能从优到差排序:

    • system:表只有一行(如系统表),是 const 的特例。
    • const:通过主键或唯一索引查询,最多返回一行。
    • eq_ref:多表连接时,被驱动表通过主键或唯一索引匹配,每行只匹配一次。
    • ref:非唯一索引扫描,返回匹配某个值的多行。
    • range:索引范围扫描(如 WHERE id BETWEEN 1 AND 10)。
    • index:全索引扫描(比 ALL 快,因为索引文件通常比数据文件小)。
    • ALL:全表扫描(性能最差,需优化)。
  • 优化目标:至少达到 range 级别,最好是 ref 或 const。

6. possible_keys

  • 含义:MySQL 认为可能适用的索引(但不一定实际使用)。
  • 作用:若为 NULL,表示没有可用索引,可能需要添加索引。

7. key(重要)

  • 含义:MySQL 实际使用的索引。

  • 分析:

    • 若为 NULL,表示未使用索引(可能走全表扫描)。
    • 若 possible_keys 有值但 key 为 NULL,可能因索引选择性低(如字段值重复率高)被优化器放弃。

8. key_len(重要)

  • 含义:使用的索引长度(字节数),表示索引被利用的程度。

  • 作用:

    • 越长说明索引使用越充分(如联合索引中使用的字段越多)。
    • 可判断联合索引是否被完全利用(如 key_len 等于所有字段长度之和)。

9. ref

  • 含义:表示哪些列或常量被用于匹配索引列。

  • 示例:

    • const:使用常量匹配索引。
    • tbl.col:使用其他表的列匹配索引(如连接查询)。

10. rows

  • 含义:MySQL 预估需要扫描的行数(非精确值)。
  • 作用:数值越小越好,可判断索引是否有效(如索引优化后 rows 应显著减少)。

11. filtered

  • 含义:经过条件过滤后,剩余记录占总扫描行数的百分比(0-100)。
  • 分析:值越低,说明过滤效果越好(如 WHERE 条件较严格)。

12. Extra(重要)

  • 含义:额外信息,非常重要,包含优化器的关键决策。

  • 常见重要值:

    • Using index:覆盖索引(仅使用索引即可获取数据,无需回表),性能极佳。
    • Using where:使用 WHERE 条件过滤,但未使用索引(可能需优化)。
    • Using index condition:索引条件下推(ICP),减少回表次数。
    • Using filesort:需额外排序(非索引排序),性能差,可通过索引优化避免。
    • Using temporary:使用临时表存储中间结果(如 GROUP BY 无合适索引),性能差。
    • Using join buffer:未使用索引连接,使用连接缓冲区,需优化。
    • Range checked for each record:未找到合适索引,需逐行判断,性能极差。
  • 索引缺失或错误是慢查询的常见原因。根据查询条件添加合适的索引

  • 索引优化原则:

      • 最左前缀匹配:联合索引需遵循查询条件的顺序。
      • 避免冗余索引:如已有(a, b)索引,无需单独创建(a)索引。
      • 索引选择性:选择区分度高的字段(如user_id)作为索引。
  • 优化 SQL 语句

      • 避免全表扫描:确保查询条件有索引支持。
      • 减少子查询:用JOIN替代子查询。
      • 避免函数或表达式操作
      • 分批处理大查询:对大量数据的操作分批次执行。
  • 优化表结构:

  • 拆分大表:垂直分表(按字段拆分)和水平拆分(分库分表)

  • 使用合适得数据类型

事务与锁机制(数据库核心逻辑)

什么是事务?

一系列操作的集合,事务是处理数据操作的最小逻辑单元。

事务的四大特性(ACID)

事务的四大特性(ACID)分别是原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability) 。

原子性指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败回滚;

一致性表示事务执行前后,数据库的完整性约束没有被破坏,数据从一个正确状态转换到另一个正确状态;

隔离性指多个事务并发执行时,一个事务的执行不能被其他事务干扰,各个事务之间相互隔离;

持久性意味着一旦事务提交,它对数据库中数据的改变就是永久性的,即使系统出现故障也不会丢失。

原子性和持久性如何保证

原子性主要依赖于undo日志(回滚日志)

当事务执行修改操作(如 INSERT/UPDATE/DELETE)时,InnoDB 会先将修改前的 “旧数据” 写入 undo 日志,若事务执行过程中出现错误(如代码异常、数据库崩溃),数据库会通过 undo 日志 反向执行操作,将数据恢复到事务开始前的状态。

持久性主要依赖于redo日志(重做日志)

redo 日志 记录了 “数据页的物理修改”,先写 redo 日志到磁盘,再修改内存中的数据页

事务中的隔离性是如何保证的呢?(你解释一下MVCC)

​ 数据库通过锁机制MVCC(多版本并发控制) 共同保证隔离性,其中 MVCC 是 InnoDB 引擎实现高并发读写的核心机制,尤其在「读已提交(RC)」和「可重复读(RR)」隔离级别中发挥关键作用。

什么是 MVCC?

MVCC(Multi-Version Concurrency Control,多版本并发控制)的核心思想是:为数据保存多个版本,不同事务在并发访问时,根据自身的「版本可见性规则」读取对应版本的数据,从而实现 “读写不冲突”(读操作无需加锁等待写操作,写操作也无需阻塞读操作),大幅提升并发性能。

InnoDB 通过「隐藏列」「undo 日志」「Read View」三者配合,实现了 MVCC 的版本管理和可见性判断。

InnoDB 的每行数据除了用户定义的字段外,还隐含 3 个隐藏列,记录最后一次修改该数据的「事务 ID」;「回滚指针」:指向该数据的上一个版本;如果表没有主键或唯一索引,InnoDB 会用这个自增 ID 作为聚类索引的键

当事务修改数据时,InnoDB 会先把修改前的旧数据存入「undo 日志」( undo log),并通过DB_ROLL_PTR将新数据与旧数据串联成「版本链」。

当事务执行「读操作」时,InnoDB 会生成一个「Read View」(读视图),用于判断版本链中哪个版本的数据对当前事务 “可见”。

  • m_ids:当前活跃(未提交)的事务 ID 列表。
  • min_trx_idm_ids中最小的事务 ID(当前最早活跃的事务)。
  • max_trx_id:当前系统中尚未分配的下一个事务 ID(即未来事务的 ID 会≥这个值)。
  • creator_trx_id:生成该 Read View 的事务自身的 ID。

只有 “已提交的旧事务” 或 “自身事务” 修改的版本才可见,未提交的事务或未来的事务版本不可见。

读已提交(RC):每次执行「SELECT」时,都会重新生成一个 Read View

可重复读(RR):只在事务中第一次执行 SELECT 时生成 Read View,后续所有查询复用这个 Read View。

幻读、脏读、不可重复读

并发事务问题:

脏读 :一个事务读到另外一个事务还没提交得数据

不可重复读 :一个事务先后读取同一条记录,但两次读取得数据不同,

幻读 :一个事务内多次执行同一查询(如统计符合条件的行数),但结果行数不一致(因中间被另一个事务提交的 “新增 / 删除” 操作干扰)。

事务隔离级别(读未提交、读已提交、可重复读、串行化)

我们一般使用的都是mysql默认的隔离级别:可重复读

image-20251027205209233

数据库中的锁

数据库锁是为了 保证并发访问时的数据一致性和完整性,主要解决 脏读、不可重复读、幻读 等问题。

锁的核心目标:控制并发访问,保证事务隔离性。

按粒度划分:表锁、行锁、页锁

按锁类型划分:共享锁、排他锁、

死锁产生的原因与定位方式(SHOW ENGINE INNODB STATUS

死锁是指两个或多个事务在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉,这些事务将永远无法推进下去。

解决死锁的方法

  • 设置死锁检测和超时机制

    • 死锁检测:MySQL 默认开启死锁检测,当检测到死锁时,会自动选择一个牺牲者(通常是回滚开销最小的事务),回滚该事务,释放其持有的锁,让其他事务继续执行
    • 超时机制:可以设置事务等待锁的超时时间(通过参数innodb_lock_wait_timeout)。当一个事务等待锁的时间超过这个设定值时,该事务会自动回滚。
  • 优化事务逻辑

    • 按相同顺序访问资源:确保多个事务以相同的顺序访问资源,避免循环等待。
    • 减少事务持有锁的时间:尽量缩短事务的执行时间,在获取锁后尽快完成操作并释放锁。
  • 合理设计数据库架构

    • 锁粒度调整:如果可能,尽量使用粒度更细的锁。
    • 拆分大事务:将一个大事务拆分成多个小事务,减少单个事务占用资源的时间和范围。

百万级别或以上的数据如何删除

分批删除(Batch Delete)

核心思想:一次只删除少量行,循环执行,避免大事务。

分区表删除(Partition Drop)

如果是 大表、按时间或业务字段分区

  • 优点:删除速度极快,几乎是元数据操作
  • 条件:表必须按删除条件建分区
  • 适合场景:按日期、地区等定期清理数据

使用临时表 + 交换(Swap Table)

  • 优点:不会触发大事务日志
  • 缺点:需要额外空间

逻辑删除(Soft Delete)

  • 优点:快速,不阻塞业务
  • 缺点:表会越来越大,需要定期物理清理

数据库优化

MySQL 优化从多个层面进行:

  • 架构设计:表设计、分库分表、读写分离等
  • SQL 查询优化:合理设计索引、避免全表扫描、优化查询结构
  • 存储引擎与配置优化:内存管理、数据库参数配置、引擎选择
  • 监控与运维:性能监控、日志管理、备份恢复等

索引优化:

  • 合理的创建索引
  • 尽量使用覆盖索引

查询语句优化:

  • 避免全表扫描
  • 优化连接查询
  • 使用合适的聚合函数

数据类型优化:

  • 使用合适的数据类型,根据数据的实际范围和用途,使用占用空间小,查询效率高的数据结构
  • 尽量避免使用TEXT和BLOB类型。效率低

数据库设计:

  • 合理分表,根据数据的特点和查询需求,进行大表拆分

定期维护数据库

  • 清理无用的数据
  • 优化表结构
  • 备份数据库

超大分页怎么处理?

在数据库查询中,“超大分页” 通常指分页查询的页码极大(如第 10000 页、第 10 万页),此时传统的 LIMIT offset, size 方式会出现严重的性能问题(如查询缓慢、IO 负载高)。数据库需要扫描大量无用得数据后丢弃,效率极低。

1. 基于 “上一页最后一条数据” 的游标分页(推荐)

利用索引有序性,通过 “上一页的最后一条记录的标记”(如主键 ID、时间戳)作为条件,直接定位下一页的起始位置,替代 offset

适用场景

  • 分页字段是唯一且有序的(如自增主键 id、带唯一约束的时间戳 create_time + id)。
  • 业务允许 “只能上一页 / 下一页翻页”,不支持 “直接跳转到第 N 页”(如 APP 列表的加载更多)。

2. 限制最大页码(业务层面优化)

从业务角度避免超大分页场景,例如:

  • 规定最大可访问页码(如最多显示前 100 页),超过则提示 “数据过多,请缩小查询范围”。
  • 引导用户通过筛选条件(如时间范围、分类)缩小数据量,而非无限制分页。

3. 分表分库场景:按分片键定位

若数据已分表分库(如按 id 范围分片),可通过分片键计算目标页数据所在的分片,仅在对应分片内查询,减少跨分片扫描。

MySQL 高并发环境解决方案?

MySQL 高并发环境解决方案 分库 分表 分布式 增加二 级缓存。。。。。

需求分析:互联网单位 每天大量数据读取,写入,并发 性高。

现有解决方式:

  • 水平分库分表,由单点分布到多点数 据库中,从而降低单点数据库压力。
  • 集群方案:解决DB宕机带来的单点DB不能访问问 题。
  • 读写分离策略:极大限度提高了应用中Read数据的速 度和并发量。无法解决高写入压力。

书写SQL的建议

1.尽量不用select *,而是具体的字段

2.如果知道结果条数只有一条建议用limit 1

3.尽量避免在where子句中使用or连接条件,建议使用union all

4.使用like时不用%开头,容易造成索引失效

5.使用where条件限定要查询的数据,避免返回多余的行

6.尽量避免在索引上使用内置的函数

7.应尽量避免在 where 子句中对字段进行表达式操作,这将导致系统放弃使用索引而进行全表扫描

8.Inner join 、left join、right join,优先使用Inner join,如果是left join,左边表结果尽量小

9.避免在where子句中使用!=或者<>,可能导致索引失效

10.使用联合索引时,要保证最左匹配原则

11.对查询进行优化时,尽量在where和order by涉及的列上面建立索引,避免全表扫描

12.如果插入过多 可以考虑批量插入

13.在适当的时候使用覆盖索引,

14.字段很多的时候,避免使用distinct,会降低查询效率

15.删除冗余和重复的索引

16.数据量过大时,可以优化删除语句。可以分批删除

17.where子句中考虑使用默认值代替null

18.不要有五个以上的表连接

19.合理使用exist和in

20.尽量用union all(去重)代替 union

21.索引一般五个以内 不是越多越好

22.能用数字型的字段不用字符型

23.索引不建立在重复度高的字段上

24.尽量避免向客户端返回过多的数据

25.当在SQL语句中连接多个表时,请使用表的别名,并把别名前缀于每一列上,这样语义更加清晰。

26.尽可能使用varchar/nvarchar 代替 char/nchar。

27.为了提高group by 语句的效率,可以在执行到该语句前,把不需要的记录过滤掉。

28.如果字段类型是字符串,where时一定用引号括起来,否则索引失效

29.使用explain 分析你SQL的计划