假设有两个独立的集合: 城市{名称:“NYC”,地区:“1223”,人:[1,2,3]}和 人{personId:1,姓名:“abc”}
正如您所看到的,我们已经在城市中链接了人的ID(RDBMS中的外键)。
现在,如果我决定删除Id = 1的人,那么我想更新Cities中的人员数组,就像在RDBMS中使用级联删除操作一样,我知道我们没有按照{的方式执行级联删除操作{3}},因为我们在MOngoDB中没有事务,如果我们以某种方式无法在城市上执行“级联删除”操作,则数据库可能处于不一致状态。如何确保整个“删除人+城市级联删除”作为MongoDB中的原子操作?如果不能,那么我们是否希望总是嵌入链接?
答案 0 :(得分:3)
嵌入优于链接操作速度和一致性。你想要实现的是mongodb中的一个事务操作,它不是原子的,因为它涉及多个文档。
您可以很容易地从每个人文档中引用该城市,而不是让每个城市引用每个人。对于你所拥有的这种多对一关系来说,这是一个更健全的结构。
如果数据一致性对于类似案例的应用程序很重要,您可能需要考虑使用模拟RDBMS事务的mongodb's two-phase commit pattern。
答案 1 :(得分:1)
如何确保整个“删除城市中的人+级联删除”作为MongoDB中的原子操作?
正如其他人所说,MongoDB有两阶段提交的客户端版本,但是这个:
如果您可以依赖您的应用程序写入数据库并且没有失败,那么两阶段提交的MongoDB版本可以在这里工作;然而,那你为什么不只是在另一个之后做一个查询而不是增加使用假两阶段提交的额外开销。
通常是 ,假设如果其中一个删除查询成功,可以写入mongodb,但是如果他们没有将“级联”的父行“标记”为已删除或某事并且有一个专门的cronjob,它会在稍后回来并以一致的方式清理它(因为那样做然后会延迟客户端)。
对于哪种架构设计最好,嵌入是首选是不正确的。我注意到你说:
然后我想更新城市中的人员数组
最需要的是$pull
或类似的东西在该阵列上使用。我应该注意,如果该数组大大增加,$pull
的内存中操作将比查询两个单独的集合稍慢。
在一天结束时,我们无法真正建议你的架构设计因为我们不够了解所以我会把它留在那里。
虽然其他两个答案都有所帮助。如果您将city_id
嵌入到人员文档中,您实际上可以在一次调用中级联关系。当然这是一个奇怪的一个,通常你可能有太多的儿童记录,以适应Mongo文件,但这种情况适合。
答案 2 :(得分:0)
您的架构设计应与您的数据访问模式相匹配。因此,最好在人员集合中包含“city:NYC”键:值对,而不是在Cities集合中包含personID,因为如果您拥有纽约市每个人的ID,您可能会超过16mb的文档大小限制在Mongodb文档上,即因为数组将由数百万个元素组成。
这样可以更容易地更新一个人的城市(因为它不常发生),而不是更新一直在变化的城市中的人数。管理多个更新的最安全方法可能是通过您的应用程序代码。