文档数据库服务支持丰富的索引机制,合理使用索引能够加快数据检索速度并降低资源消耗。
单字段索引
比如 coll表中有如下格式的文档:
{ "_id" : ObjectId("64bde367d89fa22ccaa281e9"), "score" : 98, "name" : "zhang san" }
{ "_id" : ObjectId("64bde367d89fa22ccaa281ea"), "score" : 88, "name" : "li si" }随着业务的增长,文档数逐渐变多后,如果想要按照 score 进行排序并提取 top 10 的文档就显得比较困难,因为涉及到全表扫描和内存排序。
此时可以按照 score 字段创建索引来加速查询,使用 mongo shell 创建索引的命令如下:
db.coll.createIndex({score:1})命令中包含的 score 字段表示索引的 key, 1 表示升序,-1 表示降序。文档数据库服务会按照 score 字段创建索引,底层实现为 b-tree 形式。用户通过 score 字段进行等值查询或者排序时,文档数据库服务会自动选择索引进行加速。
用户也可以在连接上实例后,通过下面的命令查看目前表中包含哪些索引:
db.coll.getIndexes()
多字段复合索引
假设 coll 表中有如下文档:
{ "_id" : ObjectId("64bde367d89fa22ccaa281e9"), "score" : 98, "name" : "zhang san" }
{ "_id" : ObjectId("64bde367d89fa22ccaa281ea"), "score" : 88, "name" : "li si" }
{ "_id" : ObjectId("64bde73ad89fa22ccaa281eb"), "score" : 98, "name" : "wang wu" }
{ "_id" : ObjectId("64bde73ad89fa22ccaa281ec"), "score" : 98, "name" : "ma liu" }如果希望查询 score=98 的所有文档,并按照 name 字段进行升序排序。
此时可以对 score 和 name 字段创建复合索引,使用 mongo shell 创建复合索引的命令如下:
db.coll.createIndex({score:1, name:1})复合索引比单字段索引能够处理的场景更多,比如上述复合索引既可以处理 score+name 联合查询的场景,也能处理只按照 score 等值查询和排序的场景。
但是使用复合索引也有一些注意事项:
复合索引遵循前缀匹配原则。比如 {score: 1, name: 1} 复合索引能处理按照 score 查询的场景,但是不能处理按照 name 查询的场景。因此在建索引时需要特别注意字段顺序。
字段越多,索引的存储空间越大。比如用户只有按照 score 字段进行查询和排序的场景,就没有必要创建复合索引了。
Hash 索引
如果只涉及到对单个字段的等值查询,可以考虑创建 hash 索引,使用 mongo shell 创建 hash 索引的方式如下:
db.coll.createIndex({score: "hashed"})Hash 索引底层也是使用 btree 存储,但是区别在于 btree 的 key 是 hash 之后的值。
对于分片版本的文档数据库实例,如果按照 hash 方式创建分片表,则每个 shard server 上会自动创建基于分片键的 hash 索引。
Hash 索引有一些限制:
不能指定唯一属性。
不能进行范围扫描。
如果要创建索引的字段很长,由于 hash 索引是按照的 64bit 的hash 值创建索引,可以在一定程度上节省索引的存储空间。
索引属性
在创建索引时可以指定属性完成更高级的功能,常用的配置属性有:
TTL 。实现数据自动过期删除。比如有 coll 表中存放了系统运行日志,其中包含了 createTime 指定了日志生成时间,则可以按照这个字段创建 TTL 索引自动删除 1 个小时以前的数据:db.coll.createIndex( { "createTime": 1 }, { expireAfterSeconds: 3600 } )。
Unique。指定字段的唯一性。如果重复插入该字段值相同的数据会报错失败,指定方式为 db.coll.createIndex( { "name": 1 }, { unique: true } )。
Partial。只对满足条件的文档创建索引。比如只对分数大于 80 的文档创建索引:db.coll2.createIndex({score:1, name:1}, {partialFilterExpression: {score: {$gt: 80}} })。