最佳实践
传送门:《50 Tips and Tricks for MongoDB Developers》
速度优先使用嵌入数据,完整性优先使用引用数据
规范化架构
{
"_id" : productId,
"name" : name,
"price" : price,
"desc" : description
}
{
"_id" : orderId,
"user" : userInfo,
"items" : [
productId1,
productId2,
productId3
]
}
非规范化架构
{
"_id" : productId,
"name" : name,
"price" : price,
"desc" : description
}
{
"_id" : orderId,
"user" : userInfo,
"items" : [
{
"_id" : productId1,
"name" : name1,
"price" : price1
},
{
"_id" : productId2,
"name" : name2,
"price" : price2
},
{
"_id" : productId3,
"name" : name3,
"price" : price3
}
]
}
合理建立索引
数据示例
{
"_id": ObjectId('xxx'),
"threadId": 123,
"data": ISODate("2022-04-13")
}
查询语句
db.posts.find({"threadId" : id}).sort({"date" : 1}).limit(20)
业务经常需要这种数据分页式查询,则可以建立索引
db.posts.createIndex({'threadId' : 1, 'date' : 1}, {'background': true})
尽可能预填充已知内容
例如某一条记录是用于记录一天内固定的6个小时的访问情况
{
"_id" : pageId,
"start" : time,
"visits" : {
"minutes" : [
[num0, num1, ..., num59],
[num0, num1, ..., num59],
[num0, num1, ..., num59],
[num0, num1, ..., num59],
[num0, num1, ..., num59],
[num0, num1, ..., num59]
],
"hours" : [num0, ..., num5]
}
}
则对于那些仍未发生的内容,可以用缺省值先填充
{
"_id" : pageId,
"start" : someTime,
"visits" : {
"minutes" : [
[0, 0, ..., 0],
[0, 0, ..., 0],
[0, 0, ..., 0],
[0, 0, ..., 0],
[0, 0, ..., 0],
[0, 0, ..., 0]
],
"hours" : [0, 0, 0, 0, 0, 0]
}
}
MongoDB不需要为新内容寻找空间,它只是更新已经输入的值,这样会快很多。
例如,在小时开始时,程序可能会执行以下操作:
> db.pages.update({"_id" : pageId, "start" : thisHour},
... {"$inc" : {"visits.0.0" : 3}})
尽可能预聚合
例:提前把 total 值算好,MongoDB 是很笨重的数据库,对简单的检索效率很高,但是在数据量大的情况下做很复杂的聚合,性能会随着复杂度提升而降低。
> db.food.update(criteria, {"$inc" : {"apples" : 10, "oranges" : -2, "total" : 8}})
> db.food.findOne()
{
"_id" : 123,
"apples" : 20,
"oranges" : 3,
"total" : 23
}
MongoDB提供了以下Read Preference Mode:
- primary:默认模式,一切读操作都路由到replica set的primary节点
- primaryPreferred:正常情况下都是路由到primary节点,只有当primary节点不可用(failover)的时候,才路由到secondary节点。
- secondary:一切读操作都路由到replica set的secondary节点
- secondaryPreferred:正常情况下都是路由到secondary节点,只有当secondary节点不可用的时候,才路由到primary节点。
- nearest:从延时最小的节点读取数据,不管是primary还是secondary。对于分布式应用且MongoDB是多数据中心部署,nearest能保证最好的data locality。
踩坑记录
配置足够多的 Mongos 实例
在一些业务下,会频繁请求后端并读取 Mongo 数据。对于这种业务切忌增加耗时的操作
def get_method():
# 这边通过 Mongo 获取数据
data = self.mongo_service.get()
# 这边有一个耗时的数据处理,例如从别的系统获取数据
self.combine_data_from_http(data)
return data