没有JOIN的生活...理解和常见做法

60
许多大型网站使用依赖于具有索引的巨大表格和使用查询的数据存储和检索技术,这些查询不能使用JOINs(BigTable、HQL等)来处理可扩展性和分片数据库。当你有大量非常相关的数据时,这种方法如何工作?
我只能猜测很多连接必须在应用程序端完成,但这不会变得昂贵吗?如果您需要查询几个不同的表以获取信息进行编译,怎么办?难道多次访问数据库开始变得比一开始就使用连接更加昂贵了吗?我猜这取决于你拥有多少数据?
对于常见的ORM,它们通常如何处理无法使用连接的情况?今天广泛使用的ORM是否支持此功能?还是大多数需要处理此级别数据的项目都自己开发?
因此,这与我正在做的任何当前项目无关,但这是我几个月来一直在思考的问题,我只能推测“最佳实践”是什么。我从未需要在我的任何项目中解决这个问题,因为它们从未达到需要的规模。希望这个问题也能帮助其他人。
正如下面有人所说,没有连接,ORM就“无法工作”。是否有其他数据访问层已经可供处理此级别数据的开发人员使用?
编辑:为了澄清,Vinko Vrsalovic说:

“我相信snicker想谈论的是NO-SQL,这里事务性数据被去规范化并在Hadoop或BigTable或Cassandra方案中使用。”

确实是我所说的。

对于那些能够捕捉到xkcd参考的人会有额外的加分。


12
你使用“大胆的网站”一词让我想起了http://xkcd.com/37/. - rmeador
我很高兴有人捕捉到了xkcd的参考 =] - snicker
7个回答

36
在我看来,关系型数据库是一种基本工具,用来规避风险。现代计算机和RDBMS都经过了充分的优化,因此您可以在单个服务器上容纳相当大的数据量。通过选择 RDBMS,您可以非常灵活地访问数据,并且具备强大的正确性约束条件,使得编码数据变得更加容易。然而,RDBMS并不会为任何特定问题提供良好的优化方案,它只是让您轻松地更改问题。
如果您开始快速增长并意识到需要扩展到超出单个数据库服务器的规模,那么您就需要做出更艰难的选择。您需要开始识别瓶颈并消除它们。 RDBMS将成为一个非常复杂的相互依存的代码集合,您需要逐步分解它。数据之间的相互连接越多,您所需进行的工作就越多,但也许您不必完全解开这一切。如果您主要进行阅读操作,也许可以通过简单的复制来应对。如果您的市场饱和并且增长正在趋于平稳,也许可以部分去规范化并分片到固定数量的数据库服务器。也许您只有一些问题表需要移动到更可扩展的数据存储中。如果您的使用情况非常适合缓存,则可以将负载迁移到巨大的 memcached 集群中。
可扩展的键值存储(如 BigTable)的用处在于当上述解决方案均无法解决问题时,您拥有大量单一类型的数据,即使去规范化,单个表也太大了无法放在一个服务器上。此时,您需要能够任意分区,并仍然具有干净的API来访问它。当数据分布在如此多的机器上时,您无法要求这些机器之间进行大量通信,这是许多标准关系型算法所必需的。正如您所建议的那样,这些分布式查询算法可能需要比适当索引的关系数据库中的等效连接更多的处理能力,但由于它们是并行的,因此实时性能比任何单个机器都快数个数量级(假设存在可以容纳整个索引的机器)。

现在,一旦您可以通过只需插入更多服务器来横向扩展您的大规模数据集,可扩展性的难题就解决了。好吧,我不应该说解决了,因为这个规模的持续运营和开发比单服务器应用程序要困难得多,但是关键是应用服务器通常可以通过"share-nothing"架构轻松扩展,只要它们能够及时获取所需的数据。

回答您有关常用ORM如何处理无法使用JOINs的问题,简短的答案是他们不会。 ORM代表对象关系映射,大部分工作都是将强大的谓词逻辑关系范式转换为简单的面向对象数据结构。它们提供的大部分价值都无法从键值存储中实现。实际上,在这种情况下,您可能需要构建并维护自己的数据访问层,以适应您特定的需求,因为这些规模的数据配置文件将会有很大的变化,并且我认为存在太多的权衡,以至于通用工具无法出现并像RDBMS一样占主导地位。总之,在这个规模下,你总是需要做更多的工作。

话虽如此,肯定会有越来越多的基于键值存储原语构建关系型或其他聚合功能的可能性。我在这方面没有足够的经验进行具体评论,但企业计算领域有很多研究知识(例如Oracle),学术界还有大量未开发的理论知识,Google、亚马逊、Facebook等公司有大量实践经验,但过滤到更广泛开发社区中的知识仍然相对有限。

但是,现在许多应用程序正在转向Web,并且越来越多的全球人口正在上网,不可避免地越来越多的应用程序需要扩展,并且最佳实践将开始形成。云服务(如AppEngine和EC2)以及Cassandra等开源数据库将缩小双方的知识差距。在某种程度上,这与并行和异步计算一起处于起步阶段。肯定是作为程序员非常有趣的时期。


谢谢。非常有用的信息。 - snicker
以下是关于谷歌实际分布式系统的一些有趣的技术信息:http://perspectives.mvdirona.com/2009/10/17/JeffDeanDesignLessonsAndAdviceFromBuildingLargeScaleDistributedSystems.aspx - gtd
刚刚偶然看到了这个答案。我一直认为关系型数据库是一个很好的起点。避免过早优化等等。然而,如果你考虑谷歌应用引擎,他们使用面向对象的数据模型(虽然不是严格的ORM),最终映射成键值存储。 - JasonSmith

21

你的起点假设是错误的。

数据仓库并不像事务应用程序那样对数据进行规范化。它们没有很多联接,相对较少。

尤其是第二和第三范式违规并不是一个“问题”,因为数据仓库很少更新。当它们被更新时,通常只是状态标志更改以将维度行标记为“当前”或“非当前”。

由于您不必担心更新,因此您无需将事物分解到2NF级别,这样更新就不会导致异常关系。没有更新意味着没有异常;没有分解和没有联接。你可以预先联接所有内容。

通常,DW数据根据星型模式进行分解。这指导您将数据分解为包含度量-带单位的数字-和对维度的外键引用的数值“事实”表。

最好将维度(或“业务实体”)视为具有属性的真实世界事物。通常,这包括地理、时间、产品、客户等。这些事物通常具有复杂的层次结构。这些层次结构通常是任意的,根据各种商业报告需求定义,并且不作为单独的表进行建模,而只是在聚合所使用的维度列中简单地定义为列。


回答一些您的问题。

“这种联接必须在应用程序端完成。” 有点像。数据在加载之前被“预先联接”。维度数据通常是与该维度相关的源数据的联接。它被联接并作为一个相对平坦的结构加载。

它没有更新。相反,插入了额外的历史记录。

“但这开始变得昂贵了吗?” 有点像。需要一些谨慎来加载数据。然而,没有很多报告/分析联接。数据是预先联接的。

由于数据是预连接的,ORM问题在很大程度上已经不再重要。您的ORM将适当地映射到事实或维度。除非是特殊情况,否则维度往往较小并且完全适合内存。例外情况是当您处于金融(银行或保险)或公用事业部门并且拥有庞大的客户数据库时。这些客户维度很少适合内存。


我想我的意思并不是严格的“仓储”意义上的,而是指一个将积极读写数据的应用程序(如80/20 R/W)。在星型模式中,通常有很多连接...那么我的错误假设是什么? - snicker
@snicker:你的错误假设是“很多连接”。数据仓库可以通过少量连接完成。在许多情况下,应用程序级别的连接成本为零,因为表是简单的内存映射。 - S.Lott
2
在我看来,鉴于问题的编辑,这现在已经不是一个答案了(顺便说一句,这是一个非常好的非答案)。我相信 snicker 想要谈论的是 NO-SQL(blog.oskarsson.nu/2009/06/…),在这里事务性数据被去规范化并用于 Hadoop 或 BigTable 或 Cassandra 方案中。现在问题中已经没有提到数据仓库了。 - Vinko Vrsalovic
Vinko...谢谢您的澄清...那正是我一开始想要的.. - snicker
@S.Lott:你知道吗,我真的无法弄清楚我反对的基础是什么,你的帖子看起来完全没问题。我能想到的只有我可能把我的评论发错了答案。我真诚地道歉。 :-( - RBarryYoung
显示剩余7条评论

14

JOIN是一个纯关系术语,而并非所有数据库都是关系型的。

其他数据库模型有其他建立关系的方式。

网络数据库使用无尽的 查找键-获取引用-查找键 链式操作,应该使用通用编程语言来编程。

代码可以在应用程序端或服务器端运行,但它不是 SQL 甚至不是基于集合的。

如果设计得当,网络数据库比关系型数据库更快。

例如,网络数据库可以将对另一个实体的引用存储为指向文件中偏移量或甚至磁盘上块的直接指针,其中存储有关此实体的信息。

这使得遍历网络变得非常快速——如果您编写了有效的代码。

关系型数据库只能将引用存储为基本值对,如整数(或更高阶的三元组或元组)。

要在关系型数据库中查找这些值,引擎应执行以下操作:

  • 查找包含第一个值的元组位于何处
  • 找到第二个值
  • 查找保存第二个数字所指数据的 B-Tree 根地址
  • 遍历这棵树
  • 查找指向实际表的指针(它本身可以作为一个 B-Tree 存储,在这种情况下,我们要找的行的PRIMARY KEY是指针的值)
  • 通过指针找到表的行或遍历表
  • 最后,获得结果。

您只能在一定程度上控制这个过程。之后,您只需发出 SQL 查询并等待即可。

关系模型旨在简化开发人员的工作,而不是总是以任何方式都达到超级速度。

这就像汇编语言与高级语言一样,关系模型是一种高级语言。

您可能想阅读我博客中的文章。

  • 什么是关系数据库?
  • 在这篇文章中,我将尝试解释几种常用数据库模型之间的区别。


    4
    当您以这种方式对数据进行反规范化时,您这样做是为了避免连接不同项的成本;您接受一些数据可能会重复,并且某些组合方式可能会很困难,但可以使用简单的查询来获得性能上的好处。
    如果您在应用程序级别必须执行任何更大量的连接,这意味着您没有对其进行足够的反规范化。
    理想情况下,您将能够为所需的任何数据集进行一次查询。实际上,您不应该为应用程序的任何方面使用超过两到三个查询,并且任何应用程序级别的连接都将更多地是从单独的结果集中检索内容,以便插入视图。
    这种类型的事情只在真正大规模的数据集中才真正需要,而且涉及各种权衡。举一个例子:BigTable无法执行聚合查询,例如给出计数。它可用于提供大致准确的数字 - 就像如果您有12,149,173条记录,其中在最后一个小时添加了23,721条记录,那么找出“约有12,100,000条记录”就足够了。如果您的应用程序取决于随时知道精确数字,则不应该使用BigTable,这是一般的态度。

    3

    像Facebook这样的应用程序很少更改数据,大多数情况下用户都在发布新内容。因此,当更改项目时需要更新多个记录的事实是一个较小的问题。

    这使得数据不必规范化也能避免常见的更新问题。

    像亚马逊这样的应用程序可以负担得起将单个用户的所有数据加载到RAM中(购物车有多大?),然后在RAM中更新数据并将其写出为单个数据项。

    再次消除了大部分数据规范化的需求。

    你正在用易于应用程序开发的方式来交换可扩展性,因此,如果你不需要扩展到极限,你可能希望保留关系数据库管理系统提供的应用程序开发便利性。


    0

    我认为在这些情况下,你将会非常独立,需要自己处理所有事情。虽然我没有亲身经历过,但考虑过在我们的一些项目中使用。关系型数据库可以变得非常庞大(正如SO所展示的),因此我现在仍将享受关系型数据库的好处。


    0
    通常来讲,数据仓库是基于联接和将数据分割到维表和事实表进行构建的(使用所谓的“星型模式”等)。
    联接经常被预先计算并存储为非规范化表格。
    我不知道有任何 ORM 工具可以与不允许联接的数据库系统一起使用,因为这些数据库通常不被视为传统的关系数据库。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接