如何处理数据库中的大量记录以供用户授权?

我正在使用Ruby on Rails 3.2.2和MySQL。 我想知道是否“建议”/“希望”存储在与类相关的数据库表中与其实例的每个“ 组合 ”的两个其他类相关的所有记录。

也就是说,我有UserArticle模型。 为了存储所有用户文章授权对象,我想实现一个ArticleUserAuthorization模型,以便给定N个用户和M篇文章有N * M ArticleUserAuthorization记录。

这样,我可以声明并使用ActiveRecord::Associations如下:

 class Article  'ArticleUserAuthorization' has_many :users, :through => :user_authorizations end class User  'ArticleUserAuthorization' has_many :articles, :through => :article_authorizations end 

但是,上面存储所有组合的方法将导致一个包含数十亿数十亿行的大型数据库表! 此外,理想情况下,我计划在创建UserArticle对象时创建所有授权记录(也就是说,我计划一次创建所有先前提到的“ 组合 ”,或者更好地,以“延迟”批次创建。无论如何,这个过程会创建其他数十亿的数据库表行!!!)并在销毁时做出反之亦然(通过删除数十亿数据库表行!!!)。 此外,我计划在更新UserArticle对象时立即读取和更新这些行。

所以,我的怀疑是:

  • 这种方法“可取”/“可取”吗? 例如,可能会出现什么样的性能问题? 或者,管理/管理具有非常大的数据库表的数据库是一种糟糕的“方式”/“处方”?
  • 我可能/可能/应该如何处理(也许,通过“重新思考”如何以更好的方式处理用户授权)?

注意 :我会使用这种方法,因为,为了在检索UserArticle对象时检索“授权对象”,我认为我需要“primefaces”用户授权规则(即每个用户和文章对象的一个​​用户授权记录)因为系统不是基于“admin”,“registered”等用户组。 因此,我认为ArticleUserAuthorization表的可用性避免了运行与用户授权相关的方法(注意:这些方法涉及一些可能会恶化性能的MySQL查询 – 请参阅我之前针对每个检索的“授权”方法实现的问题)对象通过“简单地”访问/加入ArticleUserAuthorization表,以便仅检索“用户授权”对象。

事实上,如果您需要每个用户的文章级权限,那么您需要一种方法将User与他们可以访问的Article相关联。 这需要最低限度你需要N * A(其中A是唯一许可文章的数量)。

正如你所建议的那样,3NF的方法是设置一个UsersArticles …这将是一个非常大的表(正如你所注意到的)。

考虑一下这个表会被访问很多……在我看来,这似乎是一种稍微非规范化的方法(甚至是noSQL)更合适的情况之一。

考虑Twitter用于其用户关注者表的模型:

杰夫阿特伍德关于这个问题

和高可扩展性博客

这些文章中的一个示例是在Twitter上学到的一个教训,即从规范化表格中查询关注者会给Users表带来巨大压力。 他们的解决方案是对关注者进行非规范化,以便将用户的关注者存储在他们各自的用户设置中。

非规范化很多。 一手救了他们。 例如,它们将所有用户ID朋友ID存储在一起,这阻止了大量昂贵的连接。 – 避免复杂的连接。 – 避免扫描大量数据。

我想可以使用类似的方法来提供文章权限,并避免使用极其紧张的UsersArticles单表。

您不必重新发明轮子。 ACL(访问控制列表)框架现在处理相同类型的问题,如果你问我,效率最高。 您有资源(文章)或更好的资源组(文章类别/标签/等)。另一方面,您有用户(用户) 用户组。 然后你会有一个相对较小的表,它将资源组映射到用户组。 并且您将拥有另一个相对较小的表,该表保存此常规映射的exception。 或者,您可以使用规则集来满足访问文章的要求。您甚至可以拥有动态组,例如:authors_friends,具体取决于您的用户 – 用户关系。

只需看看任何体面的ACL框架,您就会知道如何处理这类问题。

如果确实存在“包含数十亿数十亿行的大型数据库表”的前景,那么您应该根据(相对)人口稀少的表格为您的特定需求制定解决方案。

大型数据库表对系统定位相关行或多行的速度提出了重大的性能挑战。 这里真的需要索引和主键; 但是,它们会增加存储要求,并且还需要在添加,更新和删除记录时维护CPU周期。 Evenso,重型数据库系统还具有分区function(请参阅http://en.wikipedia.org/wiki/Partition_(database )),以解决此类行位置性能问题。

如果没有返回任何行,则可以使用稀疏填充的表来实现此目的,假设可以使用某些(可计算或常量)缺省值。 仅在需要非默认值的位置插入行。 人口稀少的表将需要更少的存储空间,系统将能够更快地定位行。 (使用用户定义的函数或视图可能有助于简化查询。)

如果你真的不能让一个人烟稀少的桌子为你工作,那么你就会陷入困境。 也许您可以将这个庞大的表格变成一个较小的表集合,但是如果您的数据库系统支持分区,我怀疑这有什么帮助。 此外,一组较小的表格使得查询更加混乱。

因此,假设您拥有数百万或数十亿的用户,他们对您系统中的数百万或数十亿篇文章拥有或未拥有某些特权。 那么,在业务级别确定用户有权使用给定文章做什么? 用户必须是(付费)订户吗? 或者他或她可能是客人? 用户是否申请(并支付)某些物品的包裹? 用户是否可以获得编辑某些文章的权限? 等等等等。

因此,假设某个用户想要对某篇文章做某事。 对于稀疏填充的表,该扩展表UsersArticles上的SELECT将返回1行或不返回。 如果它返回一行,则会立即知道ArticleUserAuthorization,并可继续执行其余操作。

如果没有行,那么可能只是说用户不能对这篇文章做任何事情。 或者,User可能是某个UserGroup的成员,该UserGroup有权获得具有某些ArticleAttribute的任何Article的特定权限(本文已经或没有)。 或者,对于在UsersArticles中已经没有此类记录的任何用户,该文章可能具有默认的ArticleUserAuthorization(存储在其他一些表中)。 管他呢…

关键是许多情况都有结构和规律性,可用于帮助减少系统所需的资源。 例如,人类可以添加两个数字,每个数字最多6个数字,而无需查阅超过半万亿条目的表格; 这是利用结构。 至于规律性,大多数人都听说过帕累托原则(“80-20”规则 – 见http://en.wikipedia.org/wiki/Pareto_principle )。 你真的需要“数十亿数十亿行”吗? 或者更确切地说,大约80%的用户将只拥有数百或数千篇文章的(特殊)特权 – 在这种情况下,为什么要浪费其他“数十亿数十亿”(四舍五入:-P) 。

您应该查看基于层次角色的访问控制(RBAC)解决方案。 您还应该考虑合理的默认值。

  • 是否允许所有用户默认阅读文章? 然后存储deny例外。

  • 默认情况下,是否所有用户都不允许阅读文章? 然后存储allow例外。

  • 是否依赖于文章默认是allow还是deny ? 然后将其存储在文章中,并存储allowdenyexception。

  • 是否将文章置于问题中,并将收集到期刊和期刊中的问题收集到知识领域? 然后在users和这些对象之间存储授权。

  • 如果允许User阅读Journal但被拒绝特定Article怎么办? 然后存储User-Journal:allowUser-Article:deny和最具体的指令(在本例中为文章)优先于更一般的 (在这种情况下是默认值和日志)。

通过user_id对ArticleUserAuthorization表进行分片。 原则是减少访问路径上的有效数据集大小。 某些数据将比其他数据更频繁地访问,也可以以特定方式访问。 在该路径上,结果集的大小应该很小。 在这里,我们通过一个碎片来做到这一点。 此外,如果索引是读取工作负载,则可以通过索引来优化该路径,对其进行缓存等

如果您想要用户授权的所有文章,则此特定分片很有用。
如果您还想按文章查询,那么也可以通过article_id复制表和分片。 当我们有第二个分片方案时,我们对数据进行了非规范化。 现在数据被复制,应用程序需要做额外的工作来维护数据一致性。 写入也会更慢,使用队列进行写入

分片问题是跨分片的查询无效,您需要一个单独的报告数据库。 选择一个分片方案并考虑重新计算分片。

对于真正庞大的数据库,您可能希望在物理机器上拆分它。 例如。 每个用户的文章一台或多台机器。

一些nosql建议是:

  1. 关系是图表。 所以看看图形数据库。 尤其
    https://github.com/twitter/flockdb
  2. redis,通过将关系存储在列表中。
  3. 面向列的数据库,如hbase。 可以像稀疏的嵌套哈希一样对待它

所有这些取决于数据库的大小和查询类型

编辑:修改后的答案。 以前有’had_one’关系的问题还添加了nosql建议1和2

首先,考虑默认值和行为并不将它们存储在数据库中是很好的。 例如,如果默认情况下,除非指定了用户,否则用户无法读取文章,因此,不必将其作为false存储在数据库中。

我的第二个想法是,您可以在articles表中使用users_authorizations列,在users表中使用articles_authorizations 。 这两列将以3,7,65,78,29,78的forms存储用户ID和文章ID。 例如,对于articles表,这将意味着具有ID 3,7,65,78,29,78用户可以访问这些文章。 然后,您必须修改您的查询以便以这种方式检索用户:

 @article = Article.find(34) @users = User.find(@article.user_authorizations.split(',')) 

每次保存或销毁文章和用户时,都必须创建回调以更新授权列。

 class User < ActiveRecord after_save :update_articles_authorizations def update_articles_authorizations #... end end 

Article模型做同样的事情。

最后一件事:如果您有不同类型的授权,请不要犹豫,创建更多像user_edit_authorization这样的列。

使用这些组合技术,数据量和对数据库的命中率是最小的。

阅读所有评论和问题,我仍然怀疑存储所有组合的有效性。 以另一种方式思考这个问题 – 谁会填充那张桌子? 文章的作者或主持人,还是其他人? 并根据什么规则? 你想象一下这有多难。 填充所有组合是不可能的。

Facebook也有类似的function。 撰写post时,您可以选择要与谁分享。 您可以选择“朋友”,“朋友之友”,“所有人”或自定义列表。 自定义列表允许您定义将包含和排除的人员。 与此相同,您只需要存储特殊情况,例如“包含”和“排除”,所有剩余的组合都属于默认情况。 通过这个,N * M可以显着减少。 发布可见性