关系型数据库与NoSQL数据库的区别

5
这个问题适用于所有的NoSQL专家,尤其是mongoDB专家。我最初为一个项目设计了一个关系型数据库,但客户想要我们使用能够轻松扩展的数据库。为了实现这一点,我们决定使用mongoDB。近日,我在将我的关系模型映射到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>
}

选项2(仅在用户中使用外键):

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相反,用户表的mongoIds保存在其他表中。我不太喜欢这个选项。
如果有人能指导我或提出更好的模型,我会非常感激。
基本上这是一个MMORPG游戏,多个客户端应用程序将通过Web服务连接到服务器。我们在客户端有一个本地数据库来存储数据。我希望有一个模型,使服务器能够响应完整的用户对象,然后更新或插入在客户端应用程序上更改的数据。

“更好”是一个目的问题。您对这些数据的访问模式是什么? - Sergio Tulentsev
基本上,这是一个多人在线角色扮演游戏,多个客户端应用程序将通过 Web 服务连接到服务器。我们在客户端拥有本地数据库来存储数据。我想要一个模型,通过该模型,服务器可以响应完整的用户对象,然后更新或插入在客户端应用程序上更改的数据。 - umair
8
关系型数据库完全可以扩展...两者的不同之处应该在于它们预计要存储的数据类型,而不是错误的先入为主观念认为关系型数据库只适合小数据,mongodb才适合大数据。 - Mark Baker
2个回答

5
首先,NoSQL并非一刀切的解决方案。在SQL中,几乎每个1:N和M:N关系都是以相同方式建模的。而NoSQL的哲学是,你建模数据的方式取决于数据及其使用模式。
其次,我同意Mark Baker的观点:扩展很难,并且通过放宽约束来实现,这不是技术问题。我喜欢使用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

1

选项2是最好的选择。

如果您在关系型数据库中执行此操作,那么在某个时刻(当您需要开始水平扩展时),您还需要开始在应用程序级别上删除SQL连接并连接数据。

即使是10gen也建议使用“手动”引用ID:http://www.mongodb.org/display/DOCS/Database+References


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接