MongoDB开发技巧12例笔记一

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

技巧一:速度与完整性的折中

在关系数据库中,我们一般范式化设计,而且有时候又会有冗余来提高查询效率,这里说的就是范式化和反范式化两种策略取舍建议。

已商品订单为例,范式化的设计:


//商品
{
    "_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引用),反范式化读取快,一致性削弱了。

我们在选择时,主要考虑以下几点:

是否总是额外读取一次几乎不怎么改变的数据?

一致性很重要吗?

要不要快速的读取?

 

技巧三:尽量单个查询获取数据

MongoDB的数据库设计要从应用单元的查询出发,应用单元可以理解成对后端的一次请求,很多情况下一个应用单元要对应多个文档,但尽量只通过单条查询来完成这种请求。

 

技巧四:嵌入关联数据

这个和第一个类似,那些只有一个键一个值的表(如标签、权限、省市)在MongoDB中几乎都应该嵌入,还有某些信息只在一个文档中使用,则应该嵌入这个文档。

 

技巧五:嵌入时间点数据

什么是时间点数据呢?订单中的商品图片与价格都是时间点数据,比如A用户100元购买,过段时间打折了,B用户可能70就购买了同样的商品。同样,订单中的收货地址也是时间点数据,若某人更新了个人信息,并不需要更改以往订单的收货地址。

 

技巧六:不要嵌入不断增加的数据

MongoDB存储数据的机制决定了对数组不断追加数据是很低效的。嵌入20个、100个、1000000个子文档都不是问题,关键是提前就这么做, 之后基本保持不变。对数组,特别是动态数组数据结构了解的同学应该知道,如果数组长度是10,那么追加第11个元素时,系统一般是新分配20个长度的空 间,把原来数组的10个元素复制过去,再追加第11个元素。MongoDB文档的分配也是一样的,所以,放任文档不断增长会使系统慢得让你受不了的。


技巧七:预填充数据

如果已经知道未来要用到哪些字段,第一次插入的时候就定义这些字段会比用到时再动态创建效率更高,这点要效仿关系型数据库了,比如月统计:

{
    "_id":201403,
    "day1":0,
    "day2":0,
    ......,
    "day31":0
}
//或用数组
{
    "_id":201403,
    days:[0,0,......,0]
}



这一思想也适用于其他类型的数据,甚至集合和数据库也可以用类似的方法。若每天都要使用新的集合,最好也提前创建。

 

技巧八:尽可能预先分配空间

这个和技巧六、七关系紧密。只要知道文档开始比较小,后来会变成确定大小,就可以用这种方法优化。具体的方法就是用垃圾字段填充到和最终文档大小一样大,然后删除该字段。

> collection.insert({"_id":123, /* 其他字段*/, "garbage":"someLongString"})
> collection.update({"_id":123},{"$unset":{"garbage":1}})



技巧九:用数组存放要匿名访问的内嵌数据

内嵌信息到底用数组还是用子文档?这个要根据查询来,如果确切知道要查询的内容,则要用子文档。如果不清楚查询的具体内容,则要用数组。这个举例说明反而麻烦,具体做法就是先设计出来,看能否满足你的查询,如果不行,就换另一种好了!

 

技巧十:文档要自给自足

MongoDB几乎不做任何数据的处理,仅仅存储数据,要尽量遵守这点。像求平均值或求和,要放在客户端去做。所以,那些在关系数据库里的一般聚合查询,在MongoDB里,最好用单独的字段保存起来。

 

技巧十一:优先使用$操作符

有些情况查询不能用$操作符表达,这时可以使用$where字句来执行JavaScript代码:

db.members.find({"$where":function(){
    return this.member[0].age==this.member[1].age;
}});



this就是每一个文档,正如预期,$where为查询带来了极大的灵活性,但效率着实不高!

效率不高是因为MongoDB要为此做很多事情,一般的查询(没有$where的查询),客户端将查询转换成BSON,MongoDB 中数据也是BSON格式,所以可以直接比较,而用$where时,MongoDB要将集合每个文档转换成JavaScript对象,解析文档BSON并添 加所有字段到JavaScript对象中,然后执行JavaScript代码,之后就销毁,所以极其消耗时间和资源。

如果非要用$where不可时,要尽量减少$where匹配的文档数来缓解性能损失,具体做法就是在$where条件前加限定条件。

技巧十二:随时聚合

要尽可能用$inc随时计算聚合,这点和技巧十相应,如果前期没有随时聚合,应该都通过批处理计算完成。

转载于:https://my.oschina.net/lichaoqiang/blog/542778

你可能感兴趣的:(数据库,javascript,python)