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

时间:2012-06-20 06:50:27

标签: mysql sql ruby-on-rails ruby ruby-on-rails-3

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

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

这样做,我可以陈述并使用ActiveRecord::Associations如下:

class Article < ActiveRecord::Base
  has_many :user_authorizations, :class_name => 'ArticleUserAuthorization'
  has_many :users, :through => :user_authorizations
end

class User < ActiveRecord::Base
  has_many :article_authorizations, :class_name => 'ArticleUserAuthorization'
  has_many :articles, :through => :article_authorizations
end

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

所以,我的怀疑是:

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

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

7 个答案:

答案 0 :(得分:6)

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

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

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

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

Jeff Atwood on the subject

And High Scalability Blog

来自这些作品的样本是在Twitter上学到的经验教训,即从规范化表格中查询关注者会给Users表带来巨大压力。他们的解决方案是对关注者进行非规范化处理,以便用户的关注者存储在他们各自的用户设置中。

  

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

我想可以使用类似的方法来提供文章权限,避免使用压力过大的UsersArticles单个表格。

答案 1 :(得分:5)

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

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

答案 2 :(得分:4)

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

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

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

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

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

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

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

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

答案 3 :(得分:1)

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

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

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

  • 是否依赖于文章默认为allow还是deny?然后将其存储在文章中,并存储allowdeny例外情况。

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

  • 如果允许User读取Journal但拒绝具体Article,该怎么办?然后存储User-Journal:allowUser-Article:deny和最特定的指令(在本例中为文章)优先于更多通用(在这种情况下,默认,和日记)。

答案 4 :(得分:0)

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

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

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

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

一些nosql建议是:

  1. 关系是图表。所以看一下图数据库。特别
    https://github.com/twitter/flockdb
  2. redis,将关系存储在列表中。
  3. 面向列的数据库,如hbase。可以像稀疏的嵌套哈希一样对待它
  4. 所有这些取决于数据库的大小和查询类型

    编辑:修改后的答案。以前的问题'had_one'关系还添加了nosql建议1&amp; 2

答案 5 :(得分:0)

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

我的第二个想法是,您的users_authorizations表格中可以有articles列,articles_authorizations表格中有users列。这两列将以3,7,65,78,29,78的形式存储用户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

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

答案 6 :(得分:0)

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

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