7

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

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

这样做,我可以声明和使用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

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

所以,我的疑问是:

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

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

4

7 回答 7

6

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

正如您所建议的那样,3NF 方法将是一个UsersArticles集合......这将是一个非常大的表(如您所述)。

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

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

杰夫阿特伍德谈这个话题

和高可扩展性博客

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

非规范化很多。单枪匹马救了他们。例如,他们将一个用户 ID 的所有朋友 ID 存储在一起,从而避免了很多代价高昂的连接。- 避免复杂的连接。- 避免扫描大量数据。

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

于 2012-06-22T15:29:14.380 回答
5

您不必重新发明轮子。ACL(访问控制列表)框架处理相同类型的问题已经很久了,如果你问我,效率最高。您有资源(文章)甚至更好的资源组(文章类别/标签/等)。另一方面,您有用户(用户)用户组。然后,您将拥有一个相对较小的表,将资源组映射到用户组。您将拥有另一个相对较小的表,其中包含此一般映射的例外情况。或者,您可以有规则集来满足访问文章。您甚至可以有动态组,例如:authors_friends,具体取决于您的用户-用户关系。

只要看看任何像样的 ACL 框架,您就会知道如何处理此类问题。

于 2012-06-25T19:01:49.113 回答
4

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

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

假设只要没有返回任何行,就可以使用一些(可计算的或恒定的)默认值,一个稀疏填充的表可能可以达到目的。仅在需要非默认值的地方插入行。一个稀疏填充的表将需要更少的存储空间,并且系统将能够更快地定位行。(使用用户定义的函数或视图可能有助于保持查询的直接性。)

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

因此,假设您有数百万或数十亿用户,他们或可能没有对您系统中数百万或数十亿文章的某些特权。那么,在业务层面,什么决定了用户有权对给定的文章做什么?用户必须是(付费)订阅者吗?或者他或她可能是客人?用户是否申请(并支付)某些文章的包裹?是否可以授予用户编辑某些文章的特权?等等等等。

因此,假设某个用户想要对某个文章做某事。对于人口稀少的表,SELECT该大表 UsersArticles 上的 a 将返回 1 行或无。如果它返回一行,则立即知道 ArticleUserAuthorization,并可以继续进行其余操作。

如果没有行,那么说用户不能对这篇文章做任何事情就足够了。或者,用户可能是某个用户组的成员,该用户组有权对任何具有某些 ArticleAttribute(本文具有或不具有)的文章具有某些特权。或者,对于在 UsersArticles 中没有此类记录的任何用户,文章可能具有默认的 ArticleUserAuthorization(存储在其他表中)。管他呢...

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

于 2012-06-22T21:24:23.150 回答
1

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

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

  • 默认情况下不允许所有用户阅读文章吗?然后存储allow异常。

  • allow默认是还是取决于文章deny?然后将其存储在文章中,并存储两者allowdeny异常。

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

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

于 2012-06-28T12:49:02.380 回答
0

通读所有评论和问题,我仍然怀疑存储所有组合的有效性。以另一种方式思考这个问题 - 谁将填充该表?文章的作者或版主,还是其他人?并基于什么规则?你可以想象那是多么困难。不可能填充所有组合。

Facebook 也有类似的功能。撰写帖子时,您可以选择要与谁分享。您可以选择“朋友”、“朋友的朋友”、“所有人”或自定义列表。自定义列表允许您定义将包括和排除谁。所以同样的,你只需要存储特殊情况,比如'include'和'exclude',剩下的所有组合都属于默认情况。通过 dong 这个,N*M 可以显着减少。发布可见性

于 2012-06-28T06:21:01.080 回答
0

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

我的第二个想法是你可以在你的表中有一个users_authorizations列,在你的表articles中有一个列。这 2 列将在表单中存储用户 ID 和文章 ID 。例如,对于表格,这意味着具有 id的用户可以访问文章。然后,您将不得不修改查询以通过这种方式检索用户:articles_authorizationsusers3,7,65,78,29,78articles3,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.

使用这些组合技术,对数据库的数据量和命中率将降至最低。

于 2012-06-25T15:54:44.187 回答
0

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

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

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

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

一些 nosql 建议是:

  1. 关系是图表。所以看看图形数据库。特别是
    https://github.com/twitter/flockdb
  2. redis,通过将关系存储在列表中。
  3. 面向列的数据库,例如 hbase。可以将其视为稀疏嵌套哈希

这一切都取决于数据库的大小和查询的类型

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

于 2012-06-23T17:26:11.437 回答