当前位置: 首页 > news >正文

mysql的InnoDB索引总结

MySQL InnoDB索引知识点总结

1. 索引类型

1.1 聚簇索引(Clustered Index)

定义与特性
  • 定义:聚簇索引是InnoDB的默认存储方式,数据行按照主键的顺序物理存储在磁盘上
  • 特性
    • 每个InnoDB表只能有一个聚簇索引
    • 数据页中的记录按主键值的顺序存放
    • 叶子节点直接存储完整的数据行
    • 查询主键时可以直接定位到数据,无需额外的回表操作
数据与索引存储方式
  • 数据页和索引页是一体的,存储在同一个B+树结构中
  • 非叶子节点存储主键值和指向子节点的指针
  • 叶子节点存储主键值和完整的数据行记录
  • 叶子节点通过双向链表连接,方便范围查询
主键选择对聚簇索引的影响
  • 自增主键
    • 新记录总是插入到末尾,减少页分裂
    • 顺序插入,提高写入性能
    • 主键值较小,节省存储空间
  • 非自增主键
    • 可能导致频繁的页分裂和页合并
    • 降低插入性能,产生碎片
  • 无主键时:InnoDB会自动创建一个6字节的隐藏主键(ROW_ID)

1.2 二级索引(Secondary Index)

定义与结构
  • 定义:除聚簇索引外的所有其他索引都称为二级索引(也叫非聚簇索引)
  • 结构特点
    • 索引键值 + 主键值的组合
    • 叶子节点不存储完整数据行,只存储索引列值和主键值
    • 可以有多个二级索引
叶节点存储主键值的机制
  • 二级索引的叶子节点存储:索引列值 + 主键值
  • 这种设计避免了主键变更时需要更新所有二级索引
  • 当聚簇索引页分裂时,二级索引不需要更新
  • 主键值作为"书签"指向聚簇索引中的具体记录
回表查询(书签查找)过程
  1. 首先在二级索引B+树中查找索引键值
  2. 获得对应的主键值
  3. 使用主键值在聚簇索引中进行二次查找
  4. 最终在聚簇索引的叶子节点获得完整记录
-- 示例:使用二级索引查询
SELECT * FROM users WHERE email = 'user@example.com';
-- 1. 在email索引中查找 'user@example.com'
-- 2. 获得主键值,如 id=123
-- 3. 在聚簇索引中查找 id=123
-- 4. 返回完整记录

1.3 复合索引(Composite Index)

多列组合索引规则
  • 复合索引由多个列组成,如 INDEX(col1, col2, col3)
  • 索引按照列的定义顺序进行排序
  • 先按第一列排序,第一列相同时按第二列排序,以此类推
最左前缀匹配原则
  • 原则:查询必须从索引的最左列开始,并且不能跳过中间的列
  • 有效使用场景
    INDEX(a, b, c)
    -- 可以使用索引:
    WHERE a = 1
    WHERE a = 1 AND b = 2
    WHERE a = 1 AND b = 2 AND c = 3
    WHERE a = 1 AND c = 3  -- 只能使用a列的索引-- 无法使用索引:
    WHERE b = 2
    WHERE c = 3
    WHERE b = 2 AND c = 3
    
索引选择性与列顺序优化
  • 选择性高的列放在前面:选择性 = 不重复值数量 / 总记录数
  • 考虑查询频率:经常用于WHERE条件的列放在前面
  • 考虑排序需求:如果需要排序,将排序列放在索引中合适位置

1.4 其他特殊索引类型

覆盖索引(Covering Index)
  • 定义:索引包含查询所需的所有列,无需回表查询
  • 优势
    • 避免回表操作,减少I/O
    • 提高查询性能
    • 减少锁竞争
  • 示例
    -- 创建覆盖索引
    CREATE INDEX idx_user_info ON users(status, age, name);-- 下面的查询可以使用覆盖索引
    SELECT name FROM users WHERE status = 1 AND age > 18;
    
前缀索引(Prefix Index)
  • 定义:只索引列值的前几个字符
  • 适用场景:VARCHAR、TEXT等长字符串列
  • 优势:节省存储空间,提高索引效率
  • 劣势:可能降低索引选择性
  • 示例
    -- 为email列的前10个字符创建索引
    CREATE INDEX idx_email_prefix ON users(email(10));
    
全文索引(Full-Text Index)
  • 定义:用于全文搜索的特殊索引类型
  • 支持的存储引擎:InnoDB(MySQL 5.6+)、MyISAM
  • 适用场景:文本搜索、关键词匹配
  • 示例
    -- 创建全文索引
    CREATE FULLTEXT INDEX idx_content ON articles(title, content);-- 使用全文索引搜索
    SELECT * FROM articles WHERE MATCH(title, content) AGAINST('MySQL 索引');
    

2. 索引数据结构

2.1 为什么MySQL选择B+树作为索引结构

数据库索引的特殊需求

在分析为什么选择B+树之前,我们需要了解数据库索引的特殊需求:

  • 大量数据存储:数据库通常需要处理TB级别的数据
  • 磁盘I/O优化:数据主要存储在磁盘上,磁盘I/O是性能瓶颈
  • 范围查询频繁:数据库经常需要进行范围查询和排序
  • 并发访问:多个事务同时访问数据
  • 持久化存储:数据需要可靠地存储在磁盘上
B+树 vs 其他数据结构详细对比
与平衡二叉树(AVL树/红黑树)的对比
对比维度B+树平衡二叉树
树的高度矮胖型,高度通常3-4层高瘦型,高度log₂n
磁盘I/O次数少(每层一次I/O)多(可能需要很多次I/O)
节点大小大(通常4KB-16KB)小(只存储一个键值)
磁盘利用率高(一次读取多个键值)低(一次只读取一个键值)
范围查询高效(叶子节点链表)低效(需要中序遍历)
内存友好性好(符合磁盘页面大小)差(随机访问模式)

具体分析

假设有100万条记录:
- 平衡二叉树:高度约为log₂(1000000) ≈ 20层最坏情况需要20次磁盘I/O才能找到数据- B+树(假设每个节点1000个键值):高度约为log₁₀₀₀(1000000) ≈ 2层最多只需要3次磁盘I/O(根节点+内部节点+叶子节点)
与B树的对比
对比维度B+树B树
数据存储位置只在叶子节点存储数据所有节点都存储数据
内部节点容量更多键值(不存储数据)较少键值(需要存储数据)
范围查询效率高(叶子节点链表遍历)低(需要回溯到公共祖先)
查询稳定性稳定(都要到叶子节点)不稳定(可能在任意层结束)
缓存友好性好(内部节点更紧凑)相对较差

B+树优势示例

-- 范围查询:SELECT * FROM users WHERE age BETWEEN 20 AND 30;B+树执行过程:
1. 从根节点找到age=20的叶子节点
2. 从该叶子节点开始,沿着链表顺序扫描到age=30
3. 整个过程只需要很少的磁盘I/OB树执行过程:
1. 找到age=20的位置(可能在任意层)
2. 进行复杂的树遍历,需要反复回溯
3. 无法充分利用磁盘的顺序读特性
与哈希表的对比
对比维度B+树哈希表
等值查询O(log n)O(1)
范围查询支持且高效不支持
排序输出天然有序无序
模糊查询支持(前缀匹配)不支持
磁盘存储友好(顺序存储)困难(随机分布)
冲突处理不存在冲突需要处理哈希冲突
内存占用相对较少可能较多(负载因子)
与跳表的对比
对比维度B+树跳表
实现复杂度复杂(但已成熟)相对简单
空间效率中等(索引层开销)
并发控制成熟的锁机制实现复杂
磁盘友好性非常好一般
范围查询高效高效
B+树在数据库中的具体优势
1. 磁盘I/O优化
磁盘特性:
- 顺序读写速度:100-200 MB/s
- 随机读写速度:1-5 MB/s
- 磁盘页面大小:通常4KB或8KBB+树优势:
- 节点大小匹配磁盘页面
- 叶子节点链表支持顺序读取
- 减少随机I/O,提高缓存命中率
2. 内存缓存效率
InnoDB Buffer Pool机制:
- 将热点数据页缓存在内存中
- B+树的内部节点更容易被缓存
- 大部分查询只需要访问已缓存的根节点和少数内部节点
3. 范围查询优化
-- 高效的范围查询实现
SELECT * FROM orders WHERE order_date BETWEEN '2024-01-01' AND '2024-01-31';执行过程:
1. 定位到'2024-01-01'对应的叶子节点
2. 沿着叶子节点链表顺序扫描
3. 直到找到'2024-01-31'为止
4. 整个过程主要是顺序I/O操作
4. 并发控制友好
B+树的并发优势:
- 读操作通常不需要锁定整个树
- 可以实现细粒度的锁控制
- 支持多版本并发控制(MVCC)
- 页面级别的锁定机制
实际性能数据对比

假设一个包含1000万条记录的表:

数据结构查找操作范围查询插入操作内存占用
B+树3-4次I/O高效较好适中
平衡二叉树20+次I/O较差较少
B树3-4次I/O中等较好适中
哈希表1次I/O不支持较多
为什么不选择其他结构的总结
  1. 平衡二叉树

    • 树太高,导致过多的磁盘I/O
    • 不适合磁盘存储的特性
    • 范围查询效率低
  2. 哈希表

    • 不支持范围查询和排序
    • 难以在磁盘上高效实现
    • 不支持模糊匹配
  3. B树

    • 范围查询效率不如B+树
    • 内部节点存储数据降低了扇出度
    • 查询性能不够稳定
  4. 跳表

    • 在磁盘上的实现不够高效
    • 空间开销相对较大
    • 并发控制复杂
B+树的设计哲学

B+树的设计完美契合了数据库存储的需求:

  • 面向磁盘优化:最小化磁盘I/O次数
  • 支持多种查询:等值查询、范围查询、排序
  • 高并发友好:支持细粒度锁控制
  • 空间效率高:紧凑的存储结构
  • 实现成熟:经过数十年的优化和验证

2.2 B+树索引结构

非叶子节点与叶子节点的区别
  • 非叶子节点(内部节点)

    • 只存储键值和指向子节点的指针
    • 不存储实际数据
    • 用于导航和定位
    • 键值是子节点中的最大值或最小值
  • 叶子节点

    • 存储实际的数据记录(聚簇索引)或主键值(二级索引)
    • 所有叶子节点在同一层级
    • 包含所有的索引键值
叶子节点的双向链表特性
  • 所有叶子节点通过指针连接形成双向链表
  • 支持高效的范围查询和排序操作
  • 便于顺序扫描和反向扫描
  • 提高了区间查询的性能
[叶子节点1] ←→ [叶子节点2] ←→ [叶子节点3] ←→ [叶子节点4]
平衡树结构与查询效率
  • 平衡性:所有叶子节点都在同一层,保证查询路径长度一致
  • 查询复杂度:O(log n),其中n是记录数
  • 树的高度:通常3-4层就能存储数百万记录
  • 页分裂与合并:自动维护树的平衡性

2.3 B+树与其他结构对比(简化版)

这里提供一个简化的对比表格,详细的对比分析请参考上面的"为什么MySQL选择B+树作为索引结构"章节。

与B树的差异
特性B+树B树
数据存储位置只在叶子节点所有节点都可以存储数据
叶子节点连接双向链表连接叶子节点独立
范围查询效率高(链表遍历)低(需要回溯)
磁盘I/O更少(非叶子节点更紧凑)相对较多
查询稳定性稳定(都到叶子节点)不稳定(可能在任意层找到)
与哈希索引的适用场景
比较维度B+树索引哈希索引
等值查询O(log n)O(1)
范围查询支持不支持
排序支持不支持
部分匹配支持(最左前缀)不支持
存储引擎InnoDB、MyISAMMemory
适用场景通用场景等值查询为主的场景

3. InnoDB索引特殊特性

3.1 自适应哈希索引(Adaptive Hash Index)

自动创建与维护机制
  • 自动检测:InnoDB监控二级索引的使用模式
  • 创建条件
    • 对某个索引页的访问模式稳定
    • 以相同方式访问了至少100次
    • 页面通过该模式访问了至少N次(N基于页面大小和访问模式)
  • 维护机制
    • 自动维护,无需人工干预
    • 当访问模式改变时自动删除
    • 内存中的哈希表结构
使用场景与限制
  • 适用场景

    • 大量等值查询
    • 查询模式相对稳定
    • 工作集能够放入内存
  • 限制

    • 只支持等值查询,不支持范围查询
    • 只能针对整个索引键,不支持部分索引键
    • 无法显式创建或删除
    • 可能与某些锁操作冲突
-- 查看自适应哈希索引状态
SHOW ENGINE INNODB STATUS;
-- 或
SELECT * FROM INFORMATION_SCHEMA.INNODB_METRICS 
WHERE NAME LIKE '%adaptive_hash%';

3.2 索引组织表(Index-Organized Table)

数据按主键顺序存储
  • InnoDB表本质上就是索引组织表
  • 数据行按照主键顺序物理存储
  • 表数据就是聚簇索引的叶子节点
  • 没有独立的数据文件,数据存储在索引结构中
与堆组织表的区别
特性索引组织表(InnoDB)堆组织表(MyISAM)
数据存储按主键顺序存储按插入顺序存储
主键查询直接定位需要额外索引查找
插入性能可能有页分裂通常较快
范围查询高效(数据有序)需要额外排序
存储空间可能有碎片相对紧凑

3.3 事务与索引一致性

MVCC对索引查询的影响
  • 版本可见性:索引查询需要结合MVCC判断记录版本的可见性
  • 删除标记:删除的记录在索引中标记为删除,但不立即物理删除
  • 回滚段:通过undo log维护数据的历史版本
  • Read View:事务开始时建立一致性视图,确保读取数据的一致性
锁机制与索引并发控制
  • 记录锁(Record Lock):锁定索引记录
  • 间隙锁(Gap Lock):锁定索引记录之间的间隙
  • Next-Key Lock:记录锁 + 间隙锁,防止幻读
  • 插入意向锁:插入前获取的特殊间隙锁
-- 示例:Next-Key Lock的作用
-- 事务1
BEGIN;
SELECT * FROM users WHERE age BETWEEN 20 AND 30 FOR UPDATE;-- 事务2(会被阻塞)
INSERT INTO users (name, age) VALUES ('Tom', 25);

4. 索引维护与优化

4.1 索引维护操作

页分裂(Page Split)与页合并(Page Merge)
  • 页分裂发生时机

    • 插入新记录时页面空间不足
    • 更新操作导致记录长度增加
  • 页分裂过程

    1. 创建新页面
    2. 将部分记录移动到新页面
    3. 更新父节点的指针
    4. 调整页面间的链接关系
  • 页合并发生时机

    • 删除记录后页面利用率过低
    • 页面利用率低于MERGE_THRESHOLD(默认50%)
  • 影响

    • 页分裂增加I/O开销,降低性能
    • 页合并有助于回收空间,但也有开销
索引碎片产生与整理
  • 碎片产生原因

    • 频繁的插入、删除、更新操作
    • 页分裂导致的逻辑顺序与物理顺序不一致
    • 删除记录后留下的空隙
  • 碎片类型

    • 内部碎片:页面内的未使用空间
    • 外部碎片:页面之间的不连续性
  • 整理方法

    -- 重建表(会重建所有索引)
    ALTER TABLE table_name ENGINE=InnoDB;-- 优化表
    OPTIMIZE TABLE table_name;-- 重建特定索引
    ALTER TABLE table_name DROP INDEX idx_name, ADD INDEX idx_name(col1, col2);
    

4.2 索引设计最佳实践

主键选择策略(自增ID vs 业务主键)
  • 自增ID主键

    • 优势:顺序插入,减少页分裂,性能稳定
    • 劣势:额外存储开销,可能泄露业务信息
    • 适用:大多数OLTP应用
  • 业务主键

    • 优势:有业务含义,减少关联查询
    • 劣势:可能导致随机插入,性能不稳定
    • 适用:主键具有明确业务含义且插入模式可控
  • 联合主键

    • 优势:符合业务模型
    • 劣势:键值较大,影响二级索引性能
    • 适用:关系表、日志表
合理控制索引数量
  • 原则

    • 优先创建高选择性的索引
    • 避免创建冗余索引
    • 考虑查询频率和重要性
    • 平衡查询性能和维护成本
  • 索引数量建议

    • 单表索引数量一般不超过6个
    • 复合索引列数不超过3-4个
    • 监控索引使用情况,删除无用索引
避免过度索引与冗余索引
  • 过度索引问题

    • 增加存储空间
    • 降低写入性能
    • 增加维护成本
  • 冗余索引识别

    -- 查找冗余索引
    SELECT table_schema,table_name,redundant_index_name,redundant_index_columns,dominant_index_name,dominant_index_columns
    FROM sys.schema_redundant_indexes;
    
  • 索引合并策略

    • 将多个单列索引合并为复合索引
    • 考虑最左前缀原则
    • 根据查询模式调整列顺序

4.3 索引使用分析

EXPLAIN执行计划解读
EXPLAIN SELECT * FROM users WHERE status = 1 AND age > 25;

关键字段解读:

  • type:连接类型,性能从好到坏:

    • const:主键或唯一索引等值查询
    • eq_ref:唯一索引查找
    • ref:非唯一索引等值查询
    • range:索引范围查询
    • index:索引全扫描
    • ALL:全表扫描
  • key:实际使用的索引

  • key_len:使用的索引长度

  • rows:预估扫描行数

  • Extra:额外信息

    • Using index:覆盖索引
    • Using filesort:需要额外排序
    • Using temporary:使用临时表
索引失效场景与避免方法
  • 常见失效场景
    1. 在索引列上使用函数

      -- 错误:索引失效
      SELECT * FROM users WHERE UPPER(name) = 'JOHN';-- 正确:索引有效
      SELECT * FROM users WHERE name = 'JOHN';
      
    2. 类型转换

      -- 错误:字符串列与数字比较
      SELECT * FROM users WHERE phone = 13800138000;-- 正确:类型匹配
      SELECT * FROM users WHERE phone = '13800138000';
      
    3. LIKE以通配符开头

      -- 错误:索引失效
      SELECT * FROM users WHERE name LIKE '%john%';-- 正确:可以使用索引
      SELECT * FROM users WHERE name LIKE 'john%';
      
    4. OR条件中有非索引列

      -- 如果age没有索引,整个查询不能使用索引
      SELECT * FROM users WHERE name = 'john' OR age = 25;-- 使用UNION改写
      SELECT * FROM users WHERE name = 'john'
      UNION
      SELECT * FROM users WHERE age = 25;
      
    5. 复合索引不遵循最左前缀

      -- 索引:INDEX(a, b, c)
      -- 错误:跳过了a列
      SELECT * FROM table WHERE b = 1 AND c = 2;-- 正确:从最左列开始
      SELECT * FROM table WHERE a = 1 AND b = 1;
      

5. InnoDB与MyISAM索引对比

5.1 存储结构差异

特性InnoDBMyISAM
索引文件数据和索引存储在.ibd文件中索引存储在.MYI文件,数据存储在.MYD文件
聚簇索引支持聚簇索引,数据按主键顺序存储不支持聚簇索引,使用堆组织表
二级索引叶子节点存储主键值叶子节点存储行指针(文件偏移量)
主键要求必须有主键(显式或隐式)主键可选

5.2 事务支持与崩溃恢复

特性InnoDBMyISAM
事务支持完全支持ACID事务不支持事务
崩溃恢复通过redo log和undo log自动恢复需要手动修复,可能丢失数据
数据完整性通过事务保证一致性依赖应用程序保证
回滚能力支持事务回滚不支持回滚

5.3 并发控制机制

特性InnoDBMyISAM
锁粒度行级锁表级锁
读写并发高并发读写读写互斥
MVCC支持多版本并发控制不支持
死锁检测自动死锁检测和回滚不适用
锁等待支持锁等待超时简单的锁等待

5.4 性能表现对比(读/写操作、内存占用)

读操作性能
  • InnoDB

    • 主键查询:非常快(聚簇索引)
    • 二级索引查询:可能需要回表,相对较慢
    • 范围查询:利用B+树链表结构,性能较好
    • 并发读:通过MVCC支持高并发
  • MyISAM

    • 所有查询都需要通过索引定位到数据文件
    • 没有回表概念,但需要额外的I/O读取数据
    • 范围查询性能一般
    • 并发读性能好,但与写操作互斥
写操作性能
  • InnoDB

    • 插入:如果是顺序插入(自增主键),性能很好
    • 随机插入:可能导致页分裂,性能相对较差
    • 更新:支持事务,有额外的日志开销
    • 删除:逻辑删除,定期清理
  • MyISAM

    • 插入:通常在表尾插入,性能较好
    • 更新:表级锁,并发性能差
    • 删除:物理删除,但会产生碎片
内存占用
  • InnoDB

    • 使用缓冲池(Buffer Pool)缓存数据页和索引页
    • 内存占用相对较高,但缓存效果好
    • 自动管理内存,LRU算法淘汰页面
  • MyISAM

    • 只缓存索引,数据依赖操作系统缓存
    • 内存占用相对较低
    • 索引缓存大小由key_buffer_size控制
适用场景总结
  • 选择InnoDB的场景

    • 需要事务支持的应用
    • 高并发读写应用
    • 对数据一致性要求高
    • 需要崩溃恢复能力
    • 现代Web应用(推荐)
  • 选择MyISAM的场景

    • 以读为主的应用
    • 不需要事务支持
    • 对查询性能要求极高
    • 数据仓库、日志系统
    • 注意:MySQL 8.0默认存储引擎是InnoDB,MyISAM已不推荐使用

总结

InnoDB的索引系统是一个复杂而精妙的设计,其B+树结构、聚簇索引、以及各种优化机制使得它能够在保证事务ACID特性的同时,提供出色的查询性能。理解这些索引原理和最佳实践,对于数据库设计和性能优化具有重要意义。

在实际应用中,应该根据具体的业务场景选择合适的索引策略,平衡查询性能和维护成本,并通过持续的监控和优化来确保数据库的稳定运行。

http://www.dtcms.com/a/319588.html

相关文章:

  • 制作一款打飞机游戏87:最后冲刺
  • 如何提高云手机中数据信息的安全性?
  • MySQL 启动报错:InnoDB 表空间丢失问题及解决方法InnoDB: Tablespace 5975 was not found at
  • TikTok Shop冷启动破局战:亚矩阵云手机打造爆款账号矩阵
  • 云手机存在的意义是什么?
  • 你用的是什么键盘?
  • 【Java】Predicate使用案例
  • vnc远程连接VirtualBox中的Ubuntu24.04(xvfb,虚拟屏幕)
  • 什么是SpringBoot
  • OpenAI深夜开源2个推理模型gpt-oss,o4-mini水平,国内直接使用,笔记本/手机就能跑
  • 适用于个人开发、中小型项目的Embedding方案(配合ChromaDB)
  • 计算机毕业设计java疫情防控形势下的高校食堂订餐管理系统 高校食堂订餐管理系统在疫情防控背景下的设计与实现 疫情防控期间高校食堂线上订餐管理平台
  • Windows下Rust编码实现MP4点播服务器
  • 3a服务器的基本功能1之身份认证
  • iSCSI 服务器
  • Ubuntu设置Samba文件共享
  • Spring AI 打造智能面试人实战
  • Debian系统 为账号添加sudo权限
  • 通用优化软件GAMS的数学建模和优化分析
  • 快手提出强化学习创新框架RLEP,突破大模型推理瓶颈
  • AI算力平台统一监控方案:让AI算力资源透明化
  • 电线杆鸟巢识别误检率↓75%:陌讯多模态融合算法实战解析
  • 多线程问题,子线程同时操作全局变量,使用后需要清空吗 ?
  • Python生产环境部署指南:专业级应用启动方案
  • USRP 毫米波通信解决方案
  • SpringBoot如何固定版本
  • day069-Jenkins基础使用与参数化构建
  • 网络安全与软件定义汽车的发展
  • [spring-cloud: 动态刷新]-源码分析
  • k8s中pod如何调度?