为什么Mysql需要索引以及如何应用到项目中

索引

  • 优势:加速查询(减少磁盘 IO)、支持高效排序(减少 CPU 消耗)
  • 代价:占用存储空间、降低写入(INSERT/UPDATE/DELETE)速度

需要权衡读写比例和空间成本

索引概念

有序的数据结构,高效获取数据

优缺点

优势 劣势
提高数据检索效率,降低数据库IO成本 索引列是要占用空间
通过索引对数据进行排序,降低数据排序成本,降低CPU的消耗 提高效率的同时降低更新表的速度

实现

索引是在存储引擎层实现

结构

  • 二叉树

    顺序插入式会形成一个链表,查询性能大大降低,检索慢

  • B-Tree(多路平衡查找树)

    以一颗最大度数为5,每个节点最多存储4个key,5个指针

  • B±Tree

    所有节点都会在叶子节点中出现。在原树上,增加一个只想相邻子节点的链表指针,形成带有顺序指针的B+Tree,提高区间访问的性能

InnoDB存储引擎选择使用B+Tree

  • 二叉树如果顺序插入,就形成一个链表,时间复杂度为O(n),用红黑树也是二叉树。使用B+Tree层级更少,搜索效率更高
  • 对于B-Tree 的每个节点都存储数据,导致单页能存的键值更少,树更高;而 B+Tree 非叶子节点只存键和指针,叶子节点存数据,层级更低,I/O 更少
  • 相对与Hash索引,B+Tree支持范围匹配及排序操作:
    • 非叶子节点只存键+指针,叶子节点存完整数据
    • 叶子节点用链表连接 → 高效范围查询
    • 层级更低 → 查询更快(通常 3~4 层可存千万级数据)

索引使用

语法

  • 创建索引

    SQL 复制代码
    CREATE[UNIQUE|FULLTEXT] INDEX INDEX_NAME ON TABLE_NAME(INDEX_COL_NAME,...);

    **注意:**联合字段的话要在括号里写明,同时顺序也有考究

  • 查看索引

    SQL 复制代码
    SHOW INDEX FROM TABLE_NAME
  • 删除索引

    SQL 复制代码
    DROP INDEX INDEX_NAME ON TABLE_NAME;

1.最左前缀原则

必须从最左列开始连续使用才能命中索引:

mysql 复制代码
-- ✅ 命中索引
WHERE a = 1
WHERE a = 1 AND b = 2
WHERE a = 1 AND b = 2 AND c = 3

-- ❌ 不命中(跳过 a 或中间断开)
WHERE b = 2
WHERE a = 1 AND c = 3

2.回表&覆盖索引

回表:通过二级索引找到主键->再查聚簇索引拿完整数据(多一次IO)

覆盖索引:查询字段全部包含在索引中->直接返回,无需回表

主键id一般是实现回表查询

失效情况

场景 示例 是否失效
字符串不加引号 WHERE name = 123(name 是 VARCHAR) ✅ 失效
头部模糊匹配 WHERE name LIKE '%张' ✅ 失效 LIKE '张%' 不失效
OR 条件部分无索引 WHERE indexed_col = 1 OR no_index_col = 2 ✅ 整个失效
对索引列使用函数/运算 WHERE YEAR(create_time) = 2024 WHERE score + 10 > 100 ✅ 失效
  • 数据分布影响

    如果Mysql评估使用索引比全表更慢,那么不使用索引

    is null 和is not null要具体看数据分布

    对索引列本身使用函数(如 UPPER(col)、DATE(col))会导致该列索引失效;SELECT 或 ORDER BY 中的计算字段不会导致 WHERE 条件的索引失效,但这些计算字段本身无法使用索引进行排序或过滤。

实践

1.最左前缀原则

联合索引(a,b,c),查询条件必须从a开始才能命中索引

联合索引查找必须按顺序来

实践

在个人项目WenJi中,AI对话的ai_chat_message表中采用了最左前缀原则:

mysql 复制代码
-- ai_chat_session 表的复合索引
CREATE INDEX idx_ai_chat_session_user_status_time 
ON ai_chat_session(user_id, status, last_active_time);

能够实现:

mysql 复制代码
// 1. 使用全部三列 - 完全匹配
SELECT * FROM ai_chat_session 
WHERE user_id = 1 AND status = 'active' AND last_active_time > '2024-01-01';

// 2. 使用前两列 - 部分匹配
SELECT * FROM ai_chat_session 
WHERE user_id = 1 AND status = 'active';

// 3. 只使用第一列 - 部分匹配
SELECT * FROM ai_chat_session 
WHERE user_id = 1;

2.回表与覆盖查询

通过二级索引找到主键后,在回到聚集索引中获取完整数据行的过程

实践

ai_chat_message表中通过使用session_id索引,实现信息的查找

mysql 复制代码
-- ai_chat_message 表有索引 idx_session_id(session_id)
-- 这个查询会发生回表
SELECT * FROM ai_chat_message WHERE session_id = 'abc123';

执行流程

  1. 在 idx_session_id 二级索引中找到 session_id = 'abc123' 的记录
  2. 获取该记录的主键 message_id
  3. 拿着 message_id 去聚簇索引(主键索引)中查找完整行数据
  4. 返回所有字段 (*)

3.覆盖索引

索引中包含了查询所需的所有字段,数据库可以直接从索引获取全部数据,无需再访问表的数据行

这样避免了回表,减少I/O操作,效率提高

实践

在项目中我把返回所有值改成只返回需要的字段值

mysql 复制代码
-- ❌ 回表查询(需要获取所有字段)
SELECT * FROM ai_chat_message WHERE session_id = 'abc123';

-- ✅ 覆盖索引(只查询索引中包含的字段)
SELECT message_id, session_id, role, content, create_time 
FROM ai_chat_message 
WHERE session_id = 'abc123';

4.索引失效

对索引列进行函数运算或表达式计算

在项目中为了去实现用户进入景区游玩能相应获得积分的功能,我在sql语句中判断景点是否在规定的范围内的函数运算

sql 复制代码
-- HeritageSiteMapper.xml 中的距离计算
SELECT *, (6371 * acos(...)) AS distance 
FROM heritage_sites
WHERE status = 1
ORDER BY distance ASC;
相关推荐
Old Uncle Tom2 小时前
提示词编写规范
数据库·算法
l1t2 小时前
DeepSeek总结的Postgres 扩展天花板:当一个实例试图包揽一切时
数据库·postgresql
我要升天!2 小时前
C语言连接 MySQL:libmysqlclient 获取方式详解
c语言·开发语言·数据库·mysql·adb
roman_日积跬步-终至千里4 小时前
【系统架构师案例题-知识点】数据库与缓存设计
数据库·缓存·系统架构
不剪发的Tony老师4 小时前
DBcooper:一款面向开发者的现代数据库客户端
数据库·sql
小裕哥略帅4 小时前
mybaits跨表查询返回分页
mysql
添砖java‘’4 小时前
MYSQL数据类型
数据库·mysql
qq_372154234 小时前
如何配置表中某列的排序权重_全文索引配置与权重分配
jvm·数据库·python
2501_914245935 小时前
CSS如何使用-nth-of-type精确选择列表项_通过元素类型限制提升样式健壮性
jvm·数据库·python