1. MongoDB索引概述

MongoDB索引是提高查询性能的关键技术,通过创建索引可以显著提升数据查询速度。在Java应用中,合理使用MongoDB索引是优化数据库性能的重要手段。本文将详细介绍MongoDB索引的各种类型、创建方法、性能优化技巧以及在Java实战中的应用。

1.1 索引的核心作用

  1. 提升查询性能: 加速数据检索速度
  2. 优化排序操作: 提高排序查询效率
  3. 支持唯一约束: 确保数据唯一性
  4. 加速聚合操作: 提升聚合查询性能
  5. 减少内存使用: 降低查询时的内存消耗

1.2 索引类型

  • 单字段索引: 在单个字段上创建索引
  • 复合索引: 在多个字段上创建索引
  • 多键索引: 在数组字段上创建索引
  • 文本索引: 支持全文搜索的索引
  • 地理空间索引: 支持地理位置查询的索引
  • 哈希索引: 支持分片集群的索引
  • 稀疏索引: 只包含有索引字段的文档

2. MongoDB索引基础操作

2.1 单字段索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/**
* MongoDB单字段索引操作
* @author 运维实战
*/
@Component
public class SingleFieldIndexService {

@Autowired
private MongoTemplate mongoTemplate;

/**
* 创建单字段升序索引
* @param collectionName 集合名称
* @param fieldName 字段名称
*/
public void createAscendingIndex(String collectionName, String fieldName) {
try {
// 创建升序索引
Index index = new Index().on(fieldName, Sort.Direction.ASC);
mongoTemplate.indexOps(collectionName).ensureIndex(index);

System.out.println("成功创建升序索引: " + fieldName);
} catch (Exception e) {
System.err.println("创建升序索引失败: " + e.getMessage());
}
}

/**
* 创建单字段降序索引
* @param collectionName 集合名称
* @param fieldName 字段名称
*/
public void createDescendingIndex(String collectionName, String fieldName) {
try {
// 创建降序索引
Index index = new Index().on(fieldName, Sort.Direction.DESC);
mongoTemplate.indexOps(collectionName).ensureIndex(index);

System.out.println("成功创建降序索引: " + fieldName);
} catch (Exception e) {
System.err.println("创建降序索引失败: " + e.getMessage());
}
}

/**
* 创建唯一索引
* @param collectionName 集合名称
* @param fieldName 字段名称
*/
public void createUniqueIndex(String collectionName, String fieldName) {
try {
// 创建唯一索引
Index index = new Index().on(fieldName, Sort.Direction.ASC).unique();
mongoTemplate.indexOps(collectionName).ensureIndex(index);

System.out.println("成功创建唯一索引: " + fieldName);
} catch (Exception e) {
System.err.println("创建唯一索引失败: " + e.getMessage());
}
}

/**
* 创建稀疏索引
* @param collectionName 集合名称
* @param fieldName 字段名称
*/
public void createSparseIndex(String collectionName, String fieldName) {
try {
// 创建稀疏索引
Index index = new Index().on(fieldName, Sort.Direction.ASC).sparse();
mongoTemplate.indexOps(collectionName).ensureIndex(index);

System.out.println("成功创建稀疏索引: " + fieldName);
} catch (Exception e) {
System.err.println("创建稀疏索引失败: " + e.getMessage());
}
}

/**
* 创建部分索引
* @param collectionName 集合名称
* @param fieldName 字段名称
* @param filterExpression 过滤条件
*/
public void createPartialIndex(String collectionName, String fieldName, String filterExpression) {
try {
// 创建部分索引
Index index = new Index().on(fieldName, Sort.Direction.ASC)
.partial(Filter.parse(filterExpression));
mongoTemplate.indexOps(collectionName).ensureIndex(index);

System.out.println("成功创建部分索引: " + fieldName);
} catch (Exception e) {
System.err.println("创建部分索引失败: " + e.getMessage());
}
}
}

2.2 复合索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
* MongoDB复合索引操作
* @author 运维实战
*/
@Component
public class CompoundIndexService {

@Autowired
private MongoTemplate mongoTemplate;

/**
* 创建复合索引
* @param collectionName 集合名称
* @param fields 字段列表
*/
public void createCompoundIndex(String collectionName, List<IndexField> fields) {
try {
Index index = new Index();

// 添加字段到索引
for (IndexField field : fields) {
index.on(field.getFieldName(), field.getDirection());
}

mongoTemplate.indexOps(collectionName).ensureIndex(index);

System.out.println("成功创建复合索引: " + fields);
} catch (Exception e) {
System.err.println("创建复合索引失败: " + e.getMessage());
}
}

/**
* 创建用户复合索引示例
* @param collectionName 集合名称
*/
public void createUserCompoundIndex(String collectionName) {
try {
// 创建用户复合索引:用户名 + 邮箱 + 创建时间
Index index = new Index()
.on("username", Sort.Direction.ASC)
.on("email", Sort.Direction.ASC)
.on("createdAt", Sort.Direction.DESC);

mongoTemplate.indexOps(collectionName).ensureIndex(index);

System.out.println("成功创建用户复合索引");
} catch (Exception e) {
System.err.println("创建用户复合索引失败: " + e.getMessage());
}
}

/**
* 创建订单复合索引示例
* @param collectionName 集合名称
*/
public void createOrderCompoundIndex(String collectionName) {
try {
// 创建订单复合索引:用户ID + 状态 + 创建时间
Index index = new Index()
.on("userId", Sort.Direction.ASC)
.on("status", Sort.Direction.ASC)
.on("createdAt", Sort.Direction.DESC);

mongoTemplate.indexOps(collectionName).ensureIndex(index);

System.out.println("成功创建订单复合索引");
} catch (Exception e) {
System.err.println("创建订单复合索引失败: " + e.getMessage());
}
}

/**
* 索引字段类
*/
public static class IndexField {
private String fieldName;
private Sort.Direction direction;

public IndexField(String fieldName, Sort.Direction direction) {
this.fieldName = fieldName;
this.direction = direction;
}

public String getFieldName() { return fieldName; }
public Sort.Direction getDirection() { return direction; }
}
}

2.3 多键索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
* MongoDB多键索引操作
* @author 运维实战
*/
@Component
public class MultiKeyIndexService {

@Autowired
private MongoTemplate mongoTemplate;

/**
* 创建数组字段索引
* @param collectionName 集合名称
* @param fieldName 数组字段名称
*/
public void createArrayFieldIndex(String collectionName, String fieldName) {
try {
// 创建数组字段索引
Index index = new Index().on(fieldName, Sort.Direction.ASC);
mongoTemplate.indexOps(collectionName).ensureIndex(index);

System.out.println("成功创建数组字段索引: " + fieldName);
} catch (Exception e) {
System.err.println("创建数组字段索引失败: " + e.getMessage());
}
}

/**
* 创建标签索引示例
* @param collectionName 集合名称
*/
public void createTagsIndex(String collectionName) {
try {
// 创建标签数组索引
Index index = new Index().on("tags", Sort.Direction.ASC);
mongoTemplate.indexOps(collectionName).ensureIndex(index);

System.out.println("成功创建标签索引");
} catch (Exception e) {
System.err.println("创建标签索引失败: " + e.getMessage());
}
}

/**
* 创建技能索引示例
* @param collectionName 集合名称
*/
public void createSkillsIndex(String collectionName) {
try {
// 创建技能数组索引
Index index = new Index().on("skills", Sort.Direction.ASC);
mongoTemplate.indexOps(collectionName).ensureIndex(index);

System.out.println("成功创建技能索引");
} catch (Exception e) {
System.err.println("创建技能索引失败: " + e.getMessage());
}
}
}

2.4 文本索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/**
* MongoDB文本索引操作
* @author 运维实战
*/
@Component
public class TextIndexService {

@Autowired
private MongoTemplate mongoTemplate;

/**
* 创建文本索引
* @param collectionName 集合名称
* @param fieldName 字段名称
*/
public void createTextIndex(String collectionName, String fieldName) {
try {
// 创建文本索引
Index index = new Index().on(fieldName, Sort.Direction.ASC).named("text_" + fieldName);
mongoTemplate.indexOps(collectionName).ensureIndex(index);

System.out.println("成功创建文本索引: " + fieldName);
} catch (Exception e) {
System.err.println("创建文本索引失败: " + e.getMessage());
}
}

/**
* 创建多字段文本索引
* @param collectionName 集合名称
* @param fields 字段列表
*/
public void createMultiFieldTextIndex(String collectionName, List<String> fields) {
try {
Index index = new Index();

// 添加多个字段到文本索引
for (String field : fields) {
index.on(field, Sort.Direction.ASC);
}

index.named("text_multi");
mongoTemplate.indexOps(collectionName).ensureIndex(index);

System.out.println("成功创建多字段文本索引: " + fields);
} catch (Exception e) {
System.err.println("创建多字段文本索引失败: " + e.getMessage());
}
}

/**
* 创建文章文本索引示例
* @param collectionName 集合名称
*/
public void createArticleTextIndex(String collectionName) {
try {
// 创建文章文本索引:标题 + 内容 + 标签
Index index = new Index()
.on("title", Sort.Direction.ASC)
.on("content", Sort.Direction.ASC)
.on("tags", Sort.Direction.ASC)
.named("article_text_index");

mongoTemplate.indexOps(collectionName).ensureIndex(index);

System.out.println("成功创建文章文本索引");
} catch (Exception e) {
System.err.println("创建文章文本索引失败: " + e.getMessage());
}
}

/**
* 执行文本搜索
* @param collectionName 集合名称
* @param searchText 搜索文本
* @return 搜索结果
*/
public List<Document> searchText(String collectionName, String searchText) {
try {
// 创建文本搜索查询
TextCriteria criteria = TextCriteria.forDefaultLanguage().matching(searchText);
Query query = TextQuery.queryText(criteria).sortByScore();

// 执行查询
List<Document> results = mongoTemplate.find(query, Document.class, collectionName);

System.out.println("文本搜索完成,找到 " + results.size() + " 条结果");
return results;
} catch (Exception e) {
System.err.println("文本搜索失败: " + e.getMessage());
return new ArrayList<>();
}
}
}

2.5 地理空间索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/**
* MongoDB地理空间索引操作
* @author 运维实战
*/
@Component
public class GeospatialIndexService {

@Autowired
private MongoTemplate mongoTemplate;

/**
* 创建2d索引
* @param collectionName 集合名称
* @param fieldName 字段名称
*/
public void create2dIndex(String collectionName, String fieldName) {
try {
// 创建2d索引
Index index = new Index().on(fieldName, Sort.Direction.ASC).named("2d_" + fieldName);
mongoTemplate.indexOps(collectionName).ensureIndex(index);

System.out.println("成功创建2d索引: " + fieldName);
} catch (Exception e) {
System.err.println("创建2d索引失败: " + e.getMessage());
}
}

/**
* 创建2dsphere索引
* @param collectionName 集合名称
* @param fieldName 字段名称
*/
public void create2dsphereIndex(String collectionName, String fieldName) {
try {
// 创建2dsphere索引
Index index = new Index().on(fieldName, Sort.Direction.ASC).named("2dsphere_" + fieldName);
mongoTemplate.indexOps(collectionName).ensureIndex(index);

System.out.println("成功创建2dsphere索引: " + fieldName);
} catch (Exception e) {
System.err.println("创建2dsphere索引失败: " + e.getMessage());
}
}

/**
* 创建位置索引示例
* @param collectionName 集合名称
*/
public void createLocationIndex(String collectionName) {
try {
// 创建位置2dsphere索引
Index index = new Index().on("location", Sort.Direction.ASC).named("location_2dsphere");
mongoTemplate.indexOps(collectionName).ensureIndex(index);

System.out.println("成功创建位置索引");
} catch (Exception e) {
System.err.println("创建位置索引失败: " + e.getMessage());
}
}

/**
* 执行地理位置查询
* @param collectionName 集合名称
* @param longitude 经度
* @param latitude 纬度
* @param maxDistance 最大距离(米)
* @return 查询结果
*/
public List<Document> findNearby(String collectionName, double longitude, double latitude, double maxDistance) {
try {
// 创建地理位置查询
Point point = new Point(longitude, latitude);
NearQuery nearQuery = NearQuery.near(point).maxDistance(maxDistance);

// 执行查询
List<Document> results = mongoTemplate.find(nearQuery, Document.class, collectionName);

System.out.println("地理位置查询完成,找到 " + results.size() + " 条结果");
return results;
} catch (Exception e) {
System.err.println("地理位置查询失败: " + e.getMessage());
return new ArrayList<>();
}
}
}

3. 索引管理操作

3.1 索引查询和管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/**
* MongoDB索引管理操作
* @author 运维实战
*/
@Component
public class IndexManagementService {

@Autowired
private MongoTemplate mongoTemplate;

/**
* 获取集合的所有索引
* @param collectionName 集合名称
* @return 索引列表
*/
public List<IndexInfo> getAllIndexes(String collectionName) {
try {
IndexOperations indexOps = mongoTemplate.indexOps(collectionName);
List<IndexInfo> indexes = indexOps.getIndexInfo();

System.out.println("集合 " + collectionName + " 共有 " + indexes.size() + " 个索引");
return indexes;
} catch (Exception e) {
System.err.println("获取索引列表失败: " + e.getMessage());
return new ArrayList<>();
}
}

/**
* 检查索引是否存在
* @param collectionName 集合名称
* @param indexName 索引名称
* @return 是否存在
*/
public boolean indexExists(String collectionName, String indexName) {
try {
IndexOperations indexOps = mongoTemplate.indexOps(collectionName);
List<IndexInfo> indexes = indexOps.getIndexInfo();

for (IndexInfo index : indexes) {
if (indexName.equals(index.getName())) {
return true;
}
}
return false;
} catch (Exception e) {
System.err.println("检查索引是否存在失败: " + e.getMessage());
return false;
}
}

/**
* 删除索引
* @param collectionName 集合名称
* @param indexName 索引名称
*/
public void dropIndex(String collectionName, String indexName) {
try {
IndexOperations indexOps = mongoTemplate.indexOps(collectionName);
indexOps.dropIndex(indexName);

System.out.println("成功删除索引: " + indexName);
} catch (Exception e) {
System.err.println("删除索引失败: " + e.getMessage());
}
}

/**
* 删除所有索引(除了_id索引)
* @param collectionName 集合名称
*/
public void dropAllIndexes(String collectionName) {
try {
IndexOperations indexOps = mongoTemplate.indexOps(collectionName);
indexOps.dropAllIndexes();

System.out.println("成功删除所有索引");
} catch (Exception e) {
System.err.println("删除所有索引失败: " + e.getMessage());
}
}

/**
* 重建索引
* @param collectionName 集合名称
*/
public void rebuildIndexes(String collectionName) {
try {
IndexOperations indexOps = mongoTemplate.indexOps(collectionName);
indexOps.reindex();

System.out.println("成功重建索引");
} catch (Exception e) {
System.err.println("重建索引失败: " + e.getMessage());
}
}

/**
* 获取索引统计信息
* @param collectionName 集合名称
* @return 索引统计信息
*/
public Map<String, Object> getIndexStats(String collectionName) {
try {
// 执行索引统计命令
Document command = new Document("collStats", collectionName);
Document result = mongoTemplate.getDb().runCommand(command);

System.out.println("获取索引统计信息成功");
return result;
} catch (Exception e) {
System.err.println("获取索引统计信息失败: " + e.getMessage());
return new HashMap<>();
}
}
}

3.2 索引性能监控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
* MongoDB索引性能监控
* @author 运维实战
*/
@Component
public class IndexPerformanceMonitor {

@Autowired
private MongoTemplate mongoTemplate;

/**
* 监控索引使用情况
* @param collectionName 集合名称
* @return 索引使用统计
*/
public Map<String, Object> monitorIndexUsage(String collectionName) {
try {
// 执行索引使用统计命令
Document command = new Document("collStats", collectionName);
Document result = mongoTemplate.getDb().runCommand(command);

// 提取索引统计信息
Map<String, Object> indexStats = new HashMap<>();
if (result.containsKey("indexSizes")) {
indexStats.put("indexSizes", result.get("indexSizes"));
}
if (result.containsKey("totalIndexSize")) {
indexStats.put("totalIndexSize", result.get("totalIndexSize"));
}

System.out.println("索引使用情况监控完成");
return indexStats;
} catch (Exception e) {
System.err.println("监控索引使用情况失败: " + e.getMessage());
return new HashMap<>();
}
}

/**
* 分析查询性能
* @param collectionName 集合名称
* @param query 查询条件
* @return 查询性能分析结果
*/
public Map<String, Object> analyzeQueryPerformance(String collectionName, Query query) {
try {
// 执行查询性能分析
Document explainResult = mongoTemplate.getCollection(collectionName)
.find(query.getQueryObject())
.explain();

// 提取关键性能指标
Map<String, Object> performance = new HashMap<>();
performance.put("executionTimeMillis", explainResult.get("executionTimeMillis"));
performance.put("totalDocsExamined", explainResult.get("totalDocsExamined"));
performance.put("totalKeysExamined", explainResult.get("totalKeysExamined"));
performance.put("stage", explainResult.get("stage"));

System.out.println("查询性能分析完成");
return performance;
} catch (Exception e) {
System.err.println("分析查询性能失败: " + e.getMessage());
return new HashMap<>();
}
}

/**
* 检查慢查询
* @param collectionName 集合名称
* @param threshold 慢查询阈值(毫秒)
* @return 慢查询列表
*/
public List<Document> findSlowQueries(String collectionName, long threshold) {
try {
// 查询执行时间超过阈值的操作
Query query = new Query();
query.addCriteria(Criteria.where("executionTimeMillis").gt(threshold));

List<Document> slowQueries = mongoTemplate.find(query, Document.class, collectionName);

System.out.println("找到 " + slowQueries.size() + " 个慢查询");
return slowQueries;
} catch (Exception e) {
System.err.println("检查慢查询失败: " + e.getMessage());
return new ArrayList<>();
}
}
}

4. 实战应用示例

4.1 用户管理系统索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/**
* 用户管理系统索引配置
* @author 运维实战
*/
@Component
public class UserIndexConfiguration {

@Autowired
private MongoTemplate mongoTemplate;

/**
* 配置用户集合索引
*/
@PostConstruct
public void configureUserIndexes() {
String collectionName = "users";

try {
// 1. 用户名唯一索引
Index usernameIndex = new Index()
.on("username", Sort.Direction.ASC)
.unique()
.named("idx_username_unique");
mongoTemplate.indexOps(collectionName).ensureIndex(usernameIndex);

// 2. 邮箱唯一索引
Index emailIndex = new Index()
.on("email", Sort.Direction.ASC)
.unique()
.named("idx_email_unique");
mongoTemplate.indexOps(collectionName).ensureIndex(emailIndex);

// 3. 手机号唯一索引
Index phoneIndex = new Index()
.on("phone", Sort.Direction.ASC)
.unique()
.sparse()
.named("idx_phone_unique");
mongoTemplate.indexOps(collectionName).ensureIndex(phoneIndex);

// 4. 状态索引
Index statusIndex = new Index()
.on("status", Sort.Direction.ASC)
.named("idx_status");
mongoTemplate.indexOps(collectionName).ensureIndex(statusIndex);

// 5. 创建时间索引
Index createdAtIndex = new Index()
.on("createdAt", Sort.Direction.DESC)
.named("idx_created_at");
mongoTemplate.indexOps(collectionName).ensureIndex(createdAtIndex);

// 6. 复合索引:状态 + 创建时间
Index statusCreatedAtIndex = new Index()
.on("status", Sort.Direction.ASC)
.on("createdAt", Sort.Direction.DESC)
.named("idx_status_created_at");
mongoTemplate.indexOps(collectionName).ensureIndex(statusCreatedAtIndex);

// 7. 文本索引:用户名 + 邮箱
Index textIndex = new Index()
.on("username", Sort.Direction.ASC)
.on("email", Sort.Direction.ASC)
.named("idx_text_search");
mongoTemplate.indexOps(collectionName).ensureIndex(textIndex);

System.out.println("用户集合索引配置完成");
} catch (Exception e) {
System.err.println("用户集合索引配置失败: " + e.getMessage());
}
}
}

4.2 订单管理系统索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/**
* 订单管理系统索引配置
* @author 运维实战
*/
@Component
public class OrderIndexConfiguration {

@Autowired
private MongoTemplate mongoTemplate;

/**
* 配置订单集合索引
*/
@PostConstruct
public void configureOrderIndexes() {
String collectionName = "orders";

try {
// 1. 订单号唯一索引
Index orderNoIndex = new Index()
.on("orderNo", Sort.Direction.ASC)
.unique()
.named("idx_order_no_unique");
mongoTemplate.indexOps(collectionName).ensureIndex(orderNoIndex);

// 2. 用户ID索引
Index userIdIndex = new Index()
.on("userId", Sort.Direction.ASC)
.named("idx_user_id");
mongoTemplate.indexOps(collectionName).ensureIndex(userIdIndex);

// 3. 订单状态索引
Index statusIndex = new Index()
.on("status", Sort.Direction.ASC)
.named("idx_status");
mongoTemplate.indexOps(collectionName).ensureIndex(statusIndex);

// 4. 创建时间索引
Index createdAtIndex = new Index()
.on("createdAt", Sort.Direction.DESC)
.named("idx_created_at");
mongoTemplate.indexOps(collectionName).ensureIndex(createdAtIndex);

// 5. 复合索引:用户ID + 状态
Index userStatusIndex = new Index()
.on("userId", Sort.Direction.ASC)
.on("status", Sort.Direction.ASC)
.named("idx_user_status");
mongoTemplate.indexOps(collectionName).ensureIndex(userStatusIndex);

// 6. 复合索引:状态 + 创建时间
Index statusCreatedAtIndex = new Index()
.on("status", Sort.Direction.ASC)
.on("createdAt", Sort.Direction.DESC)
.named("idx_status_created_at");
mongoTemplate.indexOps(collectionName).ensureIndex(statusCreatedAtIndex);

// 7. 复合索引:用户ID + 状态 + 创建时间
Index userStatusCreatedAtIndex = new Index()
.on("userId", Sort.Direction.ASC)
.on("status", Sort.Direction.ASC)
.on("createdAt", Sort.Direction.DESC)
.named("idx_user_status_created_at");
mongoTemplate.indexOps(collectionName).ensureIndex(userStatusCreatedAtIndex);

// 8. 金额索引
Index amountIndex = new Index()
.on("amount", Sort.Direction.ASC)
.named("idx_amount");
mongoTemplate.indexOps(collectionName).ensureIndex(amountIndex);

System.out.println("订单集合索引配置完成");
} catch (Exception e) {
System.err.println("订单集合索引配置失败: " + e.getMessage());
}
}
}

4.3 商品管理系统索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
* 商品管理系统索引配置
* @author 运维实战
*/
@Component
public class ProductIndexConfiguration {

@Autowired
private MongoTemplate mongoTemplate;

/**
* 配置商品集合索引
*/
@PostConstruct
public void configureProductIndexes() {
String collectionName = "products";

try {
// 1. 商品编码唯一索引
Index productCodeIndex = new Index()
.on("productCode", Sort.Direction.ASC)
.unique()
.named("idx_product_code_unique");
mongoTemplate.indexOps(collectionName).ensureIndex(productCodeIndex);

// 2. 商品名称索引
Index nameIndex = new Index()
.on("name", Sort.Direction.ASC)
.named("idx_name");
mongoTemplate.indexOps(collectionName).ensureIndex(nameIndex);

// 3. 分类索引
Index categoryIndex = new Index()
.on("category", Sort.Direction.ASC)
.named("idx_category");
mongoTemplate.indexOps(collectionName).ensureIndex(categoryIndex);

// 4. 价格索引
Index priceIndex = new Index()
.on("price", Sort.Direction.ASC)
.named("idx_price");
mongoTemplate.indexOps(collectionName).ensureIndex(priceIndex);

// 5. 状态索引
Index statusIndex = new Index()
.on("status", Sort.Direction.ASC)
.named("idx_status");
mongoTemplate.indexOps(collectionName).ensureIndex(statusIndex);

// 6. 创建时间索引
Index createdAtIndex = new Index()
.on("createdAt", Sort.Direction.DESC)
.named("idx_created_at");
mongoTemplate.indexOps(collectionName).ensureIndex(createdAtIndex);

// 7. 复合索引:分类 + 状态
Index categoryStatusIndex = new Index()
.on("category", Sort.Direction.ASC)
.on("status", Sort.Direction.ASC)
.named("idx_category_status");
mongoTemplate.indexOps(collectionName).ensureIndex(categoryStatusIndex);

// 8. 复合索引:分类 + 价格
Index categoryPriceIndex = new Index()
.on("category", Sort.Direction.ASC)
.on("price", Sort.Direction.ASC)
.named("idx_category_price");
mongoTemplate.indexOps(collectionName).ensureIndex(categoryPriceIndex);

// 9. 文本索引:商品名称 + 描述
Index textIndex = new Index()
.on("name", Sort.Direction.ASC)
.on("description", Sort.Direction.ASC)
.named("idx_text_search");
mongoTemplate.indexOps(collectionName).ensureIndex(textIndex);

// 10. 标签数组索引
Index tagsIndex = new Index()
.on("tags", Sort.Direction.ASC)
.named("idx_tags");
mongoTemplate.indexOps(collectionName).ensureIndex(tagsIndex);

System.out.println("商品集合索引配置完成");
} catch (Exception e) {
System.err.println("商品集合索引配置失败: " + e.getMessage());
}
}
}

5. 索引性能优化

5.1 索引优化策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/**
* MongoDB索引优化策略
* @author 运维实战
*/
@Component
public class IndexOptimizationService {

@Autowired
private MongoTemplate mongoTemplate;

/**
* 分析查询模式并推荐索引
* @param collectionName 集合名称
* @param queries 查询列表
* @return 推荐的索引
*/
public List<IndexRecommendation> analyzeAndRecommendIndexes(String collectionName, List<Query> queries) {
List<IndexRecommendation> recommendations = new ArrayList<>();

try {
for (Query query : queries) {
// 分析查询性能
Map<String, Object> performance = analyzeQueryPerformance(collectionName, query);

// 生成索引推荐
IndexRecommendation recommendation = generateIndexRecommendation(query, performance);
if (recommendation != null) {
recommendations.add(recommendation);
}
}

System.out.println("生成了 " + recommendations.size() + " 个索引推荐");
return recommendations;
} catch (Exception e) {
System.err.println("分析查询模式失败: " + e.getMessage());
return new ArrayList<>();
}
}

/**
* 分析查询性能
* @param collectionName 集合名称
* @param query 查询条件
* @return 性能分析结果
*/
private Map<String, Object> analyzeQueryPerformance(String collectionName, Query query) {
try {
Document explainResult = mongoTemplate.getCollection(collectionName)
.find(query.getQueryObject())
.explain();

Map<String, Object> performance = new HashMap<>();
performance.put("executionTimeMillis", explainResult.get("executionTimeMillis"));
performance.put("totalDocsExamined", explainResult.get("totalDocsExamined"));
performance.put("totalKeysExamined", explainResult.get("totalKeysExamined"));
performance.put("stage", explainResult.get("stage"));

return performance;
} catch (Exception e) {
System.err.println("分析查询性能失败: " + e.getMessage());
return new HashMap<>();
}
}

/**
* 生成索引推荐
* @param query 查询条件
* @param performance 性能分析结果
* @return 索引推荐
*/
private IndexRecommendation generateIndexRecommendation(Query query, Map<String, Object> performance) {
try {
// 检查是否需要索引优化
Long executionTime = (Long) performance.get("executionTimeMillis");
Long docsExamined = (Long) performance.get("totalDocsExamined");

if (executionTime > 100 || docsExamined > 1000) {
// 生成索引推荐
IndexRecommendation recommendation = new IndexRecommendation();
recommendation.setQuery(query);
recommendation.setExecutionTime(executionTime);
recommendation.setDocsExamined(docsExamined);
recommendation.setRecommendedIndex(generateIndexFromQuery(query));

return recommendation;
}

return null;
} catch (Exception e) {
System.err.println("生成索引推荐失败: " + e.getMessage());
return null;
}
}

/**
* 从查询生成索引
* @param query 查询条件
* @return 推荐的索引
*/
private Index generateIndexFromQuery(Query query) {
try {
Index index = new Index();

// 添加查询字段到索引
Document queryObject = query.getQueryObject();
for (String field : queryObject.keySet()) {
if (!field.startsWith("$")) {
index.on(field, Sort.Direction.ASC);
}
}

// 添加排序字段到索引
if (query.getSortObject() != null) {
Document sortObject = query.getSortObject();
for (String field : sortObject.keySet()) {
Sort.Direction direction = sortObject.getInteger(field) > 0 ?
Sort.Direction.ASC : Sort.Direction.DESC;
index.on(field, direction);
}
}

return index;
} catch (Exception e) {
System.err.println("从查询生成索引失败: " + e.getMessage());
return null;
}
}

/**
* 索引推荐类
*/
public static class IndexRecommendation {
private Query query;
private Long executionTime;
private Long docsExamined;
private Index recommendedIndex;

// getter和setter方法
public Query getQuery() { return query; }
public void setQuery(Query query) { this.query = query; }

public Long getExecutionTime() { return executionTime; }
public void setExecutionTime(Long executionTime) { this.executionTime = executionTime; }

public Long getDocsExamined() { return docsExamined; }
public void setDocsExamined(Long docsExamined) { this.docsExamined = docsExamined; }

public Index getRecommendedIndex() { return recommendedIndex; }
public void setRecommendedIndex(Index recommendedIndex) { this.recommendedIndex = recommendedIndex; }
}
}

5.2 索引监控和告警

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/**
* MongoDB索引监控和告警
* @author 运维实战
*/
@Component
public class IndexMonitoringService {

@Autowired
private MongoTemplate mongoTemplate;

@Autowired
private LogManager logManager;

/**
* 监控索引性能
* @param collectionName 集合名称
*/
@Scheduled(fixedRate = 300000) // 每5分钟执行一次
public void monitorIndexPerformance(String collectionName) {
try {
// 获取索引统计信息
Map<String, Object> indexStats = getIndexStats(collectionName);

// 检查索引大小
Long totalIndexSize = (Long) indexStats.get("totalIndexSize");
if (totalIndexSize > 1024 * 1024 * 1024) { // 超过1GB
logManager.warning("集合 " + collectionName + " 的索引大小超过1GB: " + totalIndexSize);
}

// 检查慢查询
List<Document> slowQueries = findSlowQueries(collectionName, 1000); // 超过1秒
if (!slowQueries.isEmpty()) {
logManager.warning("发现 " + slowQueries.size() + " 个慢查询");
}

System.out.println("索引性能监控完成");
} catch (Exception e) {
logManager.error("索引性能监控失败", e);
}
}

/**
* 获取索引统计信息
* @param collectionName 集合名称
* @return 索引统计信息
*/
private Map<String, Object> getIndexStats(String collectionName) {
try {
Document command = new Document("collStats", collectionName);
Document result = mongoTemplate.getDb().runCommand(command);

Map<String, Object> indexStats = new HashMap<>();
if (result.containsKey("indexSizes")) {
indexStats.put("indexSizes", result.get("indexSizes"));
}
if (result.containsKey("totalIndexSize")) {
indexStats.put("totalIndexSize", result.get("totalIndexSize"));
}

return indexStats;
} catch (Exception e) {
System.err.println("获取索引统计信息失败: " + e.getMessage());
return new HashMap<>();
}
}

/**
* 检查慢查询
* @param collectionName 集合名称
* @param threshold 慢查询阈值(毫秒)
* @return 慢查询列表
*/
private List<Document> findSlowQueries(String collectionName, long threshold) {
try {
Query query = new Query();
query.addCriteria(Criteria.where("executionTimeMillis").gt(threshold));

return mongoTemplate.find(query, Document.class, collectionName);
} catch (Exception e) {
System.err.println("检查慢查询失败: " + e.getMessage());
return new ArrayList<>();
}
}
}

6. 总结

6.1 索引最佳实践

  1. 合理选择索引类型: 根据查询模式选择合适的索引类型
  2. 优化复合索引: 将最常用的查询字段放在复合索引的前面
  3. 避免过度索引: 不要创建不必要的索引,会影响写入性能
  4. 定期监控: 监控索引使用情况和性能
  5. 及时清理: 删除不再使用的索引

6.2 性能优化建议

  • 单字段索引: 适用于简单的等值查询和范围查询
  • 复合索引: 适用于多字段查询,注意字段顺序
  • 文本索引: 适用于全文搜索场景
  • 地理空间索引: 适用于地理位置查询
  • 稀疏索引: 适用于可选字段的索引

6.3 常见问题解决

  • 索引未生效: 检查查询条件是否匹配索引
  • 索引过大: 考虑使用部分索引或稀疏索引
  • 写入性能下降: 减少不必要的索引
  • 内存使用过高: 优化索引大小和数量

通过本文的MongoDB索引Java实战指南,您可以掌握MongoDB索引的各种类型、创建方法、性能优化技巧以及在实际项目中的应用。记住,合理使用索引是提升MongoDB性能的关键!