• 幕客老师召集小伙伴
  • python自动化运维项目实战
  • nginx从入门到实战
  • 阿里云与Centos7实战

mongodb-数据库的建模与表结构设计

一、内嵌对象

 

iaskjob:PRIMARY> db.doc_test.contacts.insert({username:"tom",contact:{phone:"222-333-444",email:"aaa@gmail.com"},access:{level:5,group:"dev"}})
WriteResult({ "nInserted" : 1 })
iaskjob:PRIMARY> db.doc_test.contacts.insert({username:"lily",contact:{phone:"123-333-444",email:"liliy@gmail.com"},access:{level:2,group:"hr"}})
WriteResult({ "nInserted" : 1 })

{
        "_id" : ObjectId("5516cbf8df8130a54260ff15"),
        "username" : "tom",
        "contact" : {
                "phone" : "222-333-444",
                "email" : "aaa@gmail.com"
        },
        "access" : {
                "level" : 5,
                "group" : "dev"
        }
}

优点:只需一次查询即可获取数据

缺点:数据重复性(占空间、内存)不可作为单独对象、修改、大小(每个document 大小16MB)

二、链接对象

 

doc

1、可以作为单独对象

2、需要进行二次查询

三、文档建模

1、一对较少

场景:考试成绩、个人住址

iaskjob:PRIMARY> db.doc_test.grades.insert({name:'edison',grades:[{project:”english”,grade:80},{project:”math”,grade:90}]})

{
        "_id" : ObjectId("5516d547df8130a54260ff17"),
        "name" : "edison",
        "grades" : [
                {
                        "project" : "english",
                        "grade" : 80
                },
                {
                        "project" : "math",
                        "grade" : 90
                }
        ]
}

#类似于一个名片,直接查询名字即可查到相关结果。

2、一对较多

以一个游戏的系统为例子,每个人所获卡牌, 可能有几百个。那我们可能将这些卡牌的id 作为数组存储在该用户信息中,此外每个卡 牌又有其自己的文档对象(实际中会有所变 动,此处仅是例子) 实际中会碰到一个问题,大家可以想想是什 么问题。

iaskjob:PRIMARY> db.doc_test.card_id.insert({name:"jeson",card:[1,2,3,4]})
WriteResult({ "nInserted" : 1 })

iaskjob:PRIMARY> db.doc_test.card_number.insert({_id:1,name:'a1',star:'1',ablility:"90"})
WriteResult({ "nInserted" : 1 })
iaskjob:PRIMARY> db.doc_test.card_number.insert({_id:2,name:'a2',star:'3',ablility:"80"})
WriteResult({ "nInserted" : 1 })

{
        "_id" : ObjectId("5516d80ddf8130a54260ff19"),
        "name" : "jeson",
        "card" : [
                1,
                2,
                3,
                4
        ]
}

{ "_id" : 1, "name" : "a1", "star" : "1", "ablility" : "90" }
{ "_id" : 2, "name" : "a2", "star" : "3", "ablility" : "80" }
iaskjob:PRIMARY> 

3、一对很多

我们可以假设这样的一个情景,这是一个日 志收集系统,所有游戏各个机器的游戏log, 那我们就会发现使用“父级引用”更为合理 与有效。查询的时候,只需要取出某台机器 的_id 然后去查询。

db.doc_test.hostinfo.insert({"_id":1,hostname:"jeson_host",ip:'10.10.10.10'})

iaskjob:PRIMARY> db.doc_test.loginfo.insert({time:"2014-01-01",loginfo:"log info message",host:1})

iaskjob:PRIMARY> db.doc_test.hostinfo.find()
{ "_id" : 1, "hostname" : "jeson_host", "ip" : "10.10.10.10" }

{
        "_id" : ObjectId("5516dbb3df8130a54260ff1a"),
        "time" : "2014-01-01",
        "loginfo" : "log info message",
        "host" : 1
}

4、双向关联

我们以游戏系统中的任务系统为例,有 person和task2个集合

iaskjob:PRIMARY> db.test_haha.person.insert({name:"jeson",task:[1,2,3,4,5,6]})
WriteResult({ "nInserted" : 1 })

iaskjob:PRIMARY> db.test_haha.person.findOne()
{
        "_id" : ObjectId("551778f878dc92475e026884"),
        "name" : "jeson",
        "task" : [
                1,
                2,
                3,
                4,
                5,
                6
        ]
}

如果我们的任务系统有共享任务,就会涉 及任务所有者,这样就可以在task集合中增 加一个owner。

iaskjob:PRIMARY> db.test_haha.tasks.insert({_id:1,name:'xxx',info:'xxx',owner:211})
WriteResult({ "nInserted" : 1 })

5、反范式

就以之前我们提到的游戏中card的系统为 例。下面是没有加入反范式时候的结构。

{

        "_id" : ObjectId("5516d80ddf8130a54260ff19"),

        "name" : "jeson",

        "card" : [

                1,

                2,

                3,

                4

        ]

}

iaskjob:PRIMARY> db.doc_test.card_info.insert({_id:1,cardinfo1:'xxxx',cardinfo2:'xxxxxx2'});
WriteResult({ "nInserted" : 1 })
iaskjob:PRIMARY> db.doc_test.card_info.insert({_id:2,cardinfo1:'yyyy',cardinfo2:'yyyyyy2'});
WriteResult({ "nInserted" : 1 })
iaskjob:PRIMARY> db.doc_test.card_info.insert({_id:3,cardinfo1:'zzzz',cardinfo2:'zzzzzz2'});
WriteResult({ "nInserted" : 1 })

{ "_id" : 1, "cardinfo1" : "xxxx", "cardinfo2" : "xxxxxx2" }
{ "_id" : 2, "cardinfo1" : "yyyy", "cardinfo2" : "yyyyyy2" }
{ "_id" : 3, "cardinfo1" : "zzzz", "cardinfo2" : "zzzzzz2" }

实际应用中,我 们会反范式的内 容,大多与玩家 相关,比如,卡 牌等级,觉醒系 数等。这样的话独立出来的card信息表放入前端应用中作为静态数据独立使用。

iaskjob:PRIMARY> db.test_haha.user.insert({name:'jeson',card:[{id:1,name:’xx’,level:10},{id:2,name:’xx2′,level:4},{id:3,name:’xx’,level:2}]})
WriteResult({ "nInserted" : 1 })
iaskjob:PRIMARY> db.test_haha.user.findOne()
{
        "_id" : ObjectId("55178dfc977683d65b5c29b9"),
        "name" : "jeson",
        "card" : [
                {
                        "id" : 1,
                        "name" : "xx",
                        "level" : 10
                },
                {
                        "id" : 2,
                        "name" : "xx2",
                        "level" : 4
                },
                {
                        "id" : 3,
                        "name" : "xx",
                        "level" : 2
                }
        ]
}

三、树形结构建模

1

父文档引用:

2

db.categories.findone( { _id: "mongodb" } ).parent

db.categories.find( { parent: "databases" } )

子文档引用:

3

db.categories.findone( { _id: "databases" } ).children

db.categories.find( { children: "mongodb" } )

Documents: 以下是我们需要谨记的:

1、优先考虑内嵌,除非有什么迫不得已的原因。

2、如果我们需要单独访问一个对象,那么他就不 适合被内嵌到其他对象中。

3、数组不应该无限制增长。(系统会不断的预分配空间、导致IO,Document 空间限制16MB) 

4、不要太过担心应用层级别的join

5、在进行反范式设计时请先认真考量业务逻辑与 情况。(存储信息与用户相关、与原数据很少关联)

6、find查询时避免使用skip,略过大量的查询结果,否则会导致查询缓慢。可以考虑通过sort(date:-1)方式代替。

转载请注明:学习找工作 » 密码保护:[mongodb]数据库的建模与表结构设计

mongodb-数据库的建模与表结构设计

Pingbacks已打开。

引用地址

暂无评论

发表评论