为啥 Spark SQL 认为索引的支持不重要?
Posted
技术标签:
【中文标题】为啥 Spark SQL 认为索引的支持不重要?【英文标题】:Why Spark SQL considers the support of indexes unimportant?为什么 Spark SQL 认为索引的支持不重要? 【发布时间】:2016-08-24 15:39:45 【问题描述】:引用 Spark DataFrames,Datasets and SQL manual:
少数 Hive 优化尚未包含在 Spark 中。一些 由于 Spark SQL 的原因,这些(例如索引)不太重要 内存计算模型。其他的被安排在未来的版本中 Spark SQL。
作为 Spark 的新手,我对此感到有些困惑,原因有两个:
Spark SQL 旨在处理大数据,至少在我的使用中 如果数据大小远远超过可用内存的大小。 假设这种情况并不少见,那么“Spark SQL 的 内存计算模型”?Spark SQL 是否仅推荐用于 数据适合内存的情况?
即使假设数据适合内存,对一个非常大的 数据集可能需要很长时间。我读了this argument反对 在内存数据库中建立索引,但我不相信。这个例子 那里讨论了对 10,000,000 条记录表的扫描,但这不是 真正的大数据。扫描具有数十亿条记录的表可能会导致 “SELECT x WHERE y=z”类型的简单查询需要永远代替 立即返回。
我了解索引有一些缺点,例如较慢的 INSERT/UPDATE、空间要求等。但在我的用例中,我首先将大量数据处理并加载到 Spark SQL 中,然后将这些数据作为一个整体进行探索,没有进一步的修改。 Spark SQL 对于数据的初始分布式处理和加载很有用,但缺乏索引使得交互式探索比我预期的更慢和更麻烦。
我想知道为什么 Spark SQL 团队认为索引在某种程度上不重要,以至于超出了他们的路线图。是否有不同的使用模式可以提供索引的好处,而无需独立实现等效的东西?
【问题讨论】:
Spark
不是数据库。那么index
是什么意思?您可以在 Spark 中编写一个索引原始文本文件的进程。您可以获取原始文本文件并将其保存为Parquet
文件,并根据需要对数据进行分区。您还希望它为您做什么?
@DavidGriffin 引用的部分是关于 Hive 优化的。 Spark SQL 将在 Hive 表上执行。所以 SparkSQL 不支持索引提供给 Hive 的优化。就是这个意思,并不是说 Spark 本身就是一个数据库
@DavidGriffin Spark SQL 提供了一个 SQL 抽象。不考虑实现的考虑和存储机制,我认为希望 Spark SQL 提供与 sql CREATE INDEX 语句等效的功能而不需要我自己实现它是合理的。
为什么这样合理?您可以在 Spark 中创建一个 Hive 表,然后转到 Hive 并在该表上创建一个索引吗?如果是这样,为什么这需要成为 Spark 的责任?我无法在 Spark 中创建 Kafka 主题或为 Kafka 主题添加/删除分区。我期望能够做到这一点是不合理的。
@David Griffin,请注意我在回复中没有提到 Hive,我认为这并不相关。我要说的是,索引通常可以节省大量时间,并且对于加快 SQL 查询非常有用。从我引用的段落看来,Spark SQL 团队认为索引对于实施和作为 Spark/BigData 新手来说并不重要,我想了解原因。我正在寻找诸如“由于 Spark 架构而实现起来很困难”、“索引对于典型的 Spark SQL 应用程序来说不够有用”或“索引已被其他更合适的工具覆盖”之类的答案。
【参考方案1】:
索引输入数据
对外部数据源进行索引不在 Spark 范围内的根本原因在于 Spark 不是数据管理系统,而是批处理数据处理引擎。由于它不拥有正在使用的数据,因此无法可靠地监控更改,因此无法维护索引。 如果数据源支持索引,Spark 可以通过谓词下推等机制间接利用它。索引分布式数据结构:
标准索引技术需要持久且定义明确的数据分布,但 Spark 中的数据通常是短暂的,其确切分布是不确定的。 通过适当的分区与列存储和压缩相结合实现的高级数据布局可以提供非常高效的分布式访问,而无需创建、存储和维护索引的开销。这是不同内存中列系统使用的常见模式。李>话虽如此,Spark 生态系统中确实存在某些形式的索引结构。最值得注意的是 Databricks 在其平台上提供Data Skipping Index。
其他项目,如Succinct(今天大部分不活动)采用不同的方法并使用具有随机访问支持的高级压缩技术。
当然,这提出了一个问题——如果您需要有效的随机访问,为什么不使用从一开始就设计为数据库的系统。那里有很多选择,其中至少有一些是由 Apache 基金会维护的。同时 Spark 随着项目的发展,您使用的报价可能无法完全反映 Spark 未来的发展方向。
【讨论】:
@zero323:关于“由于它不拥有它正在使用的数据,它无法可靠地监控更改”。这不是与 Spark 支持分区的事实相矛盾吗? @shridharama 在这种情况下分区是什么意思? @zero323 我指的是spark支持功能,例如repartition()以及spark到discover partitioned parquet data的能力 @shridharama 如果是这样,答案是否定的。repartition
不监视任何内容。它创建了不可变且有效的短暂数据结构,仅限于给定的应用程序。分区发现只是假设结构是有效的。它不会监控数据,也不会在标准 Parquet 功能之外进行验证。
@zero323 分桶意味着数据可能不限于应用程序,不是吗?【参考方案2】:
一般来说,索引的实用性充其量是值得怀疑的。相反,数据分区更为重要。它们是非常不同的东西,仅仅因为您选择的数据库支持索引并不意味着考虑到 Spark 正在尝试做的事情,它们是有意义的。而且和“记忆中”没有任何关系。
到底什么是索引?
在永久存储非常昂贵(而不是基本上免费)的时代,关系数据库系统都是为了尽量减少永久存储的使用。关系模型必然会将记录拆分为多个部分——对数据进行规范化——并将它们存储在不同的位置。要读取客户记录,您可能会读取 customer
表、customerType
表、从 address
表中取出几个条目等。如果您有一个解决方案要求您读取整个表以找到你想要的,这是非常昂贵的,因为你要扫描这么多的表。
但这不是做事的唯一方法。如果您不需要固定宽度的列,则可以将整组数据存储在一个位置。无需对一堆表进行全表扫描,您只需对单个表进行扫描即可。这并不像您想象的那么糟糕,尤其是如果您可以对数据进行分区。
40 年后,物理定律发生了变化。硬盘随机读/写速度和线性读/写速度已经发生了巨大的分歧。基本上每个磁盘每秒可以进行 350 次磁头移动。 (或多或少,但这是一个很好的平均数字。)另一方面,单个磁盘驱动器每秒可以读取大约 100 MB。这是什么意思?
算一算,想一想——这意味着如果每次磁头移动读取的数据少于 300KB,则说明您正在限制驱动器的吞吐量。
说真的。想一想。
索引的目标是允许您将磁盘磁头移动到您想要的磁盘上的精确位置并读取该记录——比如作为customer
记录的一部分加入的address
记录。我说,那没用。
如果我基于现代物理学设计一个索引,它只需要让我在目标数据的 100 KB 左右(假设我的数据已经被大块布局——但我们在谈论理论反正在这里)。根据上面的数字,比这更高的精度只是浪费。
现在回到您的规范化表格设计。假设customer
记录实际上被拆分为 5 个表中的 6 行。总共 6 次磁盘磁头移动(我假设索引缓存在内存中,因此没有磁盘移动)。这意味着我可以读取 1.8 MB 的线性/非规范化客户记录并且同样高效。
那么客户历史呢?假设我不仅想查看客户今天的样子——想象一下我想要完整的历史记录,还是历史记录的子集?将以上所有内容乘以 10 或 20 即可得到图片。
比索引更好的是数据分区——确保所有客户记录最终都在一个分区中。这样,只需移动一次磁盘磁头,我就可以读取整个客户历史记录。一个磁盘磁头移动。
再告诉我你为什么需要索引。
索引与___?
不要误会我的意思——“预先准备”您的搜索是有价值的。但是物理定律提出了一种比传统索引更好的方法。与其将客户记录存储在一个位置,并创建一个指向它的指针(索引),不如将记录存储在多个位置?
请记住,磁盘空间基本上是空闲的。与其尽量减少我们使用的存储量(关系模型的过时产物),不如将您的磁盘用作您的搜索缓存。
如果您认为有人希望查看按地理位置和销售代表列出的客户,则以优化这些搜索的方式存储您的客户记录的多个副本。就像我说的,像使用内存缓存一样使用磁盘。与其通过将不同的持久数据组合在一起来构建内存缓存,不如构建持久数据来镜像内存缓存,这样您所要做的就是读取它。事实上,甚至不必费心尝试将它存储在内存中 - 每次需要时直接从磁盘读取它即可。
如果您认为这听起来很疯狂,请考虑一下——如果您将其缓存在内存中,您可能会缓存两次。您的操作系统/驱动器控制器可能使用主内存作为缓存。不要费心缓存数据,因为其他人已经在缓存了!
但我离题了...
长话短说,Spark 绝对支持正确的索引类型 - 能够从原始数据创建复杂的派生数据,以提高未来的使用效率。它只是没有按照您想要的方式进行。
【讨论】:
@DavidGriffin,我理解你的观点,如果你的查询需要大量的头部移动,你可能会更好地阅读顺序数据。但准确地说,我们应该考虑顺序读取的速度:如果数据的大小足够大,读取它需要更多时间然后磁头移动,我们仍然会从索引中受益,不是吗?例如简单的 SELECT x WHERE y=z 查询将需要很少的头部移动,并且可能在非常大的数据大小(TB)上运行。在这些情况下,顺序读取不会慢几个数量级吗? 您的结果是返回一行还是数千?如果成千上万或数以百万计的数据或多或少地分布在您的数据中,那么索引的伤害也无济于事。如果只有一行,那么不同的数据库可能更有意义(我从不在 Spark 中处理一条记录,对吗?) 如果你仔细想想,你在现实生活中永远不会只靠一行工作(没有相关的行与之相连)。在测试中,是的。但不在生产中。数据本身完全没有意义,您的示例看起来很有说服力,直到您意识到它是一个无用的用例。 谢谢@DavidGriffin,我想我明白你的意思了。我可能会接受 zero323 的回答,因为它解决了这个问题的更多方面,但这对我来说是一个非常有教育意义的讨论。 我不怪你——我的回答只是在主题上。以上是关于为啥 Spark SQL 认为索引的支持不重要?的主要内容,如果未能解决你的问题,请参考以下文章
技术分享 为啥 SELECT 查询选择全表扫描,而不走索引?