/** Spark SQL源码分析系列文章*/

前面讲到了Spark SQL In-Memory Columnar Storage的存储结构是基于列存储的。

那么基于以上存储结构,我们查询cache在jvm内的数据又是如何查询的,本文将揭示查询In-Memory Data的方式。

一、引子

本例使用hive console里查询cache后的src表。
select value from src

当我们将src表cache到了内存后,再次查询src,可以通过analyzed执行计划来观察内部调用。

即parse后,会形成InMemoryRelation结点,最后执行物理计划时,会调用InMemoryColumnarTableScan这个结点的方法。

如下:

  1. scala> val exe = executePlan(sql("select value from src").queryExecution.analyzed)
  2. 14/09/26 10:30:26 INFO parse.ParseDriver: Parsing command: select value from src
  3. 14/09/26 10:30:26 INFO parse.ParseDriver: Parse Completed
  4. exe: org.apache.spark.sql.hive.test.TestHive.QueryExecution =
  5. == Parsed Logical Plan ==
  6. Project [value#5]
  7. InMemoryRelation [key#4,value#5], false, 1000, (HiveTableScan [key#4,value#5], (MetastoreRelation default, src, None), None)
  8. == Analyzed Logical Plan ==
  9. Project [value#5]
  10. InMemoryRelation [key#4,value#5], false, 1000, (HiveTableScan [key#4,value#5], (MetastoreRelation default, src, None), None)
  11. == Optimized Logical Plan ==
  12. Project [value#5]
  13. InMemoryRelation [key#4,value#5], false, 1000, (HiveTableScan [key#4,value#5], (MetastoreRelation default, src, None), None)
  14. == Physical Plan ==
  15. InMemoryColumnarTableScan [value#5], (InMemoryRelation [key#4,value#5], false, 1000, (HiveTableScan [key#4,value#5], (MetastoreRelation default, src, None), None)) //查询内存中表的入口
  16. Code Generation: false
  17. == RDD ==

二、InMemoryColumnarTableScan

InMemoryColumnarTableScan是Catalyst里的一个叶子结点,包含了要查询的attributes,和InMemoryRelation(封装了我们缓存的In-Columnar Storage数据结构)。
执行叶子节点,出发execute方法对内存数据进行查询。
1、查询时,调用InMemoryRelation,对其封装的内存数据结构的每个分区进行操作。
2、获取要请求的attributes,如上,查询请求的是src表的value属性。
3、根据目的查询表达式,来获取在对应存储结构中,请求列的index索引。
4、通过ColumnAccessor来对每个buffer进行访问,获取对应查询数据,并封装为Row对象返回。

  1. private[sql] case class InMemoryColumnarTableScan(
  2. attributes: Seq[Attribute],
  3. relation: InMemoryRelation)
  4. extends LeafNode {
  5. override def output: Seq[Attribute] = attributes
  6. override def execute() = {
  7. relation.cachedColumnBuffers.mapPartitions { iterator =>
  8. // Find the ordinals of the requested columns.  If none are requested, use the first.
  9. val requestedColumns = if (attributes.isEmpty) {
  10. Seq(0)
  11. } else {
  12. attributes.map(a => relation.output.indexWhere(_.exprId == a.exprId)) //根据表达式exprId找出对应列的ByteBuffer的索引
  13. }
  14. iterator
  15. .map(batch => requestedColumns.map(batch(_)).map(ColumnAccessor(_)))//根据索引取得对应请求列的ByteBuffer,并封装为ColumnAccessor。
  16. .flatMap { columnAccessors =>
  17. val nextRow = new GenericMutableRow(columnAccessors.length) //Row的长度
  18. new Iterator[Row] {
  19. override def next() = {
  20. var i = 0
  21. while (i < nextRow.length) {
  22. columnAccessors(i).extractTo(nextRow, i) //根据对应index和长度,从byterbuffer里取得值,封装到row里
  23. i += 1
  24. }
  25. nextRow
  26. }
  27. override def hasNext = columnAccessors.head.hasNext
  28. }
  29. }
  30. }
  31. }
  32. }

查询请求的列,如下:

  1. scala> exe.optimizedPlan
  2. res93: org.apache.spark.sql.catalyst.plans.logical.LogicalPlan =
  3. Project [value#5]
  4. InMemoryRelation [key#4,value#5], false, 1000, (HiveTableScan [key#4,value#5], (MetastoreRelation default, src, None), None)
  5. scala> val relation =  exe.optimizedPlan(1)
  6. relation: org.apache.spark.sql.catalyst.plans.logical.LogicalPlan =
  7. InMemoryRelation [key#4,value#5], false, 1000, (HiveTableScan [key#4,value#5], (MetastoreRelation default, src, None), None)
  8. scala> val request_relation = exe.executedPlan
  9. request_relation: org.apache.spark.sql.execution.SparkPlan =
  10. InMemoryColumnarTableScan [value#5], (InMemoryRelation [key#4,value#5], false, 1000, (HiveTableScan [key#4,value#5], (MetastoreRelation default, src, None), None))
  11. scala> request_relation.output //请求的列,我们请求的只有value列
  12. res95: Seq[org.apache.spark.sql.catalyst.expressions.Attribute] = ArrayBuffer(value#5)
  13. scala> relation.output //默认保存在relation中的所有列
  14. res96: Seq[org.apache.spark.sql.catalyst.expressions.Attribute] = ArrayBuffer(key#4, value#5)
  15. scala> val attributes = request_relation.output
  16. attributes: Seq[org.apache.spark.sql.catalyst.expressions.Attribute] = ArrayBuffer(value#5)
整个流程很简洁,关键步骤是第三步。根据ExprId来查找到,请求列的索引
attributes.map(a => relation.output.indexWhere(_.exprId == a.exprId))
  1. //根据exprId找出对应ID
  2. scala> val attr_index = attributes.map(a => relation.output.indexWhere(_.exprId == a.exprId))
  3. attr_index: Seq[Int] = ArrayBuffer(1) //找到请求的列value的索引是1, 我们查询就从Index为1的bytebuffer中,请求数据
  4. scala> relation.output.foreach(e=>println(e.exprId))
  5. ExprId(4)    //对应<span style="font-family: Arial, Helvetica, sans-serif;">[key#4,value#5]</span>
  6. ExprId(5)
  7. scala> request_relation.output.foreach(e=>println(e.exprId))
  8. ExprId(5)

三、ColumnAccessor

ColumnAccessor对应每一种类型,类图如下:

最后返回一个新的迭代器:

  1. new Iterator[Row] {
  2. override def next() = {
  3. var i = 0
  4. while (i < nextRow.length) { //请求列的长度
  5. columnAccessors(i).extractTo(nextRow, i)//调用columnType.setField(row, ordinal, extractSingle(buffer))解析buffer
  6. i += 1
  7. }
  8. nextRow//返回解析后的row
  9. }
  10. override def hasNext = columnAccessors.head.hasNext
  11. }

四、总结

Spark SQL In-Memory Columnar Storage的查询相对来说还是比较简单的,其查询思想主要和存储的数据结构有关。

即存储时,按每列放到一个bytebuffer,形成一个bytebuffer数组。

查询时,根据请求列的exprId查找到上述数组的索引,然后使用ColumnAccessor对buffer中字段进行解析,最后封装为Row对象,返回。

——EOF——

创文章,转载请注明:

转载自:OopsOutOfMemory盛利的Blog,作者: OopsOutOfMemory

本文链接地址:http://blog.csdn.net/oopsoom/article/details/39577419

注:本文基于署名-非商业性使用-禁止演绎 2.5 中国大陆(CC BY-NC-ND 2.5 CN)协议,欢迎转载、转发和评论,但是请保留本文作者署名和文章链接。如若需要用于商业目的或者与授权方面的协商,请联系我。

转自:http://blog.csdn.net/oopsoom/article/details/39577419

第十篇:Spark SQL 源码分析之 In-Memory Columnar Storage源码分析之 query的更多相关文章

  1. 第九篇:Spark SQL 源码分析之 In-Memory Columnar Storage源码分析之 cache table

    /** Spark SQL源码分析系列文章*/ Spark SQL 可以将数据缓存到内存中,我们可以见到的通过调用cache table tableName即可将一张表缓存到内存中,来极大的提高查询效 ...

  2. Spark学习之路(十)—— Spark SQL 外部数据源

    一.简介 1.1 多数据源支持 Spark支持以下六个核心数据源,同时Spark社区还提供了多达上百种数据源的读取方式,能够满足绝大部分使用场景. CSV JSON Parquet ORC JDBC/ ...

  3. Spark 系列(十)—— Spark SQL 外部数据源

    一.简介 1.1 多数据源支持 Spark 支持以下六个核心数据源,同时 Spark 社区还提供了多达上百种数据源的读取方式,能够满足绝大部分使用场景. CSV JSON Parquet ORC JD ...

  4. Tachyon在Spark中的作用(Tachyon: Reliable, Memory Speed Storage for Cluster Computing Frameworks 论文阅读翻译)

    摘要:         Tachyon是一种分布式文件系统,能够借助集群计算框架使得数据以内存的速度进行共享.当今的缓存技术优化了read过程,可是,write过程由于须要容错机制,就须要通过网络或者 ...

  5. 【Spark SQL 源码分析系列文章】

    从决定写Spark SQL源码分析的文章,到现在一个月的时间里,陆陆续续差不多快完成了,这里也做一个整合和索引,方便大家阅读,这里给出阅读顺序 :) 第一篇 Spark SQL源码分析之核心流程 第二 ...

  6. Spark SQL 源代码分析系列

    从决定写Spark SQL文章的源代码分析,到现在一个月的时间,一个又一个几乎相同的结束很快,在这里也做了一个综合指数,方便阅读,下面是读取顺序 :) 第一章 Spark SQL源代码分析之核心流程 ...

  7. 【慕课网实战】八、以慕课网日志分析为例 进入大数据 Spark SQL 的世界

    用户行为日志:用户每次访问网站时所有的行为数据(访问.浏览.搜索.点击...)     用户行为轨迹.流量日志   日志数据内容: 1)访问的系统属性: 操作系统.浏览器等等 2)访问特征:点击的ur ...

  8. Spark官方1 ---------Spark SQL和DataFrame指南(1.5.0)

    概述 Spark SQL是用于结构化数据处理的Spark模块.它提供了一个称为DataFrames的编程抽象,也可以作为分布式SQL查询引擎. Spark SQL也可用于从现有的Hive安装中读取数据 ...

  9. Spark SQL with Hive

    前一篇文章是Spark SQL的入门篇Spark SQL初探,介绍了一些基础知识和API,可是离我们的日常使用还似乎差了一步之遥. 终结Shark的利用有2个: 1.和Spark程序的集成有诸多限制 ...

随机推荐

  1. nodejs(三)上之express

    express 简介 Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具. 使用 Express 可以快速 ...

  2. Android studio 使用技巧和问题

    最近更新Android studio版本到1.2.1.1后 出现了一些问题,首先一个就是创建一个项目后,布局文件会提示 找不到类. 网上找了下答案,原来是这个版本的bug. 其实解决起来很简单,找到 ...

  3. 借助 Django 的 smart_str 和 smart_unicode 进行编码转换(转)

    原文:http://www.dirk.sh/diary/using-django-smart_str-smart_unicode/ Django 为字符编码的转换提供了非常简洁的方法: 1.djang ...

  4. BBS项目部署

    1.准备 项目架构为:LNM+Python+Django+uwsgi+Redis   (L:linux,N:nginx,M:mysql) 将bbs项目压缩上传到:  /opt 在shell中直接拖拽 ...

  5. SSM框架关于后台返回JSON数据中显示很多不需要的字段为NULL

    xml 配置 spring mvc 的 json 返回忽略 null 字段 <mvc:annotation-driven> <mvc:message-converters regis ...

  6. SIP中的 session, dialog 及 transaction 的解释

    http://stackoverflow.com/questions/35133331/difference-between-session-dialog-and-transaction-in-sip ...

  7. Java栈之顺序栈存储结构实现

    一.栈的基本定义 栈是一种数据结构,它代表一种特殊的线性表,这种线性表只能在固定一端(通常认为是线性表的尾端)进行插入.删除操作的特殊线性表,通常就是在线性表的尾端进行插入.删除操作. 二.顺序栈的实 ...

  8. python subplot 合并子图的方法

  9. 4. MySQL必知必会之排序检索数据-ORDER BY

    本章将讲授如何使用SELECT语句的ORDER BY子句,根据需要排序检 索出的数据. 1. 排序数据

  10. XDU 1130 (快速幂)

    题目大意:原题链接 解题思路:sum((p+i*d)*C(n,i))=sum(p*C(n,i)+d*i*C(n,i)) ; 又因为i*C(n,i)=n*C(n-1,i-1) 所以sum((p+i*d) ...