第九章 联合索引(Compound Indexes)

本文介绍了MongoDB的联合索引,包括创建方法、索引排序和前缀匹配。联合索引顺序对查询效率至关重要,正确的顺序能优化排序操作。同时,文章探讨了索引前缀的概念,解释了如何利用索引前缀进行查询优化。

第九章 联合索引(Compound Indexes)

  • 创建联合索引
  • 排序
  • 前缀匹配

MongoDB 支持联合索引,联合索引的字段限制为 32个字段 。创建联合索引时,索引的顺序就显得比较重要了。

创建索引

你可以以下语法创建联合索引

db.collection_name.createIndex({<field>: <type>, <field>: <type>, ...})

当指定<type>为1时,意味着索引顺序是升序排序;<type>为-1时,意味着索引顺序降序。

重要
4.4版本

  • 联合索引可以包含一个哈希索引

4.2版本或更早版本

  • 联合索引不能包含哈希索引

假设这里有一个名叫 products 集合,其数据结构如下:

{
     "item": "Banana",
		 "category": ["food", "produce", "grocery"],
		 "location": "4th Street Store",
		 "stock": 5,
		 "type": "cases"
	},
	{
     "item": "Apple",
		 "category": ["food", "produce", "grocery"],
		 "location": "4th Street Store",
		 "stock": 3,
		 "type": "cases"
	},
	{
     "item": "Cherry",
		 "category": ["food", "produce", "grocery"],
		 "location": "4th Street Store",
		 "stock": 5,
		 "type": "cases"
	},
	{
     "item": "Banana",
		 "category": ["food", "produce", "grocery"],
		 "location": "4th Street Store",
		 "stock": 4,
		 "type": "cases"
	}

创建如下联合索引:

db.products.createIndex({"item": 1, "stock": 1})

联合索引字段的顺序是非常重要的。索引会首先根据第一个字段是排序,然后在第一个字段相同情况下,再根据第一个字段排序。

假设,我需要找到所有名为 Banana 水果,并根据 item 升序排序和 stock 排序。在没有联合索引时,我们需要使用 sort({...}) 进行排序;建立联合索引,按照 item 升序和 stock 升序查询,就不需要使用 sort({...})。查询语句如下:

db.products.find({"item": "Banana", "stock": {$lt: 10}})
索引排序(Sort Order)

索引排序顺序决定 sort({..}) 能否使用得上联合索引。

假设有一个 event 集合,包含了 usernamedate 字段。要返回结果根据 username 升序排序和 date 降序排序。查询语句如下

db.events.find({}).sort({username: 1, date: -1})

或着,要求返回结果根据 username 降序和 date 升序。

db.events.find({}).sort({username: -1, date: 1})

上面两种查询,这个所有都会使用到

db.events.createIndex( { "username" : 1, "date" : -1 } )

然后,要求返回结果根据 username 升序和 date 升序,那么索引不会被使用。

db.events.find({}).sort({username: 1, date: 1})

口诀:联合索引顺序的相反数,可以使用索引,反之,不行

假如创建一个 {username: 1, date: 1} 的联合索引
db.events.find({}).sort({username: -1, date: -1}) -- 可以使用索引
db.events.find({}).sort({username: -1, date: 1}) -- 不行
db.events.find({}).sort({username: 1, date: -1}) -- 不行
索引前缀

假设有这样一个联合索引:

{"item": 1, "location": 1, "stock": 1}

该索引的前缀有以下两种:

  • {item: 1}
  • {item: 1, location: 1}

索引是顺序被解析的,如果查询忽略联合索引部分字段,不能使用到索引前缀有以下几种:

  • {location: 1}
  • {stock: 1}
  • {location: 1, stock}
<think>我们正在讨论在Spring应用中为数据库表创建联合索引。根据引用内容,我们可以通过两种主要方式来实现:1.使用JPA注解(适用于SpringDataJPA项目)2.使用原生SQL脚本(适用于MyBatis或纯JDBC项目)下面分别给出示例:###方法1:使用JPA注解(SpringDataJPA)在实体类上使用`@Table`注解的`indexes`属性来定义联合索引。```javaimportjavax.persistence.*;importorg.hibernate.annotations.Index;//注意:Hibernate的@Index已过时,推荐使用JPA的@Table注解@Entity@Table(name="user_activity",indexes={@Index(name="idx_user_activity",columnList="user_id,activity_type",unique=false)})publicclassUserActivity{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)privateLongid;@Column(name="user_id")privateLonguserId;@Column(name="activity_type")privateStringactivityType;//其他字段...}```说明:-`@Index`注解位于`@Table`的`indexes`数组中,用于定义索引。-`name`属性指定索引名称。-`columnList`属性指定联合索引的字段,多个字段用逗号分隔。-`unique`属性表示是否为唯一索引。如果使用Hibernate作为JPA实现,在启动应用时,Hibernate会根据实体类自动生成或更新数据库索引。###方法2:使用原生SQL脚本(适用于任何持久层框架)在Spring中,我们可以通过以下几种方式执行SQL脚本创建索引:-在启动时通过`schema.sql`文件自动执行(SpringBoot)-使用`JdbcTemplate`执行DDL语句-使用数据库迁移工具(如Flyway或Liquibase)示例1:在SpringBoot中通过`schema.sql`创建索引在`src/main/resources/schema.sql`文件中添加:```sqlCREATEINDEXidx_user_activityONuser_activity(user_id,activity_type);```示例2:使用`JdbcTemplate`动态创建索引(通常在配置类或初始化Bean中执行)```javaimportorg.springframework.jdbc.core.JdbcTemplate;importorg.springframework.stereotype.Component;@ComponentpublicclassDatabaseInitializer{privatefinalJdbcTemplatejdbcTemplate;publicDatabaseInitializer(JdbcTemplatejdbcTemplate){this.jdbcTemplate=jdbcTemplate;}@PostConstructpublicvoidinit(){jdbcTemplate.execute("CREATEINDEXidx_user_activityONuser_activity(user_id,activity_type)");}}```示例3:使用Flyway迁移脚本在`src/main/resources/db/migration/V1__create_indexes.sql`文件中:```sqlCREATEINDEXidx_user_activityONuser_activity(user_id,activity_type);```###方法3:使用MyBatis-Plus在MyBatis-Plus中,通常建议通过数据库迁移工具或直接在数据库管理系统中创建索引。但也可以在实体类上使用注解(需要MyBatis-Plus3.5.0+版本):```javaimportcom.baomidou.mybatisplus.annotation.TableField;importcom.baomidou.mybatisplus.annotation.TableIndex;importcom.baomidou.mybatisplus.annotation.TableName;@TableName(value="user_activity",autoResultMap=true)@TableIndex(name="idx_user_activity",columns={"user_id","activity_type"})publicclassUserActivity{@TableIdprivateLongid;@TableField("user_id")privateLonguserId;@TableField("activity_type")privateStringactivityType;//其他字段...}```注意:MyBatis-Plus的`@TableIndex`注解主要用于自动建表(搭配`@TableName`和`@TableField`使用),在启动时调用`SqlSessionFactory`的`autoCreateTable`方法会自动创建表结构(包括索引)。但此功能通常用于开发环境,生产环境建议使用数据库迁移工具。###联合索引的最佳实践(引用[5])-联合索引遵循最左前缀原则:索引(a,b,c)只能用于查询条件包含a、a,b或a,b,c的情况。-范围查询(如`>`,`<`,`BETWEEN`)会导致其后的索引列失效。示例:索引`idx_user_activity(user_id,activity_type)`-有效查询:`WHEREuser_id=1`、`WHEREuser_id=1ANDactivity_type='LOGIN'`-无效查询:`WHEREactivity_type='LOGIN'`(没有使用user_id)###总结根据项目使用的持久层框架选择合适的方式:-SpringDataJPA:使用`@Table`注解定义索引-MyBatis/MyBatis-Plus:推荐使用Flyway/Liquibase或JdbcTemplate执行DDL-纯JDBC:在初始化时执行SQL脚本>注意:在生产环境中,直接通过应用程序创建索引可能存在风险,建议通过数据库迁移工具管理索引变更。[^1]:JPA和MyBatis-Plus中索引的常见管理方式[^2]:JPA中联合主键的示例(虽然问题问的是联合索引,但联合主键也是索引的一种特殊形式)[^5]:联合索引的使用原则§§相关问题§§1.SpringDataJPA中如何为联合索引指定升序/降序?2.在SpringBoot中如何通过配置自动生成数据库索引?3.联合索引与单列索引在查询性能上有何差异?4.使用Flyway管理索引变更的详细步骤是什么?5.MyBatis-Plus的@TableIndex注解支持哪些索引类型(唯一索引、全文索引等)?</think>在 Spring 应用中创建数据库联合索引主要有以下三种方式,适用于不同的技术栈: ### 方法 1:使用 JPA 注解(推荐用于 Spring Data JPA 项目) ```java import javax.persistence.*; @Entity @Table(name = "user_activity", indexes = @Index(name = "idx_user_activity", columnList = "user_id,activity_type")) public class UserActivity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "user_id") private Long userId; @Column(name = "activity_type") private String activityType; // 其他字段... } ``` **说明**: 1. `@Index` 注解定义在 `@Table` 的 `indexes` 属性中 2. `name` 指定索引名称(如 `idx_user_activity`) 3. `columnList` 定义联合索引字段,用逗号分隔多个字段 4. JPA 启动时会自动创建索引(需配置 `spring.jpa.hibernate.ddl-auto=update`) ### 方法 2:使用 Flyway 数据库迁移(推荐生产环境) 1. 在 `src/main/resources/db/migration` 创建 SQL 文件: ```sql -- V1__create_user_activity_table.sql CREATE TABLE user_activity ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL, activity_type VARCHAR(50) NOT NULL, activity_time TIMESTAMP ); -- V2__add_compound_index.sql CREATE INDEX idx_user_activity ON user_activity(user_id, activity_type); ``` 2. Spring Boot 配置(application.properties): ```properties spring.flyway.enabled=true spring.flyway.locations=classpath:db/migration ``` ### 方法 3:使用 JdbcTemplate 动态创建 ```java import org.springframework.jdbc.core.JdbcTemplate; @Service public class IndexService { private final JdbcTemplate jdbcTemplate; public IndexService(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } public void createCompoundIndex() { jdbcTemplate.execute("CREATE INDEX idx_user_activity " + "ON user_activity(user_id, activity_type)"); } } ``` ### 联合索引使用注意事项 1. **最左前缀原则**(引用[5]): ```sql -- 有效使用索引 SELECT * FROM user_activity WHERE user_id = 1001; SELECT * FROM user_activity WHERE user_id = 1001 AND activity_type = 'LOGIN'; -- 无法使用索引(缺少左侧字段) SELECT * FROM user_activity WHERE activity_type = 'LOGIN'; ``` 2. **索引排序**: ```java // JPA 中指定排序方式 @Index(name = "idx_time_desc", columnList = "activity_time DESC") ``` 3. **唯一索引**: ```java @Table(uniqueConstraints = @UniqueConstraint(columnNames = {"user_id", "activity_type"})) ``` ### 不同场景选择建议 | 场景 | 推荐方法 | 优势 | |----------------------|---------------|-------------------------| | Spring Data JPA 项目 | JPA 注解 | 自动维护,与实体绑定 | | 微服务/生产环境 | Flyway | 版本控制,安全可靠 | | 动态索引管理 | JdbcTemplate | 灵活控制创建时机 | > **最佳实践**:生产环境建议使用 Flyway 管理索引变更,避免直接通过应用修改数据库结构[^1][^3]。索引创建后使用 `EXPLAIN` 分析 SQL 执行计划验证索引是否生效[^5]。 [^1]: JPA 和 MyBatis-Plus 索引管理方式 [^2]: JPA 联合索引实体类示例 [^3]: ORM 框架中的索引管理 [^4]: JPA 唯一约束示例 [^5]: 联合索引生效规则和最左前缀原则
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值