「 简记往来」第十六篇:数据库索引设计——从0到62万条记录的查询优化
·
一、问题:数据量涨了,查询变慢了
简记往来上线初期,用户少、数据少,所有查询都是毫秒级返回。
随着用户增长到6.8万,记录数突破62万条,一些复杂的聚合查询开始变慢——最慢的到了600ms。
600ms对用户来说就是“卡住了”。必须优化。
二、索引的基本原理
MongoDB的索引和MySQL的索引原理类似:索引是一种数据结构(B-Tree),让数据库不必扫描全表就能找到数据。
没有索引时,查询 WHERE book_id = ? AND contact_id = ? 需要扫描所有记录(62万条)。有索引后,只需要扫描索引中匹配的少量记录。
索引设计的原则:
- 为查询建索引:不是所有字段都需要索引,只为WHERE和ORDER BY涉及的字段建
- 复合索引的顺序很重要:最常用的查询条件放在前面
- 选择性高的字段优先:值分布广的字段(如
contact_id)比值分布窄的字段(如type)更适合建索引
三、简记往来的核心索引
根据查询模式,简记往来建立了三个核心索引:
// 1. 按账本+日期查询(流水页)
db.records.createIndex({ book_id: 1, date: -1 })
// 2. 按账本+联系人查询(差额统计)——最常用
db.records.createIndex({ book_id: 1, contact_id: 1 })
// 3. 按账本+类型统计(收/送礼汇总)
db.records.createIndex({ book_id: 1, type: 1 })
复合索引遵循 ESR 规则:Equality(等值条件)→ Sort(排序字段)→ Range(范围条件)。
四、索引对查询性能的影响
| 查询类型 | 无索引 | 有索引 | 提升 |
|---|---|---|---|
| 按账本查询流水 | 全表扫描(62万条) | 索引扫描(几百条) | 100x+ |
| 按账本+联系人查询 | 全表扫描 | 索引精准定位 | 1000x+ |
| 差额统计聚合 | 全表扫描+内存聚合 | 索引扫描+索引聚合 | 100x+ |
没有索引时,一次简单的查询可能要扫描几十万条记录。有索引后,只需要扫描几条或几十条。
五、如何判断索引是否生效?
使用 MongoDB 的 explain() 方法分析查询执行计划:
db.records.find({ book_id: 'book_xxx', contact_id: 'contact_xxx' }).explain('executionStats')
关键指标:
totalDocsExamined:扫描的文档数(越少越好)totalKeysExamined:扫描的索引键数executionTimeMillis:执行时间
如果 totalDocsExamined 远大于 totalKeysExamined,说明索引没生效,或者索引设计不合理。
六、索引维护策略
索引不是建完就不管了。定期维护:
- 监控索引使用率:MongoDB的
$indexStats可以查看每个索引的使用频率 - 删除未使用的索引:节省存储空间,提升写入性能
- 重建碎片化索引:数据大量变更后,索引可能碎片化,需要重建
// 查看索引使用统计
db.records.aggregate([{ $indexStats: {} }])
七、总结
索引优化的核心是:根据查询模式设计索引,而不是给所有字段建索引。
简记往来当前62万条记录,核心查询响应时间稳定在150ms以内。这个性能是靠三个精心设计的复合索引支撑的。
下一篇,我们来聊聊慢查询优化实录——从600ms到80ms。
评论区聊聊:你的数据库查询遇到过慢查询吗?怎么解决的?
更多推荐


所有评论(0)