Files
blog-press/docs/Web/MySQL/Summary.md
2026-05-27 14:17:04 +08:00

35 KiB
Raw Blame History

title, date
title date
MySQL知识点 2025-11-26

一、基础知识

1.1 数据类型

1.1.1 汇总

类型 存储空间 范围 适用场景
TINYINT 1字节 -128 ~ 127 状态码、年龄、布尔值
SMALLINT 2字节 -32,768 ~ 32,767 小范围计数、年份
MEDIUMINT 3字节 -8,388,608 ~ 8,388,607 中型ID、访问量统计
INT/INTEGER 4字节 -2^31 ~ 2^31-1 用户ID、订单号常用
BIGINT 8字节 -2^63 ~ 2^63-1 分布式ID、大数据量计数
DECIMAL(M,D) 变长M+2字节 金融金额、精确计算
FLOAT 4字节 科学测量、非精确计算
DOUBLE 8字节 地理坐标、高精度计算
CHAR(M) 255字符 固定长度编码、MD5哈希
VARCHAR(M) 65,535字节 用户名、地址等变长数据
TINYTEXT 255字节 短标题、简介
TEXT 65,535字节 文章内容、评论
MEDIUMTEXT 16MB (2^24-1) 博客文章、产品描述
LONGTEXT 4GB (2^32-1) 电子书、大型文档
DATE 纯日期
TIME(fsp) 可指定微秒精度(0-6)
DATETIME(fsp) 高精度时间记录
TIMESTAMP(fsp) 自动时区转换4字节存储
ENUM 性别、状态等有限选项
SET 用户兴趣、文章标签
TINYBLOB 255字节 微小二进制对象
BLOB 65KB 标准二进制对象
MEDIUMBLOB 16MB 中等二进制对象
LONGBLOB 4GB 超大二进制对象

1.1.2 法则

整数选择优先INT大数量用BIGINT布尔值用TINYINT(1)或BIT(1)
小数选择金融金额必须用DECIMAL非精确测量可用FLOAT/DOUBLE
字符串选择定长编码用CHAR变长文本用VARCHAR大文本用TEXT系列
时间选择日期用DATE精确时间用DATETIME自动更新用TIMESTAMP
特殊场景多选项用SET结构化数据用JSON

1.2 常用函数

1.2.1 字符串函数

  1. CONCAT(str1, str2, ...)​​:连接字符串
SELECT CONCAT('Hello', ' ', 'World');
  1. LENGTH(str):返回字符串的长度(字节数)
SELECT LENGTH('Hello');
  1. SUBSTRING(str, pos, len):从字符串 str 的 pos 位置开始,截取长度为 len 的子字符串。
SELECT SUBSTRING('Hello World', 7, 5); -- 返回 'World'

1.2.2 日期/时间函数

  1. NOW()返回当前日期和时间yyyy-MM-DD HH:mm:ss
  2. CURDATE()返回当前日期不带时间部分yyyy-MM-DD
  3. DATE_FORMAT(date, format):按照指定的格式返回日期值。
SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s');
SELECT DATE_FORMAT(NOW(), '%Y-%m-%d');
  1. DATEDIFF(date1, date2):返回两个日期之间的天数差。
SELECT DATEDIFF('2024-12-31', '2024-01-01'); -- 返回364
  1. YEAR(date) / MONTH(date) / DAY(date):分别返回日期的年、月、日部分。
  2. TIME(date):从日期或时间值中提取时间部分。
  3. TIMESTAMPDIFF(unit, datetime1, datetime2):返回两个日期或时间的差值,单位可以是 SECOND, MINUTE, HOUR, DAY, MONTH, YEAR 等。
  4. STR_TO_DATE(str, format):根据给定的格式将字符串转换为日期。
SELECT STR_TO_DATE('01-09-2024', '%d-%m-%Y'); -- 返回 '2024-09-01'

1.2.3 数值函数

  1. ROUND(x, d):将数值 x 四舍五入到 d 位小数。
SELECT ROUND(123.4567, 2); -- 返回 123.46
  1. FLOOR(x) / CEIL(x):返回小于或等于 x 的最大整数(向下取整)或大于或等于 x 的最小整数(向上取整)。
SELECT FLOOR(2.9); -- 返回 2
SELECT CEIL(2.1);  -- 返回 3

1.2.4 聚合函数

  1. COUNT(expression):返回某列中的记录数。
  2. SUM(expression):返回某列中数值的总和。
  3. AVG(expression):返回某列中数值的平均值。
  4. MAX(expression) / MIN(expression):返回某列的最大值或最小值。

1.2.5 控制流函数

  1. IF(condition, true_value, false_value):如果 condition 为真,返回 true_value否则返回 false_value。
SELECT IF(1 > 0, 'Yes', 'No'); -- 返回 'Yes'
  1. CASE用于条件判断类似于多路选择。
SELECT 
  CASE 
    WHEN salary > 10000 THEN 'High'
    WHEN salary BETWEEN 5000 AND 10000 THEN 'Medium'
    ELSE 'Low'
  END AS salary_range
FROM employees;

二、索引

  索引是对数据库表中的一列或多列值进行排序的一种结构,使用索引可以快速访问数据库表中的特定信息。
索引相当于图书上的目录,可以根据目录上的页码快速找到所需的内容,提高性能(查询速度),相当于用空间换时间。

2.1 优缺点

优点:

  • 查询速度起飞 (主要目的) :通过索引,数据库可以大幅减少需要扫描的数据量,直接定位到符合条件的记录,从而显著加快数据检索速度,减少磁盘 I/O 次数。
  • 保证数据唯一性:通过创建唯一索引 (Unique Index) 可以确保表中的某一列或几列组合的值是独一无二的比如用户ID、邮箱等。主键本身就是一种唯一索引
  • 加速排序和分组:如果查询中的 ORDER BY 或 GROUP BY 子句涉及的列建有索引,数据库往往可以直接利用索引已经排好序的特性,避免额外的排序操作,从而提升性能。

缺点:

  • 创建和维护耗时:创建索引本身需要时间,特别是对大表操作时。更重要的是,当对表中的数据进行增、删、改 (DML操作) 时,不仅要操作数据本身,相关的索引也必须动态更新和维护,这会降低这些 DML 操作的执行效率
  • 占用存储空间:索引本质上也是一种数据结构,需要以物理文件(或内存结构)的形式存储,因此会额外占用一定的磁盘空间。索引越多、越大,占用的空间也就越多。
  • 可能被误用或失效:如果索引设计不当,或者查询语句写得不好,数据库优化器可能不会选择使用索引(或者选错索引),反而导致性能下降。

2.2 适用场景

适用场景

  • 频繁作为查询条件的字段应该创建索引
  • 查询中排序的字段创建索引将大大提高排序的速度(索引就是排序加快速查找)
  • 查询中统计或者分组的字段

不适用场景

  • 频繁更新的字段不适合创建索引,因为每次更新不单单是更新记录,还会更新索引,保存索引文件
  • 表记录太少,不需要创建索引
  • 数据重复且分布平均的字段,因此为经常查询的和经常排序的字段建立索引。注意某些数据包含大量重复数据,因此他建立索引就没有太大的效果,例如性别字段,只有男女,不适合建立索引。

2.3 数据结构

  在 MySQL 中MyISAM 引擎和 InnoDB 引擎都是使用 B+Tree 作为索引结构。 暂无图片 例如要查找id=75的用户SELECT * FROM users WHERE id = 75
  查找步骤:

  1. 从根节点开始75在50~100之间走中间分支
  2. 到达叶子节点找到id=75的数据位置
  3. 根据位置直接获取完整的用户数据

整个过程只需要3次IO操作而全表扫描可能需要很多次。

特点 优势 实际效果
多路平衡 树的高度很低 减少磁盘访问次数
叶子节点连接 支持范围查询 ORDER BY、分页查询快
只在叶子存数据 内部节点小 更多索引数据放入内存

2.4 索引类型

  1. 主键索引
    主键索引是最特殊的索引。
CREATE TABLE users (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(50),
  email VARCHAR(100)
);

SELECT * FROM users WHERE id = 12345; 
  1. 唯一索引
CREATE UNIQUE INDEX idx_email ON users(email);

  插入重复的邮箱会报错

  1. 普通索引
CREATE INDEX idx_name ON users(name);

SELECT * FROM users WHERE name = "张三";
  1. 联合索引
    多个字段组合的索引​
CREATE INDEX idx_name_age_city ON users(name, age, city);

SELECT * FROM users WHERE name = '张三';                      
SELECT * FROM users WHERE name = '张三' AND age = 25;         
SELECT * FROM users WHERE name = '张三' AND age = 25 AND city = '北京'; 
SELECT * FROM users WHERE age = 25;         -- 无法使用索引
SELECT * FROM users WHERE city = '北京';    -- 无法使用索引

联合索引的使用最左前缀原则。最左前缀原则指的是查询从索引的最左列开始不能跳过索引中的列。比如有A、B、C这3列作为索引则查询语句中必须是A、AB、AC、ABC不能是BC这样的跳过了ASQL语句中字段的位置可以任意但一定要包含比如CBA顺序也可以。
  联合索引中,出现范围查询(<, >),范围查询右侧的列索引失效。可以用>=或者<=来规避索引失效问题。
CREATE INDEX idx_name_age_city ON users(name, age, city);相当于创建了3个索引users(name)users(name, age)users(name, age, city)​。

::: tip 在 InnoDB 存储引擎中,根据索引的存储形式,又可以分为以下两种:

分类 含义 特点
聚集索引 将数据存储与索引放在一起,索引结构的叶子节点保存了行数据 必须有,而且只有一个
二级索引 将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键值 可以存在多个

  聚集索引就是主键索引,其他的都是二级索引,不能存放所有的数据。
  比如查询select * from user where name = 'Arm';时,虽然使用了索引,但是只能查到Arm对应的user_id主键值,还需要根据主键值去查找完整的行记录。
:::

2.5 索引设计

  • 为WHERE条件添加索引
SELECT * FROM orders WHERE user_id = 123;
SELECT * FROM orders WHERE status = 'paid';
SELECT * FROM orders WHERE create_time > '2024-01-01';

CREATE INDEX idx_user_id ON orders(user_id);
CREATE INDEX idx_status ON orders(status);
CREATE INDEX idx_create_time ON orders(create_time);
  • 为ORDER BY字段添加索引
SELECT * FROM articles ORDER BY create_time DESC LIMIT 10;

CREATE INDEX idx_create_time ON articles(create_time);
  • 复合索引的顺序很关键
SELECT * FROM users WHERE city = '北京' AND age > 25 ORDER BY create_time;

-- 索引字段顺序应该是:过滤性强的字段在前
CREATE INDEX idx_city_age_create_time ON users(city, age, create_time);
  • 限制每张表上的索引数量,建议单张表索引不超过 5 个。
  • 被频繁更新的字段应该慎重建立索引,虽然索引能带来查询上的效率,但是维护索引的成本也是不小的。 如果一个字段不被经常查询,反而被经常修改,那么就更不应该在这种字段上建立索引了。
  • 尽可能的考虑建立联合索引而不是单列索引。因为索引是需要占用磁盘空间的,可以简单理解为每个索引都对应着一颗 B+ 树。如果一个表的字段过多,索引过多,那么当这个表的数据达到一个体量后,索引占用的空间也是很多的,且修改索引时,耗费的时间也是较多的。如果是联合索引,多个字段在一个索引上,那么将会节约很大磁盘空间,且修改数据的操作效率也会提升。

2.6 索引优化

  1. 监控慢查询
-- 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;  -- 超过1秒的查询记录下来

-- 查看慢查询
SHOW VARIABLES LIKE 'slow_query_log_file';
  1. 合理使用前缀索引
-- 对于很长的字符串字段,使用前缀索引
CREATE INDEX idx_title_prefix ON articles(title(20));  -- 只索引前20个字符

三、事务

  事务是数据库操作的基本单位,它是一组原子性的 SQL 语句,或者说是一个独立的工作单元。事务内的所有操作要么全部成功,要么全部失败

3.1 特性

  • 原子性Atomicity​):原子性确保事务中的所有操作要么全部完成,要么全部不完成。如果事务执行过程中发生错误,所有已执行的操作都会回滚。
START TRANSACTION;
    INSERT INTO orders (user_id, amount) VALUES (1, 100);
    UPDATE inventory SET stock = stock - 1 WHERE product_id = 1;
    -- 如果任何一步失败,整个事务都会回滚
COMMIT;
  • 一致性Consistency​):一致性确保数据库从一个一致的状态转换到另一个一致的状态。事务执行前后,数据库的完整性约束不会被破坏。
START TRANSACTION;
    -- 确保账户余额不会出现负数
    UPDATE accounts SET balance = balance - 100 
    WHERE id = 1 AND balance >= 100;
    UPDATE accounts SET balance = balance + 100 
    WHERE id = 2;
COMMIT;
  • 隔离性Isolation​):隔离性确保并发执行的事务之间不会相互影响。每个事务都感觉不到其他事务的存在。
-- 事务1
START TRANSACTION;
    SELECT balance FROM accounts WHERE id = 1;
    -- 其他事务的修改不会影响这个查询结果
COMMIT;

-- 事务2
START TRANSACTION;
    UPDATE accounts SET balance = balance + 100 WHERE id = 1;
COMMIT;
  • 持久性Durability​):持久性确保一旦事务提交,其所做的修改就会永久保存到数据库中。
START TRANSACTION;
    INSERT INTO logs (message) VALUES ('重要操作');
COMMIT;
-- 提交后,数据已经持久化到磁盘

3.2 隔离级别

3.2.1 概念

  • 脏读 (Dirty Read): 事务A读取了事务B尚未提交的修改数据。如果事务B最终回滚事务A读到的就是无效的"脏"数据。
  • 不可重复读 (Non-Repeatable Read): 在同一个事务A中两次读取同一行数据得到的结果不同。这是因为在两次读取之间该行数据被另一个提交了的事务B修改了。
  • 幻读 (Phantom Read): 在同一个事务A中两次执行相同的查询(通常是范围查询 SELECT ... WHERE ...​),得到的结果集行数不同(出现了新的"幻影"行或原有行消失了。这是因为在两次查询之间另一个提交了的事务B插入了满足查询条件的新行或删除了原有的行。

3.2.2 类型

  1. 读未提交 (READ UNCOMMITTED)

    • 特点: 这是最低的隔离级别,允许读取尚未提交的数据变更。
    • 允许的问题:
      • 脏读: 事务可以读取其他事务尚未提交的修改。
      • 不可重复读: 可能发生。
      • 幻读: 可能发生。
    • 并发性: 最高。因为它几乎不加锁(或者锁持有时间非常短),事务之间等待最少。
    • 数据一致性: 最差。读取的数据可能是临时的、无效的(如果其他事务回滚)或中间状态。
    • 使用场景: 非常罕见,通常仅在对数据准确性要求极低、需要极高吞吐量且能容忍脏数据的统计类场景(如实时大屏粗略计数)中考虑。强烈不建议在要求数据准确性的业务中使用。
  2. 读已提交 (READ COMMITTED)

    • 特点: 允许读取并发事务已经提交的数据,这是许多数据库(如 Oracle, PostgreSQL的默认隔离级别但不是 MySQL 的默认)。
    • 允许的问题:
      • 脏读: 避免。 事务只能读取其他事务已经提交的修改。
      • 不可重复读: ✔️ 可能发生。 同一事务内多次读取同一行,结果可能不同(如果其他已提交事务修改了该行)。
      • 幻读: ✔️ 可能发生。 同一事务内多次执行相同范围查询,结果集行数可能不同(如果其他已提交事务插入/删除了满足条件的行)。
    • 并发性: 较高。避免了脏读带来的最基础问题,锁的持有时间通常比 REPEATABLE READ 短(行锁在语句执行后可能更快释放)。
    • 数据一致性: 较好。保证了读取的数据是已提交的、有效的。但同一事务内的多次读取结果可能不一致。
    • 使用场景: 适用于大多数不需要在同一个事务内保证多次读取数据绝对一致的场景。例如,一个展示数据的列表页,每次查询都是独立的快照。
  3. 可重复读 (REPEATABLE READ)

    • 特点: 确保在同一事务中多次读取同一数据得到相同的结果。这是 MySQL 的默认事务隔离级别。通过 MVCC (多版本并发控制) 实现。
    • 允许的问题:
      • 脏读: 避免。
      • 不可重复读: 避免。 在同一事务内,多次读取同一行数据的结果保证是一致的即使其他事务已提交修改。MVCC 通过为事务提供一致性视图 (Consistent Read View) 来实现,该视图基于事务开始时的快照。
      • 幻读: ⚠️ 理论可能,但 MySQL InnoDB 很大程度上避免。 这是关键点!标准的 SQL 定义中,REPEATABLE READ 允许幻读。但是,MySQL 的 InnoDB 存储引擎通过 MVCC 和 Next-Key Locking临键锁的组合在绝大多数情况下避免了幻读。对于快照读(普通 SELECT 语句MVCC 保证看到的是事务开始时的快照,因此不会看到新插入的行。对于当前读SELECT ... FOR UPDATE, SELECT ... LOCK IN SHARE MODE, UPDATE, DELETENext-Key Locking 会锁定扫描到的索引范围,阻止其他事务在该范围内插入,从而避免幻读。
    • 并发性: 中等。比 READ COMMITTED 稍低,因为锁(特别是 Next-Key Locks可能持有更长时间覆盖更大的范围索引区间
    • 数据一致性: 。保证了事务内读取数据的稳定性(同一行可重复读),并通过机制有效防止了幻读,满足大多数应用的需求。
    • 使用场景: MySQL 的默认选择,适用于绝大多数需要保证事务内数据读取一致性的场景,如订单处理、账户管理等。是兼顾一致性和并发性的良好平衡点。
  4. 串行化 (SERIALIZABLE)

    • 特点: 最高的隔离级别。它通过强制事务串行执行来实现,所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰。
    • 允许的问题:
      • 脏读: 避免。
      • 不可重复读: 避免。
      • 幻读: 避免。
    • 实现方式: 简单理解,它会在读取的数据上自动加共享锁(SELECT 默认变成 SELECT ... LOCK IN SHARE MODE​),在写入的数据上加排他锁。这些锁会持有到事务结束。这导致事务之间几乎完全串行化,读写相互阻塞非常严重。
    • 并发性: 最低。性能开销巨大,吞吐量急剧下降。
    • 数据一致性: 最好。完全保证事务的隔离性,不会出现任何并发问题。
    • 使用场景: 仅在对数据一致性要求极高,且完全不能接受任何并发副作用(如金融核心系统的某些极端操作),并且能承受极低并发性能的情况下使用。实践中很少使用。

3.3 MySQL锁

  • 表级锁: MySQL 中锁定粒度最大的一种锁全局锁除外是针对非索引字段加的锁对当前操作的整张表加锁实现简单资源消耗也比较少加锁快不会出现死锁。不过触发锁冲突的概率最高高并发下效率极低。表级锁和存储引擎无关MyISAM 和 InnoDB 引擎都支持表级锁。
  • 行级锁: MySQL 中锁定粒度最小的一种锁,是 针对索引字段加的锁 ,只针对当前操作的行记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。行级锁和存储引擎有关,是在存储引擎层面实现的。
  • 共享锁S 锁) :又称读锁,事务在读取记录的时候获取共享锁,允许多个事务同时获取(锁兼容)。
  • 排他锁X 锁) :又称写锁/独占锁,事务在修改记录的时候获取排他锁,不允许多个事务同时获取。如果一个记录已经被加了排他锁,那其他事务不能再对这条事务加任何类型的锁(锁不兼容)。
SQL语句 默认锁类型 说明
SELECT 无锁MVCC 使用快照读取,不阻塞其他事务
SELECT ... FOR UPDATE 排他锁(X) 阻塞其他事务修改这些行
SELECT ... LOCK IN SHARE MODE 共享锁(S) 允许其他事务读取但阻塞修改
INSERT 排他锁(X) 自动获取
UPDATE 排他锁(X) 自动获取
DELETE 排他锁(X) 自动获取

四、日志

MySQL 的三大核心日志系统是保证数据一致性、实现故障恢复和提供复制功能的关键组件。这三大日志分别是:​​二进制日志(binlog)​​、​​错误日志(error log) 和 ​​重做日志(redo log)​​。

4.1 二进制日志Binary Log简称 binlog

4.1.1 概念

  二进制日志​​是 MySQL ​​记录所有修改数据或可能修改数据的语句(或数据变更)的日志文件​​。它记录了数据库执行的​​更改操作​​(如 INSERT、UPDATE、DELETE 等 DML 操作,以及 CREATE、ALTER、DROP 等 DDL 操作),但不记录 SELECT 这类不修改数据的查询操作

4.1.2 作用

  1. 主从复制Replication在主从架构中主库将 binlog 发送给从库,从库通过读取并重放 binlog 来保持与主库的数据同步。
  2. 数据恢复Point-in-Time Recovery通过备份 + binlog 可以恢复到某个具体时间点。
  3. ​​审计​​:可以追踪数据库的所有变更操作。

4.2 重做日志Redo Log

4.2.1 概念

  重做日志​​是 InnoDB 存储引擎特有的日志,它记录的是 ​​“物理级别” 上的页修改信息​​,主要用于 崩溃恢复Crash Recovery
InnoDB 使用 WALWrite-Ahead Logging预写日志技术即 ​​事务提交时,先写 redo log再写磁盘数据页从而保证即使系统崩溃也能通过 redo log 恢复数据。

4.2.2 作用

  1. 实现事务的持久性Durability确保事务提交后即使发生宕机数据也不会丢失。
  2. ​​提高写入性能​​:数据不是每次修改都直接写磁盘,而是先写 redo log顺序写速度快后续再异步刷盘。
  3. ​​支持 crash-safeMySQL 宕机重启后,可通过 redo log 恢复未刷盘的数据。

4.3 回滚日志Undo Log

4.3.1 概念

  回滚日志​​也是 InnoDB 引擎特有​​ 的日志,它记录的是 ​​数据被修改前的原始值​​

4.3.2 作用

  1. 支持事务回滚​​:如果事务执行失败或调用了 ROLLBACK可以通过 undo log 将数据恢复到修改之前的状态。
  2. 实现 MVCC多版本并发控制在读已提交RC、可重复读RR隔离级别下undo log 用于提供历史版本数据,使得不同事务能看到不同的数据快照,而不需要加锁。

4.4 和事务、锁之间的关系

例如当执行一条SQL语句时事务、锁、日志等之间的关系

  1. 发起一个事务
  2. 执行一系列的DML操作如 INSERT/UPDATE/DELETE
    涉及 ​​锁​​:对操作的数据行或表加锁,防止其他事务干扰。
    涉及 ​​隔离级别​​:决定其他事务是否能“看到”你未提交的数据。
    涉及 undo log如果事务回滚可以根据 undo log 恢复旧值。
  3. 事务提交commit 或 回滚rollback
    redo log保证即使宕机已提交事务的修改也不丢失持久性
    binlog记录数据变更用于主从复制与时间点恢复。
    undo log用于实现事务回滚、MVCC。
  4. 背后有日志系统默默记录一切,锁系统保障并发安全,隔离级别定义了“你能看到啥”。​

4.4.1 相互关系

  1. 事务是锁的使用者,锁是事务实现隔离性的手段。​当一个事务对某行数据执行 UPDATE/DELETE 操作时为了防止其他事务同时修改相同数据InnoDB 会自动对该行或索引加 排他锁X锁。如果事务只是读取数据根据隔离级别可能会加 共享锁S锁 或使用 MVCC不加锁
  2. 隔离级别定义了事务之间的可见性规则,是事务“隔离性”的具体体现。​事务的隔离性是通过锁 + MVCC多版本并发控制依赖 undo log+ 隔离级别共同实现的。​
  3. 日志是事务实现 持久性、崩溃恢复、回滚 等能力的基石。redo log重做日志保障事务的持久性Durability事务提交时先将数据页的变更记录到 redo log顺序写高性能随后再异步刷盘到磁盘数据页。即使系统崩溃重启后也能通过 redo log 恢复已提交但尚未刷盘的数据。undo log回滚日志支持事务回滚 和 MVCC。事务修改数据前会先把原始数据保存到 undo log如果事务回滚可以用它恢复旧值。同时undo log 也是 MVCC多版本控制实现的基础用于提供历史版本数据。binlog二进制日志用于主从复制、时间点恢复。虽然 binlog 是 Server 层的日志,不属于 InnoDB 事务引擎的一部分但在事务提交时binlog 与 redo log 通过两阶段提交2PC保证一致性
  4. 隔离级别决定了锁的粒度和行为,锁是隔离级别的底层实现机制之一。​在 READ UNCOMMITTED 下,一般不加锁(脏读允许)。在 READ COMMITTED 和 REPEATABLE READInnoDB 通过 ​​行锁 + MVCC 实现隔离,不一定非得阻塞其他事务。在 SERIALIZABLE 下,会自动为读操作也加上共享锁,相当于所有操作串行执行,隔离性最强,但并发性能最低。
  5. 锁保障并发安全,日志保障操作可恢复。两者从不同维度确保数据库的正确性。​锁​​:是在运行时控制谁可以访问哪些数据,是 ​​并发控制​​ 的手段。​​日志​​:是在磁盘上记录操作过程,是 ​​故障恢复 & 事务一致性​​ 的手段。
  6. 隔离级别定义了事务间数据的可见性,而日志(尤其是 undo log 和 binlog为这种“可见性”提供了实现基础。undo log 是实现 MVCC多版本并发控制 的基础,而 MVCC 是 REPEATABLE READ 等隔离级别的关键。binlog 虽不直接影响隔离性,但它记录了事务的变更历史,是构建主从环境、实现数据恢复的基础。

五、优化

5.1 开启慢查询

  查看慢查询是否开启:

SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'slow_query_log_file';

  临时开启慢查询,重启服务后无效:

SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;

  永久开启慢查询:

[mysqld]
slow_query_log = ON
long_query_time = 1

  开启后可以通过查看SHOW VARIABLES LIKE 'slow_query_log_file'查看记录的文件路径。

5.2 查看SQL运行时间

SET profiling = 1;
SELECT name, age FROM users WHERE name = "user235909051";
SHOW PROFILES;

5.3 Explain 执行计划

EXPLAIN​是 MySQL 自带的一个诊断工具,它可以模拟 MySQL 查询优化器的执行过程,对SELECT​语句(在 MySQL 8.0 及以上版本,也支持对UPDATE​、DELETE​等语句使用)进行分析,并输出该语句的执行计划。

5.3.1 基本用法

在select语句前面加上EXPLAIN关键字即可例如EXPLAIN SELECT * FROM users WHERE age > 25;

5.3.2 输出列说明

列名 说明 示例
id 查询标识符 相同 id 表示同组查询,执行顺序从上到下;不同 id 值越大优先级越高
select_type 查询类型 SIMPLE无子查询、PRIMARY外层查询、SUBQUERY子查询
table 访问的表名
partitions 匹配的分区
type 访问类型 system > const > eq_ref > ref > range > index > ALL(性能核心指标,从优到劣排序)
possible_keys 可能使用的索引
key 实际使用的索引
key_len 索引使用的字节数
ref 索引匹配的列或常量
rows 预估扫描行数 越小越好
filtered 存储引擎返回数据后在 server 层过滤的比例
Extra 额外执行信息

5.3.3 重要指标Type

类型 描述 性能 示例
system 系统表,仅一行 最优 MyISAM 引擎的空表
const 主键/唯一索引的常量查询 极优 WHERE id = 1
eq_ref JOIN 时主键/唯一索引关联 JOIN ... ON t1.pk = t2.pk
ref 非唯一索引的等值查询 WHERE index_col = 10
fulltext 全文索引 MATCH(...) AGAINST(...)
ref_or_null ref + NULL 值搜索 WHERE col = 10 OR col IS NULL
index_merge 索引合并优化 多个索引条件组合
unique_subquery 唯一索引子查询 value IN (SELECT pk FROM ...)
index_subquery 非唯一索引子查询 中下 value IN (SELECT index_col FROM ...)
range 索引范围扫描 中下 WHERE id > 10
index 全索引扫描 SELECT indexed_col FROM table
ALL 全表扫描 最差 无索引查询

5.3.4 重要指标Extra

含义 优化建议
Using index 覆盖索引(无需回表) 优,保持
Using where Server 层过滤数据 检查索引使用
Using temporary 使用临时表 优化 GROUP BY/ORDER BY
Using filesort 额外排序操作 为排序字段加索引
Select tables optimized away 使用聚合函数优化
Using index condition 索引条件下推ICP MySQL 5.6+ 优化特性
Using join buffer 使用连接缓冲区 增大 join_buffer_size
Impossible WHERE WHERE 条件永不成立 查询逻辑错误
Distinct 优化 DISTINCT 操作

六、执行过程

暂无图片 MySQL架构分为两层Service层和存储引擎层。
Server 层负责建立连接、分析和执行 SQL。存储引擎层负责数据的存储和提取。

  1. 连接器:建立连接、管理连接、校验用户身份。
  2. 查询缓存MySQL8.0已删除)
  3. 解析SQL通过解析器对SQL查询语句进行词法分析、语法分析然后构建语法树方便后续模块读取表名、字段和语句类型等。
  4. 执行SQL分为预处理阶段、优化阶段和执行阶段。

七、视图

7.1 定义

视图View是 MySQL 中的一种虚拟表,它的数据来源于一个或多个实际的表(基表),其结构和数据是通过 SQL 查询语句定义的。简单来说,视图就像是一个 “查询窗口”,你看到的是基表的数据,但视图本身并不存储实际数据,每次访问视图时都会执行对应的查询语句

7.2 作用

  1. 简化复杂查询:将多表关联、聚合等复杂的 SQL 逻辑封装到视图中,后续使用时只需查询视图即可。
  2. 数据安全:可以只暴露基表中的部分列或部分行给用户,隐藏敏感数据。
  3. 数据一致性:如果业务逻辑发生变化,只需修改视图的定义,而无需修改所有使用该逻辑的查询。

7.3 使用

  创建视图

-- 创建视图v_student_basic
CREATE VIEW v_student_basic
AS
SELECT name, age, gender FROM student;

  使用视图和正常使用数据表一致:

-- 查询视图(和查询普通表的方式完全一样)
SELECT * FROM v_student_basic;

八、事件

8.1 定义

MySQL 事件Event也被称为事件调度器Event SchedulerMySQL 中一种定时执行的数据库对象,可以理解为数据库层面的 “定时任务” 或 “计划任务”。它能根据你设定的时间规则(一次性执行、周期性执行),自动触发并执行指定的 SQL 逻辑(如数据清理、统计报表生成、数据同步等)。

8.2 特点

  1. 定时执行:支持一次性执行(如某个具体时间点)和周期性执行(如每天凌晨 3 点、每小时执行一次)。
  2. 自动运行:依赖 MySQL 的事件调度器线程,只要调度器开启,事件就会按规则自动触发。
  3. 与存储过程结合:事件的执行逻辑可以是单条 SQL也可以是复杂的存储过程推荐用存储过程封装复杂逻辑

8.3 作用

  • 数据清理:定期删除过期数据(如删除 30 天前的日志表数据)。
  • 数据统计:定时生成业务统计报表(如每天凌晨统计前一天的订单数据)。
  • 数据同步:定期将 A 表的数据同步到 B 表。
  • 定时备份:定期执行数据库备份脚本(配合存储过程)。

8.4 使用

  开启事件调度器:

-- 查看事件调度器状态ON表示开启OFF表示关闭
SHOW VARIABLES LIKE 'event_scheduler';

-- 临时开启MySQL重启后会恢复为默认状态
SET GLOBAL event_scheduler = ON;
-- 永久开启需要修改my.cnf/my.ini配置文件添加以下内容然后重启MySQL
event_scheduler = ON  # 放在[mysqld]节点下

  每天凌晨 1 点执行的事件,删除日志表中 7 天前的过期数据:

-- 创建周期性事件每天凌晨1点执行无结束时间
CREATE EVENT IF NOT EXISTS event_clear_log
ON SCHEDULE EVERY 1 DAY STARTS '2025-12-22 01:00:00'
COMMENT '每天凌晨1点删除7天前的日志'
DO
    DELETE FROM log WHERE create_time < DATE_SUB(NOW(), INTERVAL 7 DAY);