面试题答案
一键面试索引设计
- 复合索引用于按用户ID和下单时间范围查询:对于根据用户ID和下单时间范围获取订单的查询,应该创建一个复合索引。在MongoDB中,复合索引的顺序很重要,应将选择性高(区分度大)的字段放在前面。通常用户ID的区分度比下单时间范围大,所以索引顺序为
userID
在前,orderTime
在后。这样可以快速定位到特定用户的订单,然后再在这个子集里根据下单时间筛选。// 假设mongoose模型定义如下 const mongoose = require('mongoose'); const orderSchema = new mongoose.Schema({ orderId: String, userId: String, orderAmount: Number, orderTime: Date }); orderSchema.index({ userId: 1, orderTime: 1 }); const Order = mongoose.model('Order', orderSchema);
- 单索引用于按订单金额范围查询:对于根据订单金额范围获取订单的查询,创建一个针对
orderAmount
的单索引即可。orderSchema.index({ orderAmount: 1 });
在mongoose模型定义中设置索引
- 复合索引设置:如上述代码,使用
index
方法在Schema
上定义复合索引。index
方法接受一个对象,对象的键是字段名,值为1表示升序索引,-1表示降序索引。对于userId
和orderTime
的复合索引,代码为orderSchema.index({ userId: 1, orderTime: 1 });
。 - 单索引设置:同样使用
index
方法定义单索引,例如针对orderAmount
的单索引,代码为orderSchema.index({ orderAmount: 1 });
。
避免索引争用等性能问题
- 合理规划索引数量:避免创建过多不必要的索引。每个索引都会占用额外的存储空间,并且在写入操作时会增加开销。只创建真正需要用于查询的索引。
- 读写分离:在高并发场景下,可以考虑使用读写分离策略。主节点处理写操作,从节点处理读操作。这样可以减轻主节点的压力,减少读写操作之间的争用。在Node.js中,可以通过配置
mongoose
连接字符串来指定主从节点。例如:const mongoose = require('mongoose'); const uri ='mongodb://primary:27017,secondary1:27017,secondary2:27017/mydb?replicaSet=myReplicaSet&readPreference=secondaryPreferred'; mongoose.connect(uri, { useNewUrlParser: true, useUnifiedTopology: true });
- 索引维护:定期检查索引的使用情况,使用
db.collection.stats()
命令查看索引的使用频率等信息。对于长时间未使用的索引,可以考虑删除,以减少索引维护的开销。 - 批量操作:尽量使用批量操作代替单个操作。例如,在进行插入操作时,使用
insertMany
而不是多次insertOne
。这样可以减少数据库的交互次数,提高性能,同时也能减少索引争用的机会。