为什么Spark SQL认为索引的支持不重要?

hil*_*lel 35 sql bigdata in-memory-database apache-spark apache-spark-sql

引用Spark DataFrames,Datasets和SQL手册:

Spark中还没有包含少数Hive优化.由于Spark SQL的内存计算模型,其中一些(如索引)不太重要.其他版本用于将来的Spark SQL版本.

作为Spark的新手,我有点困惑,原因有两个:

  1. Spark SQL旨在处理大数据,至少在我的用例中,数据大小远远超过可用内存的大小.假设这并不罕见,"Spark SQL的内存计算模型"是什么意思?Spark SQL是否仅适用于数据适合内存的情况?

  2. 即使假设数据适合内存,对非常大的数据集进行全扫描也需要很长时间.我在内存数据库中读取了这个反对索引的论点,但我并不相信.那里的例子讨论了一个10,000,000记录表的扫描,但这不是真正的大数据.扫描具有数十亿条记录的表可能导致"SELECT x WHERE y = z"类型的简单查询永远不会立即返回.

我理解索引有诸如较慢的INSERT/UPDATE,空间要求等缺点.但在我的用例中,我首先处理并将大批数据加载到Spark SQL中,然后将这些数据作为一个整体进行探索,而无需进一步修改.Spark SQL对于初始分布式处理和数据加载很有用,但缺乏索引使得交互式探索比我预期的更慢,更麻烦.

我想知道为什么Spark SQL团队认为索引不重要到它的路线图之外的程度.是否有不同的使用模式可以提供索引的好处,而无需独立实现等效的东西?

zer*_*323 35

索引输入数据

  • 索引外部数据源不在Spark范围内的根本原因是Spark不是数据管理系统而是批处理数据处理引擎.由于它不拥有它正在使用的数据,因此无法可靠地监视变化,因此无法维护索引.
  • 如果数据源支持索引,则Spark可以通过谓词下推等机制间接利用它.

索引分布式数据结构:

  • 标准索引技术需要持久且定义明确的数据分布,但Spark中的数据通常是短暂的,其精确分布是不确定的.
  • 通过适当的分区与列式存储和压缩相结合实现的高级数据布局可以提供非常有效的分布式访问,而无需创建,存储和维护索引的开销.这是不同的内存中柱状系统使用的常见模式.

可以说,某些形式的索引结构确实存在于Spark生态系统中.最值得注意的是,Databricks 在其平台上提供了数据跳过索引.

其他项目,如Succinct(今天大多不活跃)采用不同的方法,并使用高级压缩技术和随机访问支持.

当然,这提出了一个问题 - 如果需要有效的随机访问,为什么不使用从一开始就设计为数据库的系统.有很多选择,包括至少一些由Apache基金会维护的选择.与此同时,Spark作为一个项目不断发展,您使用的报价可能无法完全反映Spark未来的发展方向.

  • @shridharama 如果是这样的话,答案是否定的。`repartition` 不监控任何东西。它创建了不可变且有效的短暂数据结构,仅限于给定的应用程序。分区发现仅假设结构有效。它不监控数据,也不验证超出标准 Parquet 功能的数据。 (2认同)

Dav*_*fin 15

一般来说,索引的效用充其量是有问题的.相反,数据分区更重要.它们是非常不同的东西,并且仅仅因为您选择的数据库支持索引并不意味着它们在Spark试图做的事情中有意义.它与"在记忆中"无关.

那么什么是指数呢?

回到永久存储是疯狂昂贵(而不是基本免费)的日子,关系数据库系统都是关于最小化永久存储的使用.必要时,关系模型将记录分成多个部分 - 规范化数据 - 并将它们存储在不同的位置.要阅读客户记录,也许你会阅读一张customer桌子,一张customerType桌子,从表格中取出几个条目address等.如果你有一个解决方案要求你阅读整个表格以找到你想要的东西,这是非常昂贵的,因为你必须扫描这么多表.

但这不是唯一的做事方式.如果您不需要具有固定宽度的列,则可以将整个数据集存储在一个位置.您无需在一堆表上执行全表扫描,而只需在单个表上执行此操作.而且这并不像你想象的那么糟糕,特别是如果你可以对数据进行分区.

40年后,物理定律发生了变化.硬盘随机读/写速度和线性读/写速度有很大差异.基本上每个磁盘可以进行350次磁头移动.(或多或少,但这是一个很好的平均数.)另一方面,单个磁盘驱动器可以读取大约每秒100 MB.那是什么意思?

算一算并考虑一下 - 这意味着如果每个磁盘头的移动读数低于300KB,则会限制驱动器的吞吐量.

Seriouusly.想一想.

索引的目标是允许您将磁盘头移动到所需磁盘上的精确位置,然后只读取该记录 - 比如address记录中加入的customer记录.我说,那没用.

如果我正在设计一个基于现代物理学的索引,它只需要让我在目标数据块的100KB左右(假设我的数据已经以大块的方式布局 - 但我们无论如何都在谈论理论) .基于上面的数字,比这更精确只是浪费.

现在回到标准化的表格设计.假设一条customer记录实际上分为5个表中的6行.6总磁盘头移动(我假设索引缓存在内存中,因此没有磁盘移动).这意味着我可以读取1.8 MB的线性/非标准化客户记录,并且效率也一样高.

客户历史怎么样?假设我不想只看到客户今天的样子 - 想象一下,我想要完整的历史记录,还是历史的一部分?将上面的所有内容乘以10或20即可得到图片.

什么比索引更好的是数据分区 - 确保所有客户记录最终都在一个分区中.通过单磁盘头移动,我可以读取整个客户历史记录.一个磁头移动.

再次告诉我你为什么要索引.

索引vs ___?

不要误会我的意思 - 你的搜索"预先烹饪"是有价值的.但物理定律提出了比传统指数更好的方法.而不是将客户记录存储在一个位置,并创建指向它的指针 - 索引 - 为什么不将记录存储在多个位置?

请记住,磁盘空间基本上是免费的.而不是试图最小化我们使用的存储量 - 关系模型的过时工件 - 只需将您的磁盘用作搜索缓存.

如果您认为某人希望按地理位置和销售代表查看客户,则以优化这些搜索的方式存储客户记录的多个副本.就像我说的那样,使用像内存缓存一样的磁盘.不是通过将不同的持久数据组合在一起来构建内存缓存,而是构建持久数据以镜像内存缓存,因此您只需要读取它.事实上,甚至不想将其存储在内存中 - 只需在每次需要时直接从磁盘读取它.

如果您认为这听起来很疯狂,请考虑这一点 - 如果您将其缓存在内存中,您可能会将其缓存两次.您的操作系统/驱动器控制器可能使用主内存作为缓存.不要打扰缓存数据,因为其他人已经是!

但我离题了......

简而言之,Spark绝对支持正确的索引类型 - 从原始数据创建复杂的派生数据以使未来使用更加高效的能力.它只是不按你想要的方式去做.

  • @DavidGriffin,我理解你的观点,如果你的查询需要大量的头部移动,你可能会更好地读取顺序数据。但准确地说,我们应该考虑顺序读取的速度:如果数据的大小足够大,读取所需的时间比磁头移动的时间还要多,我们仍然会从索引中受益,不是吗?例如,简单的 SELECT x WHERE y=z 查询将需要很少的头部移动,并且可能对非常大的数据大小(TB)进行操作。在这些情况下,顺序读取是否会慢很多? (2认同)
  • “40年后,物理定律发生了变化。硬盘的随机读/写速度和线性读/写速度已经大大不同。基本上每个磁盘每秒可以进行350次磁头移动。(或多或少,但仅此而已)一个不错的平均数字。)另一方面,单个磁盘驱动器每秒可以读取大约 100 MB 的数据。” 应该指出的是,对于 SSD,这些数字“截然不同”,这显着改变了全表扫描与索引扫描的计算。 (2认同)