MySQL索引总结(小白也能看懂)

索引划分

按照底层存储方式角度划分:

  • 聚簇索引:索引结构和数据一起存放的索引,InnoDB 中的主键索引就属于聚簇索引。
  • 非聚簇索引(又称二级索引):索引结构和数据分开存放的索引,InnoDB中除主键索引外都是非聚簇索引。MySQLMyISAM 引擎,不管主键还是非主键,使用的都是非聚簇索引。

按照应用维度划分:

类型说明
主键索引(Primary Key表的主键,唯一且非空,默认是聚簇索引
唯一索引(Unique Index索引列的值必须唯一,但允许空值
普通索引(Index / Key最基本的索引类型,没有唯一性约束
联合索引(Composite Index多个列组成的索引
前缀索引(Prefix Index针对字符串字段的部分前缀创建索引,节省空间
全文索引(Fulltext Index针对文本搜索,适用于 TEXT 类型
空间索引(Spatial Index用于地理空间数据(MyISAM支持,InnoDB部分支持)

主键索引(Primary Key)

数据表的主键列使用的就是主键索引。一张数据表有只能有一个主键,并且主键不能为 null,不能重复。

MySQLInnoDB 的表中,当没有显示的指定表的主键时,InnoDB 会自动先检查表中是否有唯一索引且不允许存在 null 值的字段,如果有,则选择该字段为默认的主键,否则 InnoDB 将会自动创建一个 6Byte 的自增主键。

二级索引

二级索引(Secondary Index)的叶子节点存储的数据是主键的值并不是数据行,也就是说,通过二级索引可以定位主键的位置

  1. 唯一索引:唯一索引也是一种约束。唯一索引的属性列不能出现重复的数据,但是允许数据为 NULL,一张表允许创建多个唯一索引。 建立唯一索引的目的大都是为了该属性列的数据的唯一性,而不是为了查询效率。

    CREATE TABLE users (
      email VARCHAR(100) UNIQUE
    );
    CREATE UNIQUE INDEX idx_email ON users(email);
    
  2. 普通索引:普通索引的唯一作用就是为了快速查询数据**,一张表允许创建多个普通索引,并允许数据重复和 NULL。

    CREATE TABLE users (
      age INT,
      INDEX idx_age (age)
    );
    CREATE INDEX idx_age ON users(age);
    
  3. 联合索引:使用表中的多个字段创建索引,也叫 组合索引复合索引

    例如:以 scorename 两个字段建立联合索引:

    CREATE TABLE orders (
      user_id INT,
      product_id INT,
      INDEX idx_user_product (user_id, product_id)
    );
    ALTER TABLE `orders` ADD INDEX iidx_user_product(user_id, product_id);
    
  4. 前缀索引:前缀索引只适用于字符串类型的数据。前缀索引是对文本的前几个字符创建索引,相比普通索引建立的数据更小,因为只取前几个字符。

    CREATE TABLE files (
      filename VARCHAR(255),
      INDEX idx_filename_prefix (filename(20))
    );
    # 表示只对前 20 个字符建立索引。
    
  5. 全文索引:全文索引主要是为了检索大文本数据中的关键字的信息,支持分词匹配,不是简单的 LIKE %xxx%;适用于 TEXT, VARCHAR 字段

    CREATE TABLE articles (
      content TEXT,
      FULLTEXT INDEX idx_content (content)
    );
    # 使用时
    SELECT * FROM articles WHERE MATCH(content) AGAINST('mysql');
    

聚簇索引与非聚簇索引的区别

聚簇索引中叶子结点存放数据行本身、而非聚簇索引中叶子结点存放的是主键值,前者的查询速度要更快,因为后者查询可能需要回表(不需要回表就涉及后面的覆盖索引了);举个例子:

# 创建一个表
CREATE TABLE user (
  id INT PRIMARY KEY,
  username VARCHAR(50),
  email VARCHAR(100),
  INDEX idx_username(username)  -- 非聚簇索引
) ENGINE=InnoDB;
# 索引结构
idx_username B+Tree(非聚簇):
"alice"  -> 101
"bob"    -> 102

聚簇索引 B+Tree(主键):
101 -> { id:101, username:"alice", email:"a@example.com" }
102 -> { id:102, username:"bob", email:"b@example.com" }

# 执行查询
SELECT email FROM user WHERE username = 'alice';

# 查询流程:
username = 'alice' → 找到主键 101 → 回主键索引 → 取出 email
# 找到101之后去主键索引中查找对应数据行的过程就是回表

覆盖索引

如果一个索引包含所有需要查询的字段的值,我们就称之为 覆盖索引(Covering Index ,还是上面这个例子:

-- 我加上 email 字段的联合索引
CREATE INDEX idx_username_email ON user(username, email);

idx_username_email B+Tree(非聚簇):
("alice""a@example.com"-> 101"bob""b@example.com"-> 102

-- 此时这个查询也能被覆盖索引命中,直接返回"a@example.com",不回表
SELECT email FROM user WHERE username = 'alice';

如何避免索引失效

索引失效也是慢查询的主要原因之一,常见的导致索引失效的情况有下面这些:

  • 创建了联合索引,但查询条件未遵守最左匹配原则;

    最左前缀匹配原则指的是在使用联合索引时,MySQL 会根据索引中的字段顺序,从左到右依次匹配查询条件中的字段。如果查询条件与索引中的最左侧字段相匹配,那么 MySQL 就会使用索引来过滤数据,这样可以提高查询效率。

    假设有一个联合索引(column1, column2, column3),其从左到右的所有前缀为(column1)(column1, column2)(column1, column2, column3)(创建 1 个联合索引相当于创建了 3 个索引),包含这些列的所有查询都会走索引而不会全表扫描。

    范围查询(例如 ><BETWEENLIKE '%xx')会中断匹配链,例如:

    CREATE INDEX idx_abc ON my_table(a, b, c);
    SELECT * FROM my_table WHERE a = 1 AND b = 2 AND c = 3;-- ✅  a 、b、c都命中
    SELECT * FROM my_table WHERE a = 1 AND b > 2 AND c = 3;-- ✅ 只命中 a 和 b,c无效
    

    在联合索引(a,b,c)的B+树中,叶子节点中的排列顺序是先按 a 排序,再按 b 排序,再按 c 排序。
    b > 2b的值有无限多种可能;所以不能跳过这些不定的 b 去精确定位 c=3 的位置,只能全量扫描所有符合 a=1 且 b>2 的记录,再筛 c=3

  • 在索引列上进行计算、函数、类型转换等操作;

  • 以 % 开头的 LIKE 查询比如 LIKE '%abc';但是这种LIKE 'abc%'是不会导致索引失效的

索引的排列顺序是根据比较字符串的首字母排序的,如果首字母相同,就根据比较第二个字母进行排序,以此类推。我们在进行模糊查询的时候,如果把 % 放在了前面,最左的 n 个字母便是模糊不定的,无法根据索引的有序性准确的定位,只能进行全表扫描,找出符合条件的数据。

  • 查询条件中使用 OR,且 OR 的前后条件中有一个列没有索引,涉及的索引都不会被使用到;

  • IN 的取值范围较大时会导致索引失效,走全表扫描(NOT IN 和 IN 的失效场景相同);

  • 发生隐式转换时(当操作符左右两边的数据类型不一致时,会发生隐式转换

    -- 假设 create_time 是 DATETIME 类型,有索引
    SELECT * FROM orders WHERE create_time = '20250422';
    -- '20250422'是一个字符串;MySQL 会尝试把它转成 DATETIME,实际变成 '2025-04-22 00:00:00';
    

适合与不适合创建索引的列

不适合创建索引的场景

  1. 低基数列(有很多重复值):如性别(只有男/女),索引效果差。
  2. 频繁更新的列:索引维护成本高,影响写入性能。
  3. 小表(数据量少):全表扫描可能更快。
  4. 不参与查询的列:无意义的索引会浪费资源。

适合创建索引的场景:

  1. 高基数列(如用户ID、订单号)
  2. 如状态(status)、时间(create_time)等经常用于查询条件的列
  3. 经常用作表连接的字段
  4. 经常出现在ORDER BY/GROUP BY/DISDINCT后面的字段
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liubo666_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值