与NoSQL数据库的关系

uma*_*air 5 php database-design relational-database mongodb nosql

这个问题适用于所有NoSQL和特别是mongoDB专家.我开始为项目设计关系数据库,但客户希望我们使用可以轻松扩展的数据库.为此,我们决定使用mongoDB.这些天我无法映射我的NoSQL关系模型.我有一个用户表,它与许多其他表有多对多的关系,如下图所示:

关系数据库

转换为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>
}
Run Code Online (Sandbox Code Playgroud)

Option2(用户只有外键):

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>
}
Run Code Online (Sandbox Code Playgroud)

选项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, ...]}
}
Run Code Online (Sandbox Code Playgroud)

选项1有很多重复,因为我们正在添加其他表的完整行.我在其中看到的一个问题是,如果某个项目或战斗更新,我们将不得不在用户表中找到它的所有出现并更新它们.但是这给了我们一个优势,即始终拥有一个完整的用户对象,可以在登录时将其传递给客户端应用程序.

选项2更具关系性,我们在users表中只有mongoIds的其他表.此选项的优点是更新战斗或项目没有太多成本,因为引用的行未被复制.另一方面,当用户登录时,我们必须找到所有引用的单位,战斗,项目和位置以响应完整的用户对象.

选项3与选项2相反,其中用户表的mongoIds保存在其他表中.这个选项对我没什么吸引力.

我真的很感激有人可以指导我或想出一个更好的模型.

编辑:

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

mne*_*syn 5

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

其次,我同意Mark Ba​​ker:缩放很难,而且它是通过放松约束来实现的.这不是技术问题.我喜欢使用MongoDB,但出于其他原因(不需要编写丑陋的SQL代码;不需要复杂,膨胀的ORM;等等)

现在让我们回顾一下您的选择: 选项1复制的数据超出了需要.您经常需要对某些数据进行非规范化,但绝不会全部归一化.如果是这样,获取引用的对象会更便宜.

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

选项4部分非规范化:您的用户对象似乎是最重要的,那么如何:

user { 
 battles : [ {"Name" : "The battle of foo", "Id" : 4354 }, ... ]
 ...
}
Run Code Online (Sandbox Code Playgroud)

这样可以更轻松地显示用户仪表板,因为您无需了解仪表板中的所有详细信息.注意:数据结构然后耦合到演示的细节.

选项5边缘数据.通常,关系也需要保存数据:

user {
 battles : [ {"Name" : "The battle of foo", "unitsLost" : 54, "Id" : 34354 }, ... ]
}
Run Code Online (Sandbox Code Playgroud)

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

选项6链接器集合.当然,这种"边缘数据"可能会变得庞大,甚至可能需要一个单独的集合(链接器集合).这完全消除了访问锁定的问题:

user { 
  "_id" : 3443
}

userBattles {
  userId : 3443,
  battleId : 4354,
  unitsLost : 43,
  itemsWon : [ <some list > ],
  // much more data
}
Run Code Online (Sandbox Code Playgroud)

其中哪一个最好取决于您的应用程序的许多细节.如果用户进行了大量的点击(即你有一个细粒度的界面),那么分离对象就像选项4或6一样.如果你真的需要一批中的所有数据,那么部分非规范化无济于事,所以选项2会更好.请记住多个作家的问题.