文章目录
-
- [MySQL 存储引擎](#MySQL 存储引擎)
-
- [一、MySQL 存储引擎概述](#一、MySQL 存储引擎概述)
- 二、常见存储引擎对比
- 三、核心引擎详解
-
- [1. InnoDB](#1. InnoDB)
- [2. MyISAM](#2. MyISAM)
- 四、如何选择存储引擎?
- 五、引擎操作命令
-
- [1. 查看表的存储引擎](#1. 查看表的存储引擎)
- [2. 修改表的存储引擎](#2. 修改表的存储引擎)
- [3. 引擎与性能优化](#3. 引擎与性能优化)
- 六、示例对比
-
- [场景:频繁更新的订单表 vs 只读的产品分类表](#场景:频繁更新的订单表 vs 只读的产品分类表)
- 七、总结
- 索引失效
-
- [1. 索引列使用函数或表达式](#1. 索引列使用函数或表达式)
- [2. 隐式类型转换](#2. 隐式类型转换)
- [3. 范围查询右侧的索引列失效](#3. 范围查询右侧的索引列失效)
- [4. 模糊查询以通配符开头](#4. 模糊查询以通配符开头)
- [5. OR 条件导致索引失效](#5. OR 条件导致索引失效)
- [6. 索引列参与计算](#6. 索引列参与计算)
- [7. IS NULL 和 IS NOT NULL 部分情况](#7. IS NULL 和 IS NOT NULL 部分情况)
- [8. 数据分布不均匀](#8. 数据分布不均匀)
- [9. 索引被禁用或损坏](#9. 索引被禁用或损坏)
- sql执行全流程
MySQL 存储引擎
一、MySQL 存储引擎概述
存储引擎是 MySQL
的核心组件,负责数据的存储、读取和管理。不同的引擎在事务支持、锁机制、性能等方面有显著差异。MySQL
支持多种存储引擎,可通过以下命令查看所有支持的引擎:
sql
SHOW ENGINES;
二、常见存储引擎对比
引擎 | 特点 | 适用场景 | 默认启用 |
---|---|---|---|
InnoDB | ✅ 支持事务(ACID) ✅ 行级锁 ✅ 外键约束 ✅ 崩溃恢复能力 ✅ 聚簇索引(数据与索引绑定) | 高并发读写、需要事务的场景(如电商、金融) | ✅ 5.5+ 默认 |
MyISAM | ❌ 不支持事务 ❌ 表级锁 ✅ 全文索引 ✅ 较高的读取速度 ❗ 数据损坏后恢复困难 | 只读或读多写少(如日志分析、静态数据) | ❌ 需手动启用 |
Memory | ✅ 数据存储在内存中 ✅ 极快读写速度 ❌ 服务重启后数据丢失 ❌ 表级锁 | 临时表、缓存、快速计算中间结果 | ❌ 需手动启用 |
Archive | ✅ 高压缩比存储 ✅ 仅支持 INSERT/SELECT ❌ 不支持索引和更新 | 归档历史数据(如日志归档) | ❌ 需手动启用 |
CSV | ✅ 数据以 CSV 格式存储 ❌ 不支持索引、事务、分区 | 与外部系统交换 CSV 数据 | ❌ 需手动启用 |
三、核心引擎详解
1. InnoDB
- 核心优势 :
- 事务支持 :通过
BEGIN
,COMMIT
,ROLLBACK
保证数据一致性。 - 行级锁:仅锁定操作的行,提高并发性能。
- 外键约束:确保关联表的数据完整性。
- MVCC(多版本并发控制):读写操作互不阻塞。
- 事务支持 :通过
- 存储结构 :
- 数据与索引存储在
.ibd
文件中(使用独立表空间时)。 - 采用 B+树聚簇索引,主键索引的叶节点直接包含行数据。
- 数据与索引存储在
2. MyISAM
- 核心特点 :
- 非聚簇索引 :索引和数据文件分离(
.MYI
索引文件 +.MYD
数据文件)。 - 表级锁:写操作会锁定整个表,并发性能低。
- 压缩表 :通过
myisampack
工具压缩,节省存储空间。
- 非聚簇索引 :索引和数据文件分离(
- 典型问题 :
- 崩溃后可能需手动修复(
REPAIR TABLE
命令)。 - 不支持事务,数据一致性依赖应用层逻辑。
- 崩溃后可能需手动修复(
四、如何选择存储引擎?
场景 | 推荐引擎 | 理由 |
---|---|---|
需要事务或高并发写操作 | InnoDB | 事务和行级锁保证数据一致性和并发性能 |
只读查询为主,无事务需求 | MyISAM | 读取速度快,占用资源少(但需权衡数据安全风险) |
临时数据处理或缓存 | Memory | 内存存储实现毫秒级响应,但需注意数据丢失风险 |
历史数据归档(如日志) | Archive | 高压缩比节省存储空间,适合低频访问数据 |
五、引擎操作命令
1. 查看表的存储引擎
sql
SHOW TABLE STATUS LIKE '表名';
2. 修改表的存储引擎
sql
-- 修改单个表
ALTER TABLE 表名 ENGINE=InnoDB;
-- 修改全局默认引擎(需配置 my.cnf/my.ini)
[mysqld]
default-storage-engine=InnoDB
3. 引擎与性能优化
- InnoDB :调整
innodb_buffer_pool_size
(建议设为物理内存的 70%~80%)。 - MyISAM :优化
key_buffer_size
以提高索引缓存效率。
六、示例对比
场景:频繁更新的订单表 vs 只读的产品分类表
- 订单表 :使用
InnoDB
,保障订单状态更新、支付事务的原子性。 - 分类表 :使用
MyISAM
(若无需事务),加速分类信息的读取。
七、总结
引擎 | 事务 | 锁粒度 | 索引类型 | 数据安全 | 适用场景 |
---|---|---|---|---|---|
InnoDB | 支持 | 行级锁 | 聚簇索引 | 高(崩溃恢复) | 通用型、高并发写 |
MyISAM | 不支持 | 表级锁 | 非聚簇索引 + 全文索引 | 低 | 读多写少、静态数据 |
Memory | 不支持 | 表级锁 | 哈希索引 | 无(内存存储) | 临时数据、缓存 |
根据业务需求选择合适的引擎是数据库设计的关键步骤!如果需要进一步探讨特定场景的引擎优化,可以继续交流。
索引失效
在 MySQL
中,索引失效会导致查询无法利用索引来加速,从而进行全表扫描,降低查询性能。以下是常见的索引失效情况:
1. 索引列使用函数或表达式
当对索引列使用函数或表达式时,MySQL
无法直接使用索引来定位数据,从而导致索引失效。
sql
-- 假设 age 列有索引,以下查询索引会失效
SELECT * FROM users WHERE YEAR(birth_date) = 1990;
可以将查询改写为范围查询,避免对索引列使用函数:
sql
SELECT * FROM users WHERE birth_date BETWEEN '1990-01-01' AND '1990-12-31';
2. 隐式类型转换
如果查询条件中的数据类型与索引列的数据类型不一致,MySQL 会进行隐式类型转换,这可能会导致索引失效。
sql
-- 假设 id 列是整数类型,以下查询索引会失效
SELECT * FROM users WHERE id = '123';
应确保查询条件的数据类型与索引列的数据类型一致:
sql
SELECT * FROM users WHERE id = 123;
3. 范围查询右侧的索引列失效
对于组合索引,MySQL 遵循最左前缀原则
。如果在查询条件中使用了范围查询(如 >
、<
、BETWEEN
等),范围查询右侧的索引列将无法使用索引。
sql
-- 假设存在组合索引 (col1, col2, col3)
SELECT * FROM table_name WHERE col1 = 'value1' AND col2 > 10 AND col3 = 'value3';
-- 这里 col3 无法使用索引
4. 模糊查询以通配符开头
在使用 LIKE
进行模糊查询时,如果通配符 %
出现在字符串的开头,MySQL
无法使用索引来定位数据。
sql
-- 以下查询索引会失效
SELECT * FROM users WHERE name LIKE '%John';
如果业务允许,可以将查询改写为不以通配符开头的形式:
sql
SELECT * FROM users WHERE name LIKE 'John%';
5. OR 条件导致索引失效
当查询条件中使用 OR
连接多个条件时,如果 OR
两边的条件没有都使用索引,可能会导致索引失效。
sql
-- 假设 col1 和 col2 都有索引,但以下查询可能会全表扫描
SELECT * FROM table_name WHERE col1 = 'value1' OR col2 = 'value2';
可以将查询拆分为两个查询,然后使用 UNION
连接:
sql
SELECT * FROM table_name WHERE col1 = 'value1'
UNION
SELECT * FROM table_name WHERE col2 = 'value2';
6. 索引列参与计算
如果索引列参与了计算,MySQL 无法直接使用索引。
sql
-- 假设 price 列有索引,以下查询索引会失效
SELECT * FROM products WHERE price + 10 < 100;
可以将查询改写为不包含索引列计算的形式:
sql
SELECT * FROM products WHERE price < 90;
7. IS NULL 和 IS NOT NULL 部分情况
对于索引列使用 IS NULL
或 IS NOT NULL
,在某些情况下可能会导致索引失效。不过,在 InnoDB
存储引擎中,对于 IS NULL
是可以使用索引的,但 IS NOT NULL
不一定能使用索引,具体取决于数据分布。
sql
-- 以下查询在某些情况下可能索引失效
SELECT * FROM users WHERE email IS NOT NULL;
8. 数据分布不均匀
如果索引列的数据分布不均匀,例如大部分数据集中在少数几个值上,MySQL
可能会认为使用索引的成本较高,从而选择全表扫描。
例如,在一个 gender
列上创建了索引,该列只有 'male'
和 'female'
两个值,且分布严重不均衡,此时 MySQL
可能会选择全表扫描。
9. 索引被禁用或损坏
如果索引被手动禁用或者索引文件损坏,也会导致索引无法使用。可以通过 SHOW INDEX
查看索引状态,使用 REPAIR TABLE
修复损坏的索引。
sql执行全流程
MySQL
语句的执行过程是一个多阶段的复杂流程,涉及语法解析、优化、执行引擎等多个模块的协作。以下是 一条 SQL 语句从客户端发起到返回结果的全过程 ,以 SELECT * FROM users WHERE id = 1
为例:
一、连接阶段
-
建立连接
- 客户端通过
TCP/IP
或Socket
协议与MySQL
服务器建立连接。 MySQL
通过线程池管理连接(每个连接对应一个线程)。- 验证用户名、密码和权限。
- 客户端通过
-
接收 SQL 语句
- 客户端将
SQL
语句发送到MySQL
服务器。 - 服务器将
SQL
语句存储在「网络接收缓冲区」中。
- 客户端将
二、解析与预处理
-
查询缓存(MySQL 8.0 已移除)
- (仅适用于旧版本)检查缓存中是否有完全相同的
SQL
语句及结果。 - 若命中缓存,直接返回结果;否则继续执行。
- (仅适用于旧版本)检查缓存中是否有完全相同的
-
词法分析 & 语法分析
- 词法分析 :将
SQL
语句拆解为「关键字、表名、列名、操作符」等Token
。 - 语法分析 :验证
SQL
是否符合MySQL
语法规则(如关键字顺序、括号匹配)。 - 若语法错误,返回错误信息(如
ERROR 1064
)。
- 词法分析 :将
-
语义分析
- 验证表名、列名是否存在,用户是否有权限访问。
- 检查
WHERE
条件中的列是否存在,数据类型是否匹配。
三、优化阶段
-
生成执行计划
- 优化器(
Optimizer
)分析所有可能的执行路径,选择「成本最低」的执行计划。 - 关键决策 :
- 使用哪个索引(或全表扫描)。
JOIN
表的顺序(如果涉及多表查询)。WHERE
条件的处理顺序。
- 优化器(
-
优化器工作流程
- 统计信息 :基于表的
rows
、index cardinality
等元数据估算不同执行计划的成本。 - 成本模型 :计算磁盘
I/O、CPU
消耗、内存占用等成本。 - 示例 :对
WHERE id = 1
,优化器可能选择PRIMARY
索引(如果id
是主键)。
- 统计信息 :基于表的
四、执行阶段
-
执行引擎
- 执行器(
Executor
)根据优化器生成的执行计划,调用存储引擎接口读取数据。 - 关键步骤 :
- 打开表,获取表的元数据(如存储引擎类型)。
- 调用存储引擎的索引查询接口(如
InnoDB
的index_read
)。
- 执行器(
-
存储引擎处理(以 InnoDB 为例)
- 索引查找 :通过
B+
树索引定位到id=1
的记录。- 若使用主键索引,直接找到数据页。
- 若使用二级索引,需回表查询主键索引。
- 事务与锁 :
- 若在事务中,根据隔离级别加锁(如行锁、间隙锁)。
- 保证事务的
ACID
特性(通过undo log
、redo log
)。
- 索引查找 :通过
-
返回结果
- 存储引擎将符合条件的记录返回给执行器。
- 执行器将结果集放入「结果集缓冲区」。
- 通过网络协议将结果返回客户端。
五、日志与事务(扩展)
-
日志模块
- Binlog (
Server
层):记录逻辑操作(用于主从复制、数据恢复)。 - Redo Log (
InnoDB
):记录物理页修改,保证崩溃恢复。 - Undo Log (
InnoDB
):记录事务前的数据版本,用于回滚和MVCC
。
- Binlog (
-
事务提交
- 若
SQL
是更新操作(如UPDATE
),需写入redo log
和binlog
。 - 通过两阶段提交(
2PC
)保证日志一致性。
- 若
六、关键流程图
plaintext
客户端 → 建立连接 → 解析SQL → 优化器 → 执行引擎 → 存储引擎 → 返回结果
│ │ │ │ │
权限验证 语法检查 成本计算 调用接口 索引/行锁
七、性能优化点
- 减少解析时间
- 避免频繁发送短
SQL
,可使用预编译语句(Prepared Statement
)。
- 避免频繁发送短
- 优化执行计划
- 更新统计信息:
ANALYZE TABLE
。 - 使用
FORCE INDEX
强制指定索引(慎用)。
- 更新统计信息:
- 减少磁盘 I/O
- 通过索引减少扫描的数据量。
- 调整
innodb_buffer_pool_size
提高缓存命中率。
通过理解 SQL
执行的全过程,可以更高效地优化查询性能和排查问题(如使用 EXPLAIN
分析执行计划)。