createIndex 是什么
createIndex(keys, options) 为集合建立索引:keys 描述字段与方向(1 升序、-1 降序,或 text、hashed、2dsphere 等类型),options 用于命名、加唯一约束、限制部分文档或设置过期时间。索引让查询从全集合扫描变成按索引定位,代价是写入变慢与额外存储。构建期间业务一般可以继续读写,但大集合上会明显占用 IO。
基本信息
| 语法 | db.collection.createIndex({ field: 1 }, options) |
|---|---|
| 方向与类型 | 1 / -1 / "text" / "hashed" / "2dsphere" / "2d" |
| 常用选项 | name / unique / sparse / partialFilterExpression / expireAfterSeconds / collation |
| 复合索引 | 字段顺序决定前缀可用性 |
| 构建影响 | 占用 IO 与磁盘临时空间,业务低峰期执行 |
| 查看现状 | db.collection.getIndexes() |
典型使用场景
- 高频等值查询字段(订单号、用户 ID)建单字段索引
- 复合查询条件按「等值在前、范围在后」建复合索引
- 唯一约束:用户名、手机号等业务唯一键加 unique
- 过期数据自动清理:会话表加 expireAfterSeconds 做 TTL 索引
- 只索引部分文档:用 partialFilterExpression 减少索引体积
- 全文检索:给描述字段建 text 索引支持 $text 查询
示例
示例
db.orders.createIndex({ status: 1, ts: -1 }, { name: "status_ts" }) # 复合索引
db.orders.createIndex({ orderNo: 1 }, { unique: true }) # 唯一索引
db.sessions.createIndex({ expireAt: 1 }, { expireAfterSeconds: 3600 }) # TTL 索引
db.orders.createIndex({ status: 1, ts: -1 }, { partialFilterExpression: { status: "paid" } }) # 部分索引
db.products.createIndex({ title: "text" }, { default_language: "none" }) # 全文索引
db.orders.getIndexes() # 查看已有索引常用选项读法
| 选项 | 作用 | 使用注意 |
|---|---|---|
| name | 显式命名索引 | 便于后续 drop 与巡检 |
| unique | 唯一性约束 | 已有重复数据时创建失败 |
| sparse | 跳过缺失该字段的文档 | 默认索引会包含 null |
| partialFilterExpression | 只索引满足条件的文档 | 比 sparse 更精确也更省空间 |
| expireAfterSeconds | 按时间自动过期删除 | 仅单字段日期索引可用 |
| collation | 指定排序规则 | 大小写不敏感查询需要一致设置 |
索引构建进度可用 db.currentOp() 或 db.adminCommand({ currentOp: 1 }) 观察;构建完成后用 explain 验证查询是否真的走上索引。
给已经存在重复数据的字段加唯一索引会构建失败,先清理重复再建,或用 partialFilterExpression 收窄范围。
索引设计要点
- 查询的过滤字段、排序字段、覆盖字段决定了该建什么索引
- 复合索引字段顺序要与最常用查询的条件顺序一致
- 低选择性字段(如只有几个取值的状态)单独建索引收益有限
- 一个集合索引不宜过多,每个索引都会放大写入成本
- 排序方向要与索引方向一致,否则仍需内存排序
- 定期用 $indexStats 找出从未被使用的索引并清理
索引维护与清理
- 滚动建索引更稳妥:先在从节点建好,再逐步切换主节点
- 用 $indexStats 看索引被访问次数,长期为零的索引可评估下线
- 删索引用 dropIndex(name) 前先确认没有查询依赖它
- 改索引定义优先「先建新再删旧」,避免中间出现无索引窗口
- 索引占用用 db.collection.stats() 的 indexSizes 查看
- 哈希索引只支持等值匹配,适合分片键,范围查询要用普通索引
构建索引会与写入争抢 IO:安排到业务低峰期,构建期间用 db.currentOp() 观察进度,不要中途重启实例。
分片集合上的唯一索引有限制:分片键必须包含在唯一索引字段中,否则无法创建,规划分片键时就要把唯一性需求考虑进去。
TTL 索引的删除由后台线程按分钟级调度,不会精确到过期时刻;需要严格按时清理的场景应另设计清理任务。
索引命名建议带上字段与方向(如 status_1_ts_-1),系统默认名称在字段较多时难以辨认,巡检与删除都容易认错。