目录
- 为什么需要索引?
- 创建索引的核心方法
- 完整代码示例
- 创建不同类型的索引
- 单字段索引
- 复合索引
- 唯一索引
- TTL 索引 (自动过期)
- 文本索引
- 地理空间索引
- 索引管理
- 查看索引
- 删除索引
- 最佳实践与注意事项
为什么需要索引?
想象一下没有索引的 MongoDB 集合就像一本没有目录的书,如果你想找到特定内容("第 50 页"),你必须一页一页地翻阅(全表扫描),这非常慢。
索引 就像这本书的目录,它维护了一个特定字段或多个字段值的排序列表,并指向包含这些值的文档,当你在查询中使用了被索引的字段时,MongoDB 可以直接跳转到相关数据,而无需扫描整个集合,从而极大地提高查询速度。
权衡:
- 优点:查询速度极快,特别是对于
sort()、match()和equal查询。 - 缺点:
- 写入开销:每次插入、更新或删除文档时,MongoDB 都需要更新索引,这会降低写入速度。
- 存储空间:索引本身会占用额外的磁盘空间。
只为经常用于查询、排序或过滤的字段创建索引。
创建索引的核心方法
在 Java 驱动中,创建索引主要通过 MongoCollection 对象的 createIndex() 方法实现。
// collection 是你的 MongoCollection<Document> 对象 collection.createIndex(indexes, options);
indexes: 一个Indexes类的辅助方法,用于定义索引的字段和排序方向。Indexes.ascending("fieldName"): 创建升序索引。Indexes.descending("fieldName"): 创建降序索引。Indexes.compoundIndex(Indexes.ascending("field1"), Indexes.descending("field2")): 创建复合索引。
options: 一个IndexOptions对象,用于配置索引的高级选项,如唯一性、过期时间、名称等。
完整代码示例
这是一个完整的、可运行的 Java 示例,展示了如何连接到 MongoDB 并为 users 集合的 username 字段创建一个索引。
前提条件:
- 已安装 Java 开发环境。
- 已安装并运行 MongoDB。
- 在你的
pom.xml(Maven) 或build.gradle(Gradle) 中添加 MongoDB Java 驱动依赖。
Maven 依赖 (pom.xml):
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>4.11.1</version> <!-- 请使用最新版本 -->
</dependency>
Java 代码:
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Indexes;
import org.bson.Document;
public class CreateIndexExample {
public static void main(String[] args) {
// 1. 创建 MongoClient (推荐使用连接字符串)
String uri = "mongodb://localhost:27017";
try (MongoClient mongoClient = MongoClients.create(uri)) {
// 2. 获取数据库和集合
MongoDatabase database = mongoClient.getDatabase("myDatabase");
MongoCollection<Document> collection = database.getCollection("users");
System.out.println("成功连接到 MongoDB 并获取集合 'users'.");
// 3. 创建索引
// 为 'username' 字段创建一个升序索引
collection.createIndex(Indexes.ascending("username"));
System.out.println("为 'username' 字段成功创建索引!");
// 4. (可选) 验证索引是否创建成功
System.out.println("集合 'users' 的所有索引:");
collection.listIndexes().forEach(System.out::println);
} catch (Exception e) {
System.err.println("发生错误: " + e.getMessage());
e.printStackTrace();
}
}
}
运行此代码后,你可以在 MongoDB 的 myDatabase 数据库中,users 集合上看到一个名为 username_1 的新索引。
创建不同类型的索引
单字段索引
最简单的索引,只基于一个字段。
// 为 'email' 字段创建升序索引
collection.createIndex(Indexes.ascending("email"));
复合索引
基于多个字段的索引,可以显著提高多条件查询的性能,字段的顺序非常重要。
// 创建一个复合索引: 先按 'status' 升序,再按 'createdDate' 降序
// 这非常适合查询 "状态为 'active' 的用户,并按创建时间倒序排列"
collection.createIndex(Indexes.compoundIndex(
Indexes.ascending("status"),
Indexes.descending("createdDate")
));
唯一索引
确保集合中所有文档在指定字段上的值都是唯一的,常用于用户名、邮箱、手机号等。
import com.mongodb.client.model.IndexOptions;
// 为 'email' 字段创建唯一索引
IndexOptions options = new IndexOptions().unique(true);
collection.createIndex(Indexes.ascending("email"), options);
TTL 索引 (Time-To-Live)
用于自动在指定时间后删除文档,非常适合实现会话过期、日志清理等功能。只能创建在日期类型(Date 或 BSON Timestamp)的字段上。
import com.mongodb.client.model.IndexOptions;
import java.util.concurrent.TimeUnit;
// 假设 'lastAccessedAt' 是一个 Date 类型字段
// 创建一个 TTL 索引,文档在 'lastAccessedAt' 时间后 3600 秒(1小时)被删除
IndexOptions options = new IndexOptions().expireAfter(3600L, TimeUnit.SECONDS);
collection.createIndex(Indexes.ascending("lastAccessedAt"), options);
文本索引
用于在字符串内容中执行文本搜索。
// 在 'title' 和 'content' 字段上创建文本索引
collection.createIndex(Indexes.text("title", "content"));
创建后,你可以使用 $text 操作符进行搜索:
collection.find(new Document("$text", new Document("$search", "mongodb java")));
地理空间索引
用于存储和查询地理空间数据,如经纬度坐标。
// 2dsphere 索引,用于表示球面上的点、线和多边形
// 假设 'location' 字段是一个包含经纬度的 GeoJSON 对象,如 { "type": "Point", "coordinates": [ 40.7128, -74.0060 ] }
collection.createIndex(Indexes.geo2dsphere("location"));
索引管理
查看索引
使用 listIndexes() 方法可以查看集合上所有的索引信息。
System.out.println("--- 集合 'users' 的索引列表 ---");
collection.listIndexes().forEach(index -> {
System.out.println(index.toJson());
});
删除索引
删除索引非常灵活,可以通过索引名称或索引文档来删除。
import com.mongodb.client.model.DropIndexesOptions;
// 1. 通过索引名称删除 (最常用)
// 删除名为 'username_1' 的索引
collection.dropIndex("username_1");
// 2. 通过索引文档删除
// 删除在 'email' 字段上的升序索引
collection.dropIndex(new Document("email", 1));
// 3. 删除所有索引 (小心使用!这会保留默认的 _id 索引)
collection.dropAllIndexes();
最佳实践与注意事项
- 在开发环境中测试:在生产环境应用索引变更前,务必在测试或预发布环境中进行充分的性能测试。
- 分析查询模式:使用
explain()方法分析你的查询,看看它是否正在有效地使用索引。db.collection.find({...}).explain("executionStats")可以提供详细的执行计划。 - 避免过多索引:不要为每个字段都创建索引,只为你真正需要的查询创建索引,否则会拖慢写入速度并占用大量磁盘空间。
- 复合索引的顺序至关重要:将高选择性(区分度高,如
email)和等值查询(field = value)的字段放在索引前面,将排序(sort())和范围查询(field > value)的字段放在后面。 - 考虑后台创建:对于非常大的集合,创建索引可能会阻塞其他数据库操作,你可以在后台创建索引,但这会降低索引创建的速度。
IndexOptions options = new IndexOptions().background(true); collection.createIndex(Indexes.ascending("large_field"), options); - 命名你的索引:为重要的索引指定一个有意义的名称,方便管理和删除。
IndexOptions options = new IndexOptions().name("user_email_unique_idx").unique(true); collection.createIndex(Indexes.ascending("email"), options);
希望这份详细的指南能帮助你在 Java 项目中熟练地使用 MongoDB 索引!
