您的位置:首页 > 数据库 > Mongodb

mongodb 最佳实践

2017-07-25 14:32 387 查看


模式设计


不要按照关系型来设计表结构

MongoDB可以让你像关系型数据库一样设计表结构,但是它不支持外键,也不支持复杂的Join!如果你的程序发现有大量实用JOIN的地方,那你的设计可能需要重新来过。参照以下相关模式设计建议。


数据库集合(collection)的数量不宜太多

MongoDB的模式设计基于灵活丰富的JSON文档模式。在很多情况下,一个MongoDB应用的数据库内的集合(表)的数量应该远远小于使用关系数据库的同类型应用。MongoDB表设计不遵从第三范式。MongoDB的数据模型非常接近于对象模型,所以基本上就是按照主要的Domain object的数量来建相应的集合。根据经验,一般小型应用的集合数量通常在几个之内,中大型的应用会在10多个或者最多几十个。


不要害怕数据冗余

MongoDB模式设计不能按照第三范式,很多时候允许数据在多个文档中重复,比如说,在每一个员工的文档中重复他的部门名字,就是一个可以接受的做法。如果部门名字改了,可以执行一个update({},{}, {multi:true}) 的多文档更新来一次性把部门名字更新掉。


适合和不适合冗余的数据类型

一般来说,如果某个字段的数据值经常会变,则不太适合被大量冗余到别的文档或者别的集合里面去。举例来说,如果我们是在做一些股票类型资产管理, 可能有很多人都购买了Apple的股票,但是如果把经常变动的股价冗余到客户的文档里,由于股票价格变动频繁,会导致有大量的更新操作。从另外一个角度来说,如果是一些不经常变的字段,如客户的姓名,地址,部门等,则可以尽管进行冗余shi’yang


对 1:N(一些)的关系使用全部内嵌

对于一对多的关系,如一个人有几个联系方式,一本书有10几个章节,等等,建议使用内嵌方式,把N的数据以数组形式来描述,如:
> db.person.findOne()
{
user_id: 'tjworks',
name: 'TJ Tang',
contact : [
{ type: 'mobile', number: '1856783691' },
{ type: 'wechat', number: 'tjtang826'}
]
}


对 1: NN (很多) 的关系使用ID内嵌

有些时候这个一对多的多端数量较大, 比如说,一个部门内有多少员工。在华为一个三级部门可能有数千员工,这个时候如果把所有员工信息直接内嵌到部门内肯定不是个好的选择,有可能会超出16MB的文档限制。这个时候可以采用引用ID的方式:
> db.departments.findOne()
{
name : 'Enterprise BG',
president: 'Zhang San',
employees : [     // array of references to Employee colletion
ObjectID('AAAA'),
ObjectID('F17C'),
ObjectID('D2AA'),
// etc
]
}


如果需要查询部门下员工相关信息,你可以使用$lookup聚合操作符来把员工信息进行关联并返回。


对 1: NNN (很多) 的关系使用

如果一对多情况下,这个多端数量无限大并会频繁增长,比如说,一个测量仪的每分钟读数,一年下来有几十万条,这个时候即使是把ID放到数组里都会管理不便,这个时候就应该把多端的数据创建一个集合,并在那个集合的文档里加入对主文档的连接引用,如:
> db.sensors.findOne()
{
_id : ObjectID('AAAB'),
name : 'engine temperature',
vin : '4GD93039GI239',
engine_id: '20394802',
manuafacture: 'First Motor',
production_date: '2014-02-01'
...
}

>db.readings.findOne()
{
time : ISODate("2014-03-28T09:42:41.382Z"),
sensor: ObjectID('AAAB'),
reading: 67.4
}


把二进制大文件和元数据分集合存放

如果你有需要把PDF文件,图片,甚至小视频等二进制文件需要管理,建议使用MongoDB 的GridFS API 或者自己手动分集合来分开管理二进制数据和元数据。


经常更新的数据不要放在嵌套数组内

数组是用来表达 1对多关系的利器,但是MongoDB对嵌套的数组内元素缺乏直接更新能力。比如说:
{
name: "Annice",
courses: [
{ name: "English", score: 97 },
{ name: "Math", score: 89 },
{ name: "Physics", score: 95 }
]
}


这样设计没有嵌套数组,我们可以直接对 Math的score 修改为99:
db.students.update({name: "Annice", "courses.name":"Math"}, {$set:{"courses.$.score": 99 }})


注意数组定位符 $ 的用法,$ 表示当前匹配的第一个数组元素的在数组内的索引。

但是下面这种情况就涉及到了数组嵌套:
{
name: "Annice",
courses: [
{ name: "Math", scores: [
{term: 1, score: 80} ,
{term: 2, score: 90}
]
},
{ name: "Physics", score: 95 }
]
}


这个时候如果你想对Math course的term 1的Score进行修改,你就需要把 scores 这个数组整个调到内存然后在代码里对这个嵌套数组的元素进行修改。这是因为MongoDB的数组定位符 $ 只对第一层数组有效。

当然,如果你的模型不需要修改嵌套的数组内元素,那么这条就不适用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: