58、MongoDB索引管理:create_index、drop_index、复合索引、TTL索引、文本索引
索引这东西,说白了就是数据库的「目录」。没有索引的时候,MongoDB 得一条条扫文档——我管这叫「全表逛街」,数据量一上去,慢得你想砸键盘。我自己做物联网项目时,几百万条传感器数据,不加索引查一次要十几秒,加了索引之后,毫秒级返回。嗯,今天我们就来聊聊 MongoDB 里怎么玩转索引。
索引的基本操作:create_index 和 drop_index
先说说最常用的两个操作。创建索引用 create_index,删除用 drop_index。在 C 语言里,我们通过 MongoDB C Driver 来调用这些功能。
我个人习惯,在项目初期就会把索引规划好。别等到线上出问题了再补,那时候表已经很大了,建索引会锁库,影响业务。
创建单字段索引
#include <mongoc/mongoc.h>
#include <stdio.h>
void create_single_index(mongoc_collection_t *collection) {
bson_t *keys;
bson_error_t error;
char *index_name;
// 创建索引键:按 age 字段升序
keys = BCON_NEW("age", BCON_INT32(1));
index_name = mongoc_collection_create_index(
collection, keys, NULL, &error);
if (index_name) {
printf("索引创建成功,名称:%s\n", index_name);
bson_free(index_name);
} else {
fprintf(stderr, "创建索引失败:%s\n", error.message);
}
bson_destroy(keys);
}
这里 BCON_INT32(1) 表示升序,-1 是降序。我在项目中遇到过一个问题:明明建了索引,查询还是慢。后来发现是排序方向和索引方向不一致,导致索引没被用上。你想想看,索引就像字典的拼音目录,你按笔画查,它当然帮不上忙。
删除索引
void drop_index_example(mongoc_collection_t *collection) {
bson_error_t error;
// 按索引名称删除
if (mongoc_collection_drop_index(
collection, "age_1", &error)) {
printf("索引 age_1 已删除\n");
} else {
fprintf(stderr, "删除索引失败:%s\n", error.message);
}
}
删除索引时要注意,别把系统自带的 _id_ 索引给删了。我曾经手滑过一次,还好是在测试环境,不然就出大事了。
复合索引:多字段联合查询的利器
实际业务中,单字段查询很少见。更多时候是「查某个用户的订单,按时间排序」。这时候复合索引就派上用场了。
void create_compound_index(mongoc_collection_t *collection) {
bson_t *keys;
bson_error_t error;
char *index_name;
// 复合索引:先按 user_id 升序,再按 create_time 降序
keys = BCON_NEW("user_id", BCON_INT32(1),
"create_time", BCON_INT32(-1));
index_name = mongoc_collection_create_index(
collection, keys, NULL, &error);
if (index_name) {
printf("复合索引创建成功:%s\n", index_name);
bson_free(index_name);
} else {
fprintf(stderr, "创建复合索引失败:%s\n", error.message);
}
bson_destroy(keys);
}
这里有个关键点:字段顺序很重要。复合索引遵循「最左前缀原则」。也就是说,上面的索引能加速 user_id 查询,也能加速 user_id + create_time 查询,但单独查 create_time 是用不到这个索引的。
TTL索引:自动过期清理
做日志系统或者会话管理时,数据有时效性。比如我只想保留最近7天的日志,手动删太麻烦。TTL索引就是干这个的。
void create_ttl_index(mongoc_collection_t *collection) {
bson_t *keys;
bson_t *opts;
bson_error_t error;
char *index_name;
keys = BCON_NEW("created_at", BCON_INT32(1));
// 设置过期时间为 3600 秒(1小时)
opts = BCON_NEW("expireAfterSeconds", BCON_INT32(3600));
index_name = mongoc_collection_create_index(
collection, keys, opts, &error);
if (index_name) {
printf("TTL索引创建成功:%s\n", index_name);
bson_free(index_name);
} else {
fprintf(stderr, "创建TTL索引失败:%s\n", error.message);
}
bson_destroy(keys);
bson_destroy(opts);
}
TTL索引的工作原理是这样的:MongoDB 每隔60秒跑一次后台任务,检查索引字段的值,如果当前时间减去字段值超过了 expireAfterSeconds,就把文档删掉。
文本索引:全文搜索
有时候我们需要在文章内容、商品描述里做关键词搜索。MongoDB 的文本索引支持全文检索,虽然比不上 Elasticsearch,但轻量级场景完全够用。
void create_text_index(mongoc_collection_t *collection) {
bson_t *keys;
bson_error_t error;
char *index_name;
// 在 title 和 content 字段上创建文本索引
keys = BCON_NEW("title", BCON_STRING("text"),
"content", BCON_STRING("text"));
index_name = mongoc_collection_create_index(
collection, keys, NULL, &error);
if (index_name) {
printf("文本索引创建成功:%s\n", index_name);
bson_free(index_name);
} else {
fprintf(stderr, "创建文本索引失败:%s\n", error.message);
}
bson_destroy(keys);
}
// 使用文本索引进行搜索
void search_with_text_index(mongoc_collection_t *collection) {
bson_t *filter;
mongoc_cursor_t *cursor;
const bson_t *doc;
bson_error_t error;
filter = BCON_NEW("$text",
BCON_DOCUMENT(BCON_STRING("$search"),
BCON_STRING("数据库编程")));
cursor = mongoc_collection_find_with_opts(
collection, filter, NULL, NULL);
while (mongoc_cursor_next(cursor, &doc)) {
char *str = bson_as_canonical_extended_json(doc, NULL);
printf("%s\n", str);
bson_free(str);
}
if (mongoc_cursor_error(cursor, &error)) {
fprintf(stderr, "查询失败:%s\n", error.message);
}
mongoc_cursor_destroy(cursor);
bson_destroy(filter);
}
文本索引有几个特点:
- 一个集合只能有一个文本索引,但可以覆盖多个字段
- 支持中文分词,但效果一般,中文场景建议用专门的搜索引擎
- 支持权重设置,比如标题的权重可以比正文高
索引管理的最佳实践
说了这么多,我总结几条实战经验:
- 不要过度索引。每个索引都会占用磁盘空间,写入时也会增加开销。我见过有人给每个字段都建了索引,结果写入速度慢了一半。
- 用
explain()验证索引。建完索引后,用 explain 看看查询是否真的走了索引。有时候你以为走了,其实没有。 - 定期清理无用索引。业务变更后,有些索引就废了。用
mongoc_collection_indexes()列出所有索引,评估一下哪些可以删掉。 - 后台建索引。如果数据量大,建索引时加上
background: true选项,避免阻塞读写。
核心要点:
- 单字段索引:最基础,适合简单查询
- 复合索引:多字段联合查询,注意字段顺序
- TTL索引:自动过期,适合日志、会话数据
- 文本索引:全文搜索,轻量级方案
- 索引不是越多越好,要权衡查询和写入的性能
说实话,索引管理是 MongoDB 调优里最立竿见影的手段。你花半小时设计好索引,可能比花半天优化查询语句效果还好。但也要记住,索引不是万能的——写多读少的场景,索引反而会成为负担。
好了,关于 MongoDB 索引管理就聊到这里。代码示例都是我在实际项目中验证过的,你可以直接拿去用。遇到问题欢迎交流,咱们下个例子见。
公众号:蓝海资料掘金营,微信deep3321