我的团队正在使用 GAE (Java) 编写一个应用程序,这让我质疑 BigTable 等面向对象数据库中实体关系建模(特别是多对多)的可扩展性。
在 App Engine 数据存储区中建模无主的一对多和多对多关系的首选解决方案(请参阅JDO 中的实体关系)似乎是键列表。然而,谷歌警告:
“以这种方式实现多对多关系有一些限制。首先,您必须显式检索存储列表的集合一侧的值,因为所有可用的都是 Key 对象。另一个更重要的是你想避免存储过大的键列表......”
说到过大的键列表,如果您尝试以这种方式建模并假设您为每个键存储一个 Long ,那么每个实体的限制为 1MB,每个实体的理论最大关系数约为 130k。对于一个主要优势是可扩展性的平台来说,实际上并没有那么多关系。所以现在我们正在研究可能需要超过 130k 关系的分片实体。
作为 AppEngine 开发人员资源中掌握数据存储系列的一部分,建模实体关系一文中概述了另一种方法(关系模型) 。然而,即使在这里谷歌警告关系模型的性能:
“但是,您需要非常小心,因为遍历集合的连接将需要对数据存储进行更多调用。仅在您真正需要时才使用这种多对多关系,并注意性能你的申请。”
所以现在你在问:“为什么每个实体需要超过 13 万个关系?” 好吧,我很高兴你问。让我们以一个拥有 100 万用户的 CMS 应用程序为例(嘿,我能做梦吗?!)
用户可以上传内容并分享给: 1. 公众 2. 个人 3. 群组 4. 任意组合
现在有人登录并导航到仪表板,该仪表板显示来自他们在任何组中连接的人的新上传。此仪表板应包括公共内容,以及专门与此用户或此用户所属的组共享的内容。还不错吧?让我们深入研究一下。
public class Content {
private Long id;
private Long authorId;
private List<Long> sharedWith; //can be individual ids or group ids
}
现在,我获取允许查看的所有内容的查询可能如下所示:
List<Long> idsThatGiveMeAccess = new ArrayList<Long>();
idsThatGiveMeAccess.add(myId);
idsThatGiveMeAccess.add(publicId); //Let's say that sharing with 0L makes it public
for (Group g : groupsImIn)
idsThatGiveMeAccess.add(g.getId());
List<Long> authorIdsThatIWantToSee = new ArrayList<Long>();
//Add a bunch of authorIds
Query q = new Query("Content")
.addFilter("authorId", Query.FilterOperator.IN, authorIdsThatIWantToSee)
.addFilter("sharedWith", Query.FilterOperator.IN, idsThatGiveMeAccess);
显然我已经违反了几条规则。也就是说,使用两个 IN 过滤器会炸毁。即使是任何尺寸接近我们正在谈论的极限的单个 IN 过滤器也会爆炸。除此之外,假设我想限制和翻阅结果......不,不!如果您使用 IN 过滤器,则不能这样做。我想不出任何方法可以在单个查询中执行此操作 - 这意味着如果没有大量的读取时间处理和管理多个游标,您将无法对其进行分页。
所以这里是我能想到的工具:非规范化、分片或关系实体。然而,即使有了这些概念,我也看不出如何以可扩展的方式对这些数据进行建模。显然这是可能的。谷歌和其他公司一直在这样做。我只是看不出怎么做。任何人都可以阐明如何对此建模或向我指出任何基于 NoSQL DB 的 cms 式访问控制的良好资源吗?