Mysql数据库索引

Spring Data JPA建立索引所使用的语法

java 复制代码
@Entity
@Table(
  name = "user",
  indexes = {
    @Index(name = "idx_user_username", columnList = "username"),
    @Index(name = "idx_user_email_status", columnList = "email, status")
  },
  uniqueConstraints = {
    @UniqueConstraint(name = "uk_user_email_username", columnNames = {"email", "username"})
  }
)
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;
    private String email;
    private String status;
    // ... 其他字段
}

@Table(name = "user")

  • 指定实体类对应的数据库表名为 user
  • 如果不写 name,默认会根据实体类名推断(比如类名叫 User 就会对应 user 表或 User 表,具体与 JPA 实现有关)。

indexes = { ... }

  • 这里可以声明一个或多个普通索引(或非唯一索引)。
java 复制代码
indexes = {
  @Index(name = "idx_user_username", columnList = "username"),
  @Index(name = "idx_user_email_status", columnList = "email, status")
}
  • @Index(name = "idx_user_username", columnList = "username")
    • 代表在 username 字段上创建一个 普通索引 ,索引名为 idx_user_username
    • 这样在数据库层面会自动执行类似 CREATE INDEX idx_user_username ON user(username); 的操作。
  • @Index(name = "idx_user_email_status", columnList = "email, status")
    • 代表在 emailstatus 这两个字段上创建一个联合索引 (复合索引),索引名为 idx_user_email_status
    • 在数据库层面大致对应 CREATE INDEX idx_user_email_status ON user(email, status);

注意 :这些索引默认不是唯一索引 。如果你没有在注解里指定 unique = true,JPA 会把它当作普通索引创建。


uniqueConstraints = { ... }

  • 用来指定唯一性约束,可以是针对一个或多个列。
java 复制代码
uniqueConstraints = {
  @UniqueConstraint(name = "uk_user_email_username", columnNames = {"email", "username"})
}
  • 表示在 email + username 这两个字段上创建一个联合唯一约束 ,约束名为 uk_user_email_username
  • 数据库层面上会生成类似 ALTER TABLE user ADD CONSTRAINT uk_user_email_username UNIQUE(email, username); 的语句(具体实现可能因数据库和 JPA 实现不同而略有差异)。
  • 该约束可以确保同一张表中,不会出现 emailusername组合相同的两行数据。

区别于 indexes

  • uniqueConstraints 侧重的是唯一性约束。它不仅会在数据库中创建唯一索引,还会在表层面建立"约束"关系,不允许重复。
  • indexes 只是声明一个非唯一索引(除非加 unique=true),主要用于加速查询,不会强制数据唯一。

@Column(unique = true)

如果你只想创建唯一索引 (不一定是多字段),也可以直接在字段上用 @Column(unique = true)

java 复制代码
@Column(unique = true)
private String email;
  • 这样会自动生成一个单字段唯一索引(约束)

一、按「数据结构」分类

1.1 B+Tree 索引

1.1.1 特点与应用场景

  • 特点

    • B+Tree 是 MySQL(InnoDB/MyISAM 等多数引擎) 中最常用的索引结构。
    • 支持 等值 查询和 范围 查询 (BETWEEN, <, >, ORDER BY, 等)。
    • 查询性能稳定,适合多数增删改查需求。
  • 与其他分类的交叉

    • 物理存储:InnoDB 中的主键索引(聚簇索引)和二级索引都是 B+Tree。
    • 字段特性:主键、唯一、普通、前缀索引都可由 B+Tree 来实现。
    • 字段个数:可做单列或联合索引,底层都是 B+Tree。

1.1.2 Spring Data JPA 中如何使用 B+Tree 索引

InnoDB 引擎下,所有主键、唯一索引、普通索引默认就是 B+Tree 实现。只要我们在实体类里用以下方式指定索引,即可默认得到 B+Tree:

主键索引(B+Tree + 聚簇)

java 复制代码
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // ...
}
  • 当表是 InnoDB 时,主键索引就是聚簇索引,底层是 B+Tree。

唯一索引(B+Tree 二级索引)

java 复制代码
@Column(unique = true)
private String email;
  • 这样会自动为 email 字段创建一个唯一索引,同样是 B+Tree。

普通索引(B+Tree 二级索引)

java 复制代码
@Table(
    indexes = {
        @Index(name = "idx_user_username", columnList = "username")
    }
)
@Entity
public class User {
    @Id
    private Long id;

    private String username;
    // ...
}
  • 使用 @Index 注解在 @Table 中声明普通索引,底层也是 B+Tree。

联合索引(B+Tree)

java 复制代码
@Table(
    indexes = {
        @Index(name = "idx_user_email_status", columnList = "email, status")
    }
)
  • 声明复合(联合)索引时,依然是 B+Tree。

小结 :在 Spring Data JPA 里,正常使用 @Id/@Column(unique=true)/@Table(indexes=...) 就已经默认是 B+Tree,无需额外指定。

1.1.3 MyBatis-Plus 中如何使用 B+Tree 索引

MyBatis-Plus 并没有像 JPA 那样的注解去自动创建索引。常见做法有两种:

在数据库脚本或 Flyway/Liquibase 中显式创建

sql 复制代码
CREATE TABLE user (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(100),
    email VARCHAR(100),
    status VARCHAR(20)
    -- ...
) ENGINE=InnoDB;

-- 普通索引
CREATE INDEX idx_user_username ON user(username);
-- 唯一索引
CREATE UNIQUE INDEX uk_user_email ON user(email);
-- 联合索引
CREATE INDEX idx_user_email_status ON user(email, status);
  • 只要引擎是 InnoDB/MyISAM,默认索引结构即 B+Tree。

在 Mapper 接口中执行原生 SQL

java 复制代码
@Mapper
public interface UserMapper extends BaseMapper<User> {

    @Select("CREATE INDEX idx_user_username ON user(username)")
    void createIndexOnUsername();

    @Select("CREATE UNIQUE INDEX uk_user_email ON user(email)")
    void createUniqueIndexOnEmail();

    @Select("CREATE INDEX idx_user_email_status ON user(email, status)")
    void createIndexOnEmailStatus();
}
  • 这会在运行时执行建索引的 SQL,底层同样是 B+Tree(只要数据引擎是 InnoDB/MyISAM)。

1.2 Hash 索引

1.2.1 特点与应用场景

  • 特点
    • 基于哈希表,只适合等值查询,不支持范围查询、排序等。
    • 只在 MEMORY 存储引擎中可用(InnoDB 并不支持手动创建 Hash 索引)。
    • 在生产环境中不常见,除非有极少量、频繁等值查询的数据。

1.2.2 Spring Data JPA 中的操作

  • Spring Data JPA 没有专门的注解来指定 "Hash 索引"。
  • 如果一定要在一个 MEMORY 表上创建 Hash 索引,需要将表改成 MEMORY 引擎,再用原生 SQL 创建:
java 复制代码
public interface UserRepository extends JpaRepository<User, Long> {

    @Query(value = "ALTER TABLE user ENGINE=MEMORY", nativeQuery = true)
    @Modifying
    void convertToMemory();

    @Query(value = "ALTER TABLE user ADD KEY USING HASH (username)", nativeQuery = true)
    @Modifying
    void createHashIndex();
}
  • 这种情况十分少见,一般不做推荐。

1.2.3 MyBatis-Plus 中的操作

同样,需要把表引擎改成 MEMORY,然后再执行建索引语句:

java 复制代码
@Mapper
public interface UserMapper extends BaseMapper<User> {

    @Select("ALTER TABLE user ENGINE=MEMORY")
    void convertToMemoryEngine();

    @Select("ALTER TABLE user ADD KEY USING HASH (username)")
    void createHashIndexOnUsername();
}
  • 由于场景极少,建议仅在极端情况下使用。

1.3 Full-Text 索引

1.3.1 特点与应用场景

  • 特点
    • 用于对文本字段(CHAR、VARCHAR、TEXT)进行全文检索
    • MyISAM 引擎最早支持,InnoDB 在 5.6+ 版本也支持。
    • 需要配合 MATCH ... AGAINST 语法来进行全文搜索。

1.3.2 Spring Data JPA 中的操作

  • Spring Data JPA 没有"自动"创建全文索引的注解。

  • 需要使用原生 SQL 来创建:

    java 复制代码
    public interface ArticleRepository extends JpaRepository<Article, Long> {
    
        @Query(value = "ALTER TABLE article ADD FULLTEXT INDEX idx_content(content)", nativeQuery = true)
        @Modifying
        void createFullTextIndexOnContent();
    }
  • 然后查询时:

    java 复制代码
    @Query(value = "SELECT * FROM article WHERE MATCH(content) AGAINST(?1 IN NATURAL LANGUAGE MODE)", nativeQuery = true)
    List<Article> searchByContent(String keywords);

1.3.3 MyBatis-Plus 中的操作

同理,在 MyBatis-Plus 中也需执行原生 SQL

java 复制代码
@Mapper
public interface ArticleMapper extends BaseMapper<Article> {

    @Select("ALTER TABLE article ADD FULLTEXT INDEX idx_content(content)")
    void createFullTextIndex();

    @Select("SELECT * FROM article WHERE MATCH(content) AGAINST(#{keywords} IN NATURAL LANGUAGE MODE)")
    List<Article> searchByContent(String keywords);
}

二、按「物理存储」分类

2.1 聚簇索引(Clustered Index / 主键索引)

2.1.1 特点与应用场景

  • InnoDB 中的主键索引即为聚簇索引

    数据文件按主键顺序组织存储,主键索引本身就是一棵 B+Tree,叶子节点存的就是整行数据

  • 与其他分类维度的交叉

    • 若显式声明了主键 (PRIMARY KEY),那么这就是表的聚簇索引。
    • 如果主键是单列,则它既是单列索引;如果主键由多列组成,则是联合索引。
    • 除了主键索引外,其他都是「二级索引」。

2.1.2 Spring Data JPA 中的操作

  • 只要 你在实体类中通过 @Id 声明主键,并使用 InnoDB 引擎,就会自动成为聚簇索引(无需额外设置):

    java 复制代码
    @Entity
    @Table(name = "user")
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id; // InnoDB主键 => 聚簇索引
    
        private String username;
        // ...
    }
  • 如果是联合主键 ,也依旧是聚簇索引,比如使用 @IdClass@EmbeddedId

2.1.3 MyBatis-Plus 中的操作

  • MyBatis-Plus 并不"帮"你创建表或索引,只要你在数据库里指定:

    sql 复制代码
    CREATE TABLE user (
      id BIGINT PRIMARY KEY AUTO_INCREMENT,
      username VARCHAR(100),
      ...
    ) ENGINE=InnoDB;
    • 这时 id 就是聚簇索引。
  • 在实体类中也会有对应的 @TableId 标识主键,但它并不自动建索引,而是依赖数据库表本身:

    java 复制代码
    @TableName("user")
    public class User {
        @TableId(type = IdType.AUTO)
        private Long id;
        private String username;
        // ...
    }

2.2 二级索引(Secondary Index / 辅助索引)

2.2.1 特点与应用场景

  • 辅助索引

    • 在 InnoDB 中,非主键的索引(唯一索引、普通索引等)都属于二级索引。
    • 二级索引是一棵独立的 B+Tree,叶子节点存的是对应行的「主键值」,需要再回表到聚簇索引查数据。
  • 与其他分类维度的交叉

    • 可以是唯一索引或普通索引,也可以是单列或联合,底层都是 B+Tree 结构。
    • 比如 CREATE UNIQUE INDEXCREATE INDEX 都属于创建二级索引。
  • 二级索引的叶子节点存储的是主键值 ,然后通过主键值再去聚簇索引定位行数据(回表)。

2.2.2 Spring Data JPA 中的操作

  • 唯一索引普通索引 都是二级索引(只要不是主键)。

  • 示例(普通索引):

    java 复制代码
    @Table(
      name = "user",
      indexes = {
        @Index(name = "idx_user_username", columnList = "username")
      }
    )
    @Entity
    public class User {
        @Id
        private Long id; // 主键(聚簇)
    
        private String username; // 二级索引
        // ...
    }
    • 这里 idx_user_username 就是一个二级索引。

2.2.3 MyBatis-Plus 中的操作

  • 同样需要手动 写 SQL 或通过数据库脚本:

    sql 复制代码
    CREATE INDEX idx_user_username ON user(username);
  • 在 MyBatis-Plus 的实体中并没有注解去标明"这是二级索引",只能说非主键 的索引默认就是二级索引(InnoDB):

    java 复制代码
    @Select("CREATE INDEX idx_user_username ON user(username)")
    void createIndexUsername();

三、按「字段特性」分类

3.1 主键索引

3.1.1 特点与应用场景

  • 唯一且不能为空
    • 一张表只能有一个主键索引,它在 InnoDB 中就是聚簇索引。
  • 与其他分类维度的交叉
    • 底层数据结构:B+Tree;
    • 也是聚簇索引;
    • 可以是单列或多列(联合主键)。
  • 主键不能为空,通常自增或 UUID 等。

3.1.2 Spring Data JPA 中的操作

  • 直接 @Id 即是主键索引:

    java 复制代码
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
  • 或者在表层面指定:

    java 复制代码
    @Entity
    @Table(name = "user")
    public class User {
        @Id
        private Long id; 
        // ...
    }

3.1.3 MyBatis-Plus 中的操作

  • 数据库里定义 PRIMARY KEY:

    java 复制代码
    CREATE TABLE user (
        id BIGINT PRIMARY KEY AUTO_INCREMENT,
        ...
    ) ENGINE=InnoDB;
  • 实体类:

    java 复制代码
    @TableId(type = IdType.AUTO)
    private Long id;
  • 就完成了主键索引的声明。


3.2 唯一索引

3.2.1 特点与应用场景

  • 保证字段值不重复
    • 允许多列一起唯一,也允许为 NULL(有些场景多个 NULL 可视为不冲突)。
  • 与其他分类维度的交叉
    • 底层通常是 B+Tree;
    • 若非主键,则它就是一个「二级索引」。
    • 可以是单列或联合索引。

3.2.2 Spring Data JPA 中的操作

单列唯一

java 复制代码
@Column(unique = true)
private String email;
  • 这会自动生成一个唯一索引。

联合唯一

java 复制代码
@Table(
  uniqueConstraints = {
    @UniqueConstraint(name = "uk_user_email_username", columnNames = {"email", "username"})
  }
)
@Entity
public class User {
    @Id
    private Long id;
    private String email;
    private String username;
}
  • 会在 (email, username) 上创建一个唯一索引。

3.2.3 MyBatis-Plus 中的操作

  • 同样需要手工 或脚本:

    sql 复制代码
    CREATE UNIQUE INDEX uk_user_email ON user(email);
    CREATE UNIQUE INDEX uk_user_email_username ON user(email, username);
  • 或在 Mapper 接口中:

    java 复制代码
    @Select("CREATE UNIQUE INDEX uk_user_email ON user(email)")
    void createUniqueIndexEmail();
    
    @Select("CREATE UNIQUE INDEX uk_user_email_username ON user(email, username)")
    void createUniqueIndexEmailUsername();

3.3 普通索引

3.3.1 特点与应用场景

  • 最常见,提升查询速度,没有唯一性约束。
  • 可用于经常在 WHERE 条件或 JOIN 条件中的列。

3.3.2 Spring Data JPA 中的操作

  • 通过 @Table(indexes = {...})

    java 复制代码
    @Table(
      name = "user",
      indexes = {
        @Index(name = "idx_user_username", columnList = "username")
      }
    )
    @Entity
    public class User {
        @Id
        private Long id;
    
        private String username;
        // ...
    }

3.3.3 MyBatis-Plus 中的操作

  • 还是手动 SQL:

    sql 复制代码
    CREATE INDEX idx_user_username ON user(username);
  • 或者在 Mapper 接口:

    sql 复制代码
    @Select("CREATE INDEX idx_user_username ON user(username)")
    void createIndexUsername();

3.4 前缀索引

3.4.1 特点与应用场景

  • 字符串类型的前 N 个字符建立索引,减少索引体积,适合超长字段。
  • 会降低搜索精度(可能会有更多回表操作),仅在部分情况下使用。

3.4.2 Spring Data JPA 中的操作

  • 没有 注解可指定 "前缀长度",需要原生 SQL 或 DDL 脚本:

    sql 复制代码
    public interface UserRepository extends JpaRepository<User, Long> {
        
        @Query(value = "CREATE INDEX idx_prefix_username ON user (username(10))", nativeQuery = true)
        @Modifying
        void createPrefixIndex();
    }
  • 或者在建表脚本里提前写:

    sql 复制代码
    CREATE INDEX idx_prefix_username ON user (username(10));

3.4.3 MyBatis-Plus 中的操作

  • 同理,原生 SQL

    java 复制代码
    @Select("CREATE INDEX idx_prefix_username ON user(username(10))")
    void createPrefixIndex();
  • 或者在数据库层面定义。


四、按「字段个数」分类

4.1 单列索引

4.1.1 特点与应用场景

针对单个字段创建

  • 可以是主键索引、唯一索引或普通索引。
  • 一张表可以有多个不同字段的单列索引。

4.1.2 Spring Data JPA 中的操作

  • 常见写法:

    java 复制代码
    @Table(
      indexes = @Index(name="idx_user_username", columnList="username")
    )
    @Entity
    public class User {
        @Id
        private Long id;
    
        // username 单列索引
        private String username;
    }

4.1.3 MyBatis-Plus 中的操作

  • 依旧SQLMapper

    java 复制代码
    @Select("CREATE INDEX idx_user_username ON user(username)")
    void createIndexUsername();

4.2 联合索引(复合索引)

4.2.1 特点与应用场景

针对多个字段的组合创建

  • 可以是联合主键索引、联合唯一索引或普通联合索引。
  • 在联合索引中,需要注意"最左前缀原则",即只有在按"索引最左字段"进行查询时,索引才会被有效利用。

4.2.2 Spring Data JPA 中的操作

  • @IndexcolumnList 中写多个字段:

    java 复制代码
    @Table(
      indexes = {
        @Index(name = "idx_user_email_status", columnList = "email, status")
      }
    )
    @Entity
    public class User {
        @Id
        private Long id;
        private String email;
        private String status;
        // ...
    }
  • 或在 @UniqueConstraint(columnNames = {"col1", "col2"}) 中声明联合唯一

最左前缀原则: 从左到右依次匹配索引列,中间不能跳过;一旦遇到范围查询或被跳过的列,后续列就无法继续利用联合索引进行快速定位 。因此,在设计联合索引时,要结合最常用的查询方式,把过滤频率最高最常作为等值过滤 的列放到最左,才能最大化利用索引加速查询。

java 复制代码
CREATE INDEX idx_col1_col2_col3 ON table_name(col1, col2, col3);
  • 必须从索引的最左列开始匹配

    • 以上例中,最左列是 col1。如果在查询时 没有 使用 col1 进行过滤或连接条件,那么即便条件中包含了 col2col3,也无法有效利用这个联合索引。
    • 例如,WHERE col1 = ... 可以用索引;WHERE col1 = ... AND col2 = ... 可以继续利用索引更深层次的匹配;但如果只是 WHERE col2 = ...,MySQL 无法使用 idx_col1_col2_col3 的第一列 col1 进行定位,就不能走该索引的高效检索。
  • 索引可部分匹配,但只能持续到出现"断裂"

    • 对联合索引 (col1, col2, col3) 来说,如果查询只使用了 col1,MySQL 仍能使用索引里 (col1) 这部分来加速查询。
    • 如果查询中使用了 col1col2,但忽略了 col3,那么 MySQL 可以利用索引的前两列 (col1, col2) 进行匹配。
    • 如果跳过 了中间一列,比如只用 col1col3(而 col2 不出现在查询条件中),那么对于 col3 的过滤很难直接走这条索引,因为在索引排序里 col2 位于 col1col3 中间,造成"断裂"。
  • 对范围条件的影响

    • 若在 col1 上使用了范围查询(如 col1 BETWEEN 10 AND 20),则只能对 col1 进行索引检索,并且无法 再对后续 col2col3 继续使用索引精准匹配。因为范围扫描一旦开始,后续的精准匹配就无法发挥作用。
    • 这也是最左前缀原则的延伸:联合索引后续字段的利用,往往需要前一列是等值匹配(=)才能继续深度使用。

4.2.3 MyBatis-Plus 中的操作

  • 还是原生 SQL:

    sql 复制代码
    CREATE INDEX idx_user_email_status ON user(email, status);
  • 或者:

    java 复制代码
    @Select("CREATE INDEX idx_user_email_status ON user(email, status)")
    void createUnionIndexOnEmailStatus();

五、小结

  • 实际开发中最常用的索引:

    • B+Tree 实现的主键索引、唯一索引、普通索引和联合索引。这基本覆盖了 90%+ 的应用场景。
    • 在 Spring Data JPA 中,利用 @Id@Column(unique=true)@Table(indexes=..., uniqueConstraints=...) 已能满足绝大部分需求。
    • 在 MyBatis-Plus 中,多使用数据库脚本或 Mapper 接口原生 SQL 来管理索引。
  • Hash 索引

    • 因为只能在 MEMORY 引擎下使用,而且限制很多(不支持范围查询、无法排序等),生产中极少使用。除非需要一些极端高并发且数据量很小、仅仅等值查询的场景。
  • Full-Text 索引

    • 如果需要"搜索功能",可以考虑 MySQL 自带全文索引。但通常更推荐使用专业搜索引擎(如 Elasticsearch、Solr 等),在索引和查询性能上更强大。
    • 如果只是简单的搜索,可以用 MySQL Full-Text 索引,但要掌握 MATCH ... AGAINST 的使用和各种搜索模式。
  • 聚簇索引 = InnoDB 主键索引,只有一个;二级索引 = 非主键索引(可唯一可普通可联合)。

  • 主键、唯一、普通、前缀 这四类索引可以任意组合到单列或多列中,但前缀索引只对字符串列有效。

  • Spring Data JPA 提供了一些方便的注解:

    • @Id / @Column(unique=true) / @Table(indexes=..., uniqueConstraints=...)
    • 特殊索引(Hash、Full-Text、前缀)要用原生 SQL。
  • MyBatis-Plus 不会自动创建索引,需要:

    • SQL脚本 / Mapper接口中原生SQL / DB初始化工具(Flyway, Liquibase)
    • 只要数据库层面创建了对应索引,即可在 MyBatis-Plus 中正常使用。
相关推荐
一只小H呀の21 分钟前
接口测试Day09-数据库工具类封装
数据库·oracle
你的眼睛會笑22 分钟前
uniapp 绘制五星评分 精确到半星
数据库·uni-app
爱上语文36 分钟前
MyBatis——XML映射文件
xml·java·数据库·后端·mybatis
qiandeqiande2 小时前
MYSQL5.7 全文检索中文无返回数据
mysql·全文检索
Run Out Of Brain3 小时前
MySQL程序之:指定程序选项
数据库·mysql·adb
weisian1513 小时前
Mysql--运维篇--安全性(数据库访问控制,最小权限原则,表空间加密,TLS加密,证书签发,SQL注入及防范等)
运维·数据库·mysql
ZVAyIVqt0UFji3 小时前
360AI平台资源可视化建设
数据库·搜索引擎
終不似少年遊*4 小时前
通过一个算法的设计来了解栈的一些应用
java·前端·数据库
wishfly5 小时前
数据库重连 - 方案
数据库
梦仔生信进阶6 小时前
基于R计算皮尔逊相关系数
前端·数据库·r语言