2012-01-13 157 views
5

这个问题适用于所有NoSQL,特别是那里的mongoDB专家。我从一个项目设计关系数据库开始,但客户希望我们使用可轻松扩展的数据库。为了实现这一点,我们决定使用mongoDB。现在,我无法映射NoSQL的关系模型。我有与很多其他表的许多一对多的关系,一个用户表如下图所示:与NoSQL数据库的关系

选项:

relational DB

转换它,当MongoDB的我有几个选项1(在用户完成行):

users:{ 
    _id:<user_id>, 
    battles:{[battle1, battle2, ...]}, 
    items:{[item1, item2, ...]}, 
    locations:{[location1, location2, ...]}, 
    units:{[unit1, unit2, ...]}, 
} 

battles:{ 
    <battle_info> 
} 

locations:{ 
    <location_info> 
} 

units:{ 
    <units_info> 
} 

items:{ 
    <items_info> 
} 

1选项(在用户只外键):

users:{ 
    _id:<user_id>, 
    battles:{[battle1_id, battle2_id, ...]}, 
    items:{[item1_id, item2_id, ...]}, 
    locations:{[location1_id, location2_id, ...]}, 
    units:{[unit1_id, unit2_id, ...]}, 
} 

battles:{ 
    <battle_info> 
} 

locations:{ 
    <location_info> 
} 

units:{ 
    <units_info> 
} 

items:{ 
    <items_info> 
} 

选项3(在其它表中的用户ID):

users:{ 
    _id:<user_id>, 
} 

battles:{ 
    <battle_info>, 
    user:{[user1_id, user2_id, ...]} 
} 

locations:{ 
    <location_info>, 
    user:{[user1_id, user2_id, ...]} 
} 

units:{ 
    <units_info>, 
    user:{[user1_id, user2_id, ...]} 
} 

items:{ 
    <items_info>, 
    user:{[user1_id, user2_id, ...]} 
} 

选项1具有很多重复,因为我们正在添加其他表的完整的行。我在这里看到的一个问题是,如果某个项目或战斗更新了,我们将不得不在用户表中找到它的所有事件并更新它们。但是这给我们带来的好处是始终有一个完整的用户对象,可以在登录时交给客户端应用程序。

选项2更具关系性,我们只有用户表中其他表的mongoIds。这个选项的好处是,更新战斗或物品不会有太多的成本,因为行不被复制。另一方面,当用户登录时,我们将不得不查找所有引用的单位,战斗,项目和位置以用完整的用户对象进行响应。

选项3与选项2相反,其中users表的mongoIds保存在其他表中。这个选项对我没有吸引力。

我真的很感激有人能指导我或想出一个更好的模型。

编辑:

基本上这是一个MMORPG游戏,其中多个客户端应用程序将通过Web服务连接到服务器。我们在客户端有一个本地数据库来存储数据。我想要一个模型,服务器可以通过它完成一个完整的用户对象的响应,然后更新或插入在客户端应用上更改的数据

+0

“更好”是一个目的的问题。这些数据的访问模式是什么? – 2012-01-13 11:05:48

+0

基本上这是一个MMORPG游戏,其中多个客户端应用程序将通过web服务连接到服务器。我们在客户端有一个本地数据库来存储数据。我想要一个模型,通过它服务器可以用一个完整的用户对象进行响应,然后更新或插入在客户端应用程序中更改的数据。 – umair 2012-01-13 11:09:12

+8

关系数据库具有相当的扩展能力...差异应该是它们预期可以保持的数据类型,不是一个虚假的偏见,认为RDB只适用于小数据,mongodb适用于大型 – 2012-01-13 11:09:33

回答

5

首先,NoSQL的是一刀切。在SQL中,几乎每个1:N和M:N关系都以相同的方式建模。 NoSQL的理念是,您对数据进行建模的方式取决于数据及其使用模式。

其次,我同意马克贝克:缩放比较难,它是通过松动约束来实现的。这不是一个技术问题。我喜欢和MongoDB的工作,但由于其他原因(无需对码丑陋的SQL;不需要复杂的,臃肿的ORM等等)

现在让我们回顾一下你的选择: 选项1个拷贝的数据比需要的。您通常需要将的某些数据进行非规范化处理,但不会全部使用。如果是这样,那么获取被引用的对象会更便宜。

选项2/3它们非常相似。这里的关键是:谁在写作?您不希望很多客户端可以对同一个文档进行写入访问,因为这会强制您使用锁定机制,并且/或者仅限于修改器操作。因此,选项2可能优于3.但是,如果A攻击B,它们也会触发写入用户B,因此您必须确保写入安全。

选项4部分非规范化:您的用户对象似乎是最重要的,所以这个怎么样:

user { 
battles : [ {"Name" : "The battle of foo", "Id" : 4354 }, ... ] 
... 
} 

这将使它更容易显示例如用户仪表板,因为您不需要知道仪表板中的所有详细信息。注意:数据结构然后耦合到演示文稿的细节。

选项5边缘数据。通常情况下,相对于需要保存数据,以及:

user { 
battles : [ {"Name" : "The battle of foo", "unitsLost" : 54, "Id" : 34354 }, ... ] 
} 

这里,unitsLost是特定于用户和战斗中,因此数据坐落在图形的边缘。与战斗的名称相反,这些数据不是非规范化的。

选项6链接器集合。当然,这样的“边缘数据”可能会变得很大,甚至可能需要单独收集(链接器集合)。这完全消除了访问锁的问题:

user { 
    "_id" : 3443 
} 

userBattles { 
    userId : 3443, 
    battleId : 4354, 
    unitsLost : 43, 
    itemsWon : [ <some list > ], 
    // much more data 
} 

哪一个最好取决于你的应用程序的很多细节。如果用户进行了大量的点击(例如,您有一个细致的界面),则可以像选择4或6那样分割对象。如果您确实需要一批中的所有数据,则部分非规范化不会有帮助,所以选项2将是更可取的。请记住多个写入器问题。

+1

感谢您的详细解答。您提出了一个有趣的观点,并不是所有的多对多关系在NoSQL中都具有相同的结构。我想我必须根据我的需要来构造它们。 – umair 2012-01-13 13:06:16

1

选项2是要走的路。

如果您要在RDB中执行此操作,则在某个时间点(当您必须开始水平缩放时),您还需要开始删除SQL连接并在应用程序级别连接数据。

即使10gen公司建议使用 “手动” 参考IDS:http://www.mongodb.org/display/DOCS/Database+References