大数据信号分析:更好的存储和查询信号数据的方法

时间:2016-04-24 10:24:50

标签: hadoop apache-spark hive impala parquet

我正在使用Hadoop / Spark进行一些信号分析,我需要有关如何构建整个过程的帮助。

信号现在存储在数据库中,我们将使用Sqoop读取,并将在HDFS上的文件中进行转换,其架构类似于:

<Measure ID> <Source ID> <Measure timestamp> <Signal values>

其中信号值只是由浮点逗号分隔的数字组成的字符串。

000123  S001  2015/04/22T10:00:00.000Z  0.0,1.0,200.0,30.0 ... 100.0
000124  S001  2015/04/22T10:05:23.245Z  0.0,4.0,250.0,35.0 ... 10.0
...
000126  S003  2015/04/22T16:00:00.034Z  0.0,0.0,200.0,00.0 ... 600.0

我们希望将交互式/批量查询写入:

将聚合函数应用于信号值

SELECT *
FROM SIGNALS
WHERE MAX(VALUES) > 1000.0

选择峰值超过1000.0的信号。

将汇总应用于汇总

SELECT SOURCEID, MAX(VALUES) 
FROM SIGNALS
GROUP BY SOURCEID
HAVING MAX(MAX(VALUES)) > 1500.0

选择至少有一个信号超过1500.0的信号源。

在样本上应用用户定义的函数

SELECT *
FROM SIGNALS
WHERE MAX(LOW_BAND_FILTER("5.0 KHz", VALUES)) > 100.0)

选择以5.0 KHz过滤后的信号至少具有超过100.0的值。

我们需要一些帮助才能:

  1. 找到正确的文件格式,在HDFS上写入信号数据。我想到了Apache Parquet。你将如何构建数据?
  2. 了解正确的数据分析方法:最好创建不同的数据集(例如,使用Spark处理数据并在HDFS上保留结果)或尝试在查询时从原始数据集执行所有操作?
  3. Hive是一个很好的工具来查询我写的那些查询吗?我们在Cloudera Enterprise Hadoop上运行,因此我们也可以使用Impala。
  4. 如果我们从原始数据集生成不同的派生数据集,我们如何跟踪数据的谱系,即知道如何从原始版本生成数据?
  5. 非常感谢!

2 个答案:

答案 0 :(得分:5)

1)Parquet作为柱状格式适用于OLAP。 Parquet的Spark支持已经足够成熟,可供生产使用。我建议将表示信号值的字符串解析为以下数据结构(简化):

 case class Data(id: Long, signals: Array[Double])
 val df = sqlContext.createDataFrame(Seq(Data(1L, Array(1.0, 1.0, 2.0)), Data(2L, Array(3.0, 5.0)), Data(2L, Array(1.5, 7.0, 8.0))))

保持double数组允许定义和使用这样的UDF:

def maxV(arr: mutable.WrappedArray[Double]) = arr.max
sqlContext.udf.register("maxVal", maxV _)
df.registerTempTable("table")

sqlContext.sql("select * from table where maxVal(signals) > 2.1").show()
+---+---------------+
| id|        signals|
+---+---------------+
|  2|     [3.0, 5.0]|
|  2|[1.5, 7.0, 8.0]|
+---+---------------+

sqlContext.sql("select id, max(maxVal(signals)) as maxSignal from table group by id having maxSignal > 1.5").show()
+---+---------+
| id|maxSignal|
+---+---------+
|  1|      2.0|
|  2|      8.0|
+---+---------+

或者,如果你想要一些类型安全,使用Scala DSL:

import org.apache.spark.sql.functions._
val maxVal = udf(maxV _)
df.select("*").where(maxVal($"signals") > 2.1).show()
df.select($"id", maxVal($"signals") as "maxSignal").groupBy($"id").agg(max($"maxSignal")).where(max($"maxSignal") > 2.1).show()
+---+--------------+
| id|max(maxSignal)|
+---+--------------+
|  2|           8.0|
+---+--------------+

2)这取决于:如果您的数据大小允许在查询时间内以合理的延迟进行所有处理 - 那就去做吧。您可以从这种方法开始,然后为慢速/热门查询构建优化的结构

3)Hive很慢,Impala和Spark SQL已经过时了。有时选择并不容易,我们使用经验法则:如果所有数据都存储在HDFS / Hive中,Impala适用于没有连接的查询,Spark具有更大的延迟但连接可靠,它支持更多数据源并具有丰富的非SQL处理功能(如MLlib和GraphX)

4)保持简单:存储原始数据(主数据集)去重复和分区(我们使用基于时间的分区)。如果新数据到达分区并且您已经生成了下游数据集 - 请重新启动此分区的管道。

希望这有帮助

答案 1 :(得分:0)

首先,我相信Vitaliy的方法在各个方面都非常好。 (我全都是Spark)

但是,我想提出另一种方法。原因是:

  1. 我们想做交互式查询(+我们有CDH)
  2. 数据已经过结构化
  3. 需要分析&#39;并没有完全处理&#39;的数据。如果(a)数据结构化,我们可以更快地形成SQL查询,并且(b)我们不想在每次想要运行查询时编写程序,那么Spark可能是一种过度杀伤
  4. 以下是我想要采取的步骤:

    1. 使用sqoop进行HDFS摄取:[可选]使用--as-parquetfile
    2. 根据需要创建外部Impala表或内部表。如果您尚未将文件作为镶木地板文件传输,则可以在此步骤中执行此操作。分区,最好是Source ID,因为我们的分组将在该列上发生。
    3. 所以,基本上,一旦我们传输了数据,我们需要做的就是创建一个Impala表,最好采用镶木地板格式,并按照我们将要用于分组的列进行分区。记得在加载后进行计算统计,以帮助Impala更快地运行它。

      移动数据:   - 如果我们需要从结果中生成Feed,请创建一个单独的文件   - 如果另一个系统要更新现有数据,则在创建&gt;加载表时将数据移动到其他位置   - 如果它仅涉及查询和分析并获取报告(即外部表格就足够了),我们不需要不必要地移动数据   - 我们可以在相同数据的基础上创建外部配置单元表。如果我们需要运行长时间运行的批处理查询,我们可以使用Hive。不过,这对于交互式查询来说是禁忌。如果我们从查询中创建任何派生表并希望通过Impala使用,请记住运行“使元数据无效”#39;在对hive生成的表运行impala查询之前

      血统 - 我没有深入研究它,这里是link on Impala lineage using Cloudera Navigator