MongoDB学习笔记(十四):设计和其它补充

1.反范式设计

先看看关系数据库做个关联查询怎么做的  
比如2个表如下,一个订单有多个产品  
products:{  
'id',  
'name',  
'price',  
'desc'  
}  

orders:{  
'id',  
'user',  
'items':[product_id1,product_id2,product_id3]  
}  
这种做法关系数据库肯定没问题,一个表关联查询就行了。但mongodb则不得不查询多次,否则只能拿到产品id而已。大大降低查询性能  

那要一次的话如下设计:  
orders:{  
'id',  
'user',  
'items':[  
{ 'id':product_id1, 'name':..., 'price':..., 'desc':... },  
{ 'id':product_id2, 'name':..., 'price':..., 'desc':... },  
{ 'id':product_id3, 'name':..., 'price':..., 'desc':... }]  
}  

这样只需要一次查询便可,而且还能用elemMatch方法对items进一步过滤,就和关联查询差不多了。虽然DBRef也能做到,但DBRef不能进一步过滤。但这种方式缺点就是你在更新products时放弃了一致性。比如产品名字和价格变化,那老的数据还在order里,但这个需求是正确的,因为产品变了,但当时的订单的确不该变,不是吗。  

何时需要反范式设计:  
1.你认为引用的数据是否需要保持最新的?不是的话用反范式设计。(比如产品和订单的例子)  
2.要保持最新,那更新频率多久一次?如果极少变化,那适合用反范式设计。  
3.必须做到查询快速。那你得用反范式。  

但你可以在修改时,实时或延时同步过去,看数据量是否庞大,太庞大就算了。
 

2.不要把不断增加的数据嵌入

首先说说什么叫嵌入,数据库一对多,有2种做法,比如帖子和评论2个表,评论有个字段是帖子ID,那帖子和评论就是1对多,这个做法是引用。如果帖子有个字段叫评论(是个放ID的数组)这个就是内嵌。  

不断增加的数据,mongodb对数组追加效率是很低的。不管10个1000个10万个都不算啥,但之后要保持不变,否则会慢得你受不了。比如一个帖子的评论,那就应该使用引用做法,而不是内嵌
 
 

3.嵌入做法预留空间

mongodb如果要增加内嵌的数组,是很慢的,要分配空间。但是只是修改内容是很快的。  
所以如果你在插入数据时,明确知道总共多少条或最多多少条,可以先预留空间。放几个0或空字符串填充。
 

4.尽量避免让mongoDB做计算

由于mongodb是种无脑的大型数据库,几乎不会做任何高效的处理。虽然group,mapreduce,和聚合功能都有,但效率不佳。所以尽量避免这样去做。  
比如你有个表  
{id, appleCount, orangeCount}分别有苹果和橘子总数,你当然可以用上面方式得到他们总数,但你最好增加一个totalCount字段来达到这个效果
 
 

5.要有几个定时任务协助mongodb

1. 一致性修复器  
使用反范式模式,但你有希望数据一致性  

2.预分配器  
创建今后文档需要用到的空间,否则到了应用会很慢  

3.聚合器  
通过增加表或增加字段的方式聚合数据  

4.结构校验器  
确保当前文档都有必填字段,否则矫正或通知。  

当然以上都不能做到实时
 
 

6.ObjectId

无意义的主键用ObjectId存储,千万别转换成字符串。  

1.方便查询  
2.含有有用信息  
3.字符串要多占两倍磁盘
 
 

7.如果有自己主键,请不要用默认主键

默认主键_id,如果你有自己的主键就不要用它,节省空间资源。  
要知道1亿数据下,主键要占几百M内存。
 
 

8.DBRef尽量避免使用

1.浪费一个字段$ref的空间2.完全可以自己用id做引用的范式设计代替这个功能
 

9.小文件数据不要用GridFS

GridFS查询性能较差。如果确定都是小文件(小于16M)请存储在表里
 

10.不要到处使用索引

比如你要返回集合中50%记录,就不该使用索引,比不用索引还慢。  
所以,索引一般用在返回结果只是总体数据的一小部分时,超过一般就不要用了。  

如果已经简历索引,但又在大规模查询不用索引,就用{"$natural":1} 强制禁用索引  

写入操作大大多于查询操作,不要使用索引,否则写入会变慢。
 

11.建立分级文档加速扫描

假如你有一个表有10个字段,其中一个是zip,你查询如下
1. db.users.find({'zip':‘10003’})
那mongodb会遍历每个字段来查找zip字段  

使用内嵌文档我们能这样设计  

把10个字段分成3~4个组,每个字段里面放字段  

那么查询语句就变成
1. db.users.find({'address.zip':‘10003’})
 
 

12.and 和 or查询

对于and,越是结果少的要放在条件的前面  
对于or则相反,越是结果多的放在条件的前面  

原理自己想想吧
 

13. 日志和备份节点

日志(-journal)和备份节点都会影响性能,而且都能做为数据恢复用,每台服务器2个都开启实在没啥必要。  
建议是主纯备份节点可以用日志, 主节点和热备节点可以不用。如果没有纯备份节点,就用某个从节点开启日志
 
 

14.开发时使用notablescan参数

开启这个选项,只要做全表扫描都会报错。开发时开启很有用,但生产环境不要开启

你可能感兴趣的:(nosql-mongodb,mongodb)