我的团队正在使用GAE(Java)编写应用程序,这使我对诸如BigTable之类的面向对象的数据库中的实体关系建模(尤其是多对多)的可伸缩性提出了质疑。
在App Engine数据存储区中用于建模无主一对多和多对多关系的首选解决方案(请参见Entity Relationships in JDO)似乎是密钥列表。但是,Google警告:
“实施多对多服务有一些限制
这种关系。首先,您必须显式检索值
因为您所有,所以在列表存储位置的那一侧
有可用的关键对象。另一个更重要的是你
希望避免存储过多的键列表...”
说到密钥列表过大,如果您尝试以这种方式建模并假设您为每个密钥存储一个Long,那么每个实体的最大限制为1MB,则每个实体的理论最大关联数约为130k。对于一个平台的主要优势是可扩展性,实际上并没有那么多关系。因此,现在我们正在研究可能需要超过13万个关系的分片实体。
Modeling Entity Relationships文章中概述了另一种方法(关系模型),这是在AppEngine开发人员资源中掌握数据存储系列的一部分。但是,即使在这里Google也警告有关关系模型的性能:
“但是,您需要非常小心,因为遍历
集合的连接将需要对数据存储的更多调用。
仅在您确实需要时才使用这种多对多关系
做到这一点,并注意应用程序的性能。”
因此,现在您在问:“为什么每个实体需要超过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
}
现在,我的查询将获取允许ID查看的所有内容,如下所示:
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过滤器,则无法执行此操作。我无法想到在单个查询中执行此操作的任何方法-这意味着您无法在不进行大量读取时间处理和管理多个游标的情况下对它进行分页。
因此,这是我可以想到的用于执行此操作的工具:反规范化,分片或关系实体。但是,即使有了这些概念,我也看不到如何以可扩展的方式对数据建模。显然有可能。 Google和其他人一直在这样做。我只是看不出来。任何人都无法阐明如何进行建模,或者将我引向对基于NoSQL DB的cms样式的访问控制有任何好的资源吗?
最佳答案
存储ID列表作为属性不会缩放。
为什么不为每个新关系简单地存储一个新对象? (如sql)。
该对象将为您的cms存储两个属性:共享项的ID和用户ID。如果与1000个用户共享,那么您将有1000个。为给定的用户查询它很简单。列出给定项目的权限或用户与其共享的内容的列表也很容易。