Category Archives: Hadoop&HIVE

Hive Impala Presto, comparison of performance

测试环境如下:

presto-env
其中关于impala和Presto的内存配置:

  • Impala Daemon 内存限制mem_limit为1GB
  • Presto的config.properties 配置文件中task.max-memory=1GB。

另外,由于内存有限,每次测试时Impala和Presto都没有同时开启。
总体测试表明,Presto的性能比Impala稍逊,在数据量巨大、查询比较复杂时,Impala和Presto的性能表现都不怎么样,比Hive也就快了2、3倍的样子
由于生产环境下,除了性能之外,还有并发度、稳定性、适应性等多种因素需要考虑,所以以上测试结果仅供参考。
以下是具体测试结果:

一、1百万条记录,HDFS文件大小600MB时的查询速度

在表记录数不多时,Impala和Presto都表现出比较好的查询性能,其中Impala的查询效率大约为Hive的9~50倍,Presto的查询效率大约为Hive的8~??倍(由于Presto的查询时间只精确到秒,在最简单的select * from xxx limit 5的查询中,显示时间为0秒)。
presto600mb

二、1千万条记录,HDFS文件大小6GB时的查询速度

在表记录数比较多时,Impala的查询效率大约为Hive的7~10倍,Presto的查询效率大约为Hive的4~30倍。
persto6gb

三、1亿条记录,HDFS文件大小60GB时的查询速度

在表记录数特别多时,Impala的查询效率大约为Hive的3~60倍,Presto的查询效率大约为Hive的2~180倍。
presto60gb


Data Warehouse For Ever原创文章,转载请注明出处

Hive Impala Presto, comparison of functionality

√: Yes; ×: No; Blue: The main differences between impala and presto

hive 0.11 impala 1.1.1 presto 0.52
Implement: java c++ backend
java frontend
java
DataType:
integer
string
floating point
timestamp
uniontype × ×
DDL:
create/alter/drop table ×
create view ×
truncate table × ×
desc
create index × ×
DML:
load data ×
insert ×
explain
tablesample × ×
group by
order by
having
limit
inner/left/right/full join (no full join)
union
Sub Queries
With
Lateral View × ×
Function:
UDF × ×
Mathematical Functions √31 √30 √31
String Functions √37 √22 √15
Date and Time Functions √17 √18 √26
Regex
Type Conversion Functions ×
Conditional Functions
Aggregate Functions √19 only 5 √16
Windowing ×
Distinct
Url
Json ×

功能上,Presto和Impala有几个不同的地方,也有和Impala相同的一些缺陷:
1. Presto完全没有数据写入的功能,不能建表、建视图、导数据,只能做select查询操作。
2. Presto不支持Full Outer Join。
3. Presto和Impala一样,都不支持UDF。
4. Presto支持窗口函数,而Impala不支持,不过Impala的开发团队号称明年推出基础的rank over partition 等功能。
5. Presto支持较多的聚合函数,而Impala目前只支持5个非常基础的聚合函数:MAX(), MIN(), SUM(), AVG(), and COUNT()。


Data Warehouse For Ever原创文章,转载请注明出处

Presto的单节点和多节点配置

近期在测试机上搭了一套impala和presto的测试环境,因为presto的文档资料比较少,配置时走过一些弯路,因此将我的配置给大家说一下,希望对想研究presto的同学能有些帮助。
Presto的架构,在官网上有介绍,我将官网的架构图稍微修改了一下,增加了Discovery的服务,这样可能看起来会更清楚一些。

下面分别说一下单节点和多节点的配置。

一、Presto单节点配置

首先,我在我的机器上安装了Cloudera Manger 4,部署了CDH HDFS、Hive和Impala。配置的过程很简单,因为Cloudera Manger还是很强大且方便的。
然后,我在这台单机上部署了Presto的单节点测试环境。

需要注意的是Presto只能支持jdk 1.7,因此必须安装好jdk 1.7,并配置好环境变量PATH和CLASSPATH,保证java 1.7在PATH路径中,保证Presto的lib在CLASSPATH路径中。

要下载的软件有两个:

  1. Presto server 0.52
  2. Presto client

进入server的目录,按照官网说明Deploying Presto创建etc文件夹和配置文件,由于是单机同时作为coordinator和workder,因此config.properties配置如下:

coordinator=true
datasources=jmx,hive
http-server.http.port=8080
presto-metastore.db.type=h2
presto-metastore.db.filename=var/db/MetaStore
task.max-memory=1GB
discovery-server.enabled=true
discovery.uri=http://v125203052.bja:8080

因为Presto的coordinator集成了一个简单的discovery服务,其端口号与Presto服务端口号8080一致,因此单节点不需要单独部署Discovery服务。
启动Presto客户端执行查询:
./presto --server v125203052.bja:8080 --catalog hive --schema default

二、Presto多节点配置

我在三台虚拟机上搭建了三个节点的测试环境,使用了Cloudera Manger 4进行了部署,我说一下这三台机器承担的主要角色吧:

  1. vm1:Presto Coordinator & Worker,Discovery Server,HDFS NameNode,HDFS DataNode,HDFS Balancer,Hive Metastore Server,Hive Gateway,Impala deamon,Impala StateStore Daemon,JobTracker,TaskTracker
  2. vm2:Presto Worker,HDFS DataNode,HDFS Gateway,Hive Gateway,Impala deamon,TaskTracker
  3. vm3:Presto Worker,HDFS DataNode,HDFS Gateway,Hive Gateway,Impala deamon,TaskTracker

同样,需要注意的是Presto只能支持jdk 1.7,因此必须安装好jdk 1.7,并配置好环境变量PATH和CLASSPATH,保证java 1.7在PATH路径中,保证Presto和Discovery的lib在CLASSPATH路径中。

在原来的服务器vm1上,还得安装一个Discovery Service,服务的配置与官方保持一致,端口为8411。先启动Discovery服务。
而Presto各个服务器上的配置注意事项有:

  1. node.id务必配置正确,各个服务器要不一样的node.id,可以在每个服务器上使用uuidgen命令生成一个id。
  2. 各个Presto服务的config.properties配置文件中,discovery.uri参数必须都指向discovery服务的地址。
  3. 原来Presto服务的config.properties配置文件中,discovery-server.enabled这一行必须注释掉。

以下是我的三个Presto服务的config.properties配置:

  • 1.vm1的config.properties配置:
  • coordinator=true
    datasources=jmx,hive
    http-server.http.port=8080
    presto-metastore.db.type=h2
    presto-metastore.db.filename=var/db/MetaStore
    task.max-memory=1GB
    discovery.uri=http://v125203052.bja:8411

  • 2.vm2和vm3的config.properties配置:
  • coordinator=false
    datasources=jmx,hive
    http-server.http.port=8080
    presto-metastore.db.type=h2
    presto-metastore.db.filename=var/db/MetaStore
    task.max-memory=1GB
    discovery.uri=http://v125203052.bja:8411

Now you can start the Presto client and execute the query:
./presto --server v125203052.bja:8080 --catalog hive --schema default
The output will be like these:

presto:default> select * from ljp limit 5;
a | b
—+——
0 | test
0 | test
1 | test
0 | test
1 | test
(5 rows)

Query 20131114_061434_00005_qcu94, FINISHED, 3 nodes
Splits: 12 total, 7 done (58.33%)
0:00 [42K rows, 337KB] [109K rows/s, 871KB/s]

And then, congralutions, let’s enjoy the Presto!!!


Data Warehouse For Ever原创文章,转载请注明出处

Trevni:一种列文件格式

Version 0.1
草案
本文档是一种文件格式的权威规范,它的意图是允许各种兼容和独立的实现,以便读写这种格式的文件。

概述

数据集通常被描述为由行、列组成的表。每个数据集中的记录被视为行,记录的每个字段被视为不同的列。在以行为主(row-major)格式的系统中,新创建的记录被逐个写入文件,如Hadoop SequenceFile或Avro数据文件。
但在许多情况下,如果数据是以列为主(column-major)的格式进行组织,其中给定列的多个值相邻存储,这样可以实现更高的查询性能。本文档定义的正是这样一种以列为主的数据文件格式。
要允许可扩展的、分布式的查询评估,数据集被划分为行组(row group),包含不同的行的集合。每个行组是按照以列为主的顺序组织的,而这些行组按照以行为主的分区组成整个数据集。

基本原理

目标

这种格式是为了满足以下目标:
1.最大化行组的大小。顺序访问数据时,使用磁盘驱动器是最有效的。假设某驱动器花费10ms进行寻道,以100MB/秒的速度进行传输。如果有一个10列的数据集(所有列值都是相同大小的),被分成10MB的行组,那么,访问单个列将需要一次寻道和1MB的读取,即处理速度为20ms/MB。如果相同的数据集被分成100MB的行组,那么处理速度将提高到11ms/MB。数据集的列越多、列值越小,这种效果就会越夸张。所以,我们偏好100MB或更大的行组。
2.允许行组内的随机访问。某些查询​​将首先检查一列,并且,只有当一些比较少见的条件得到满足,才检查其他列。与通过并行遍历行组中的选定列相比,人们更常见的是遍历其中一列并随机访问其他列。这就是所谓的支持WHERE子句,即SQL中的WHERE操作。
3.数据文件的数量最小化。 HDFS是这些文件的主要预期部署平台,该文件系统中的每个文件都需要HDFS NameNode的内存,因此,为了HDFS友好性(HDFS-friendly),这种文件格式应该努力做到文件的数量最小。
4.支持在行组内对列的协同定位(co-location)。在基于列的数据集中,行组是并行操作的单元。为了高效的文件IO,理想情况下,整个行组应该分布在同一主机上进行查询评估,以避免网络延迟和瓶颈。
5.数据完整性。该格式应允许应用检测数据是否损坏。许多文件系统可以防止数据损坏,但文件可能会在文件系统之间移动的过程中损坏。所以,最好在文件中的数据可以被独立地校验。
6.可扩展性。该格式应允许应用在文件中存储一个数据集的额外信息,如 文件类型信息、来源等。某些环境可能存储了这些信息的元数据,但并非所有的都是这样,并且,文件可能会在系统之间移动,这些系统有不同的元数据体系。因此,在文件中保存类似信息的能力,简化了这些信息的协调过程。
7.最小的开销。列格式不应该让数据集更大。存储是主要的成本之一,选择使用这种格式,不应该需要额外的存储。
8.主要格式。列格式应该被用作数据集的主要格式,而不是作为辅助、加速的格式。那些原本以行为主的顺序对数据集进行处理的应用,应该能够很容易地处理列文件;那些原本以行为主的顺序产生数据集的程序,也应该能方便地生成列文件。

设计

为了实现这些目标,我们提出了以下的设计:
1.每个行组是一个单独的文件。列在一个文件中的所有值被连续地写入。这使得行组的大小最大化,当查询更少、更小的列时性能最优。
2.每个文件占用一个单一HDFS块(block)。可以指定更大的block size,例如,1GB,而不是典型的100MB。这保证了co-location,同时当查询的数据co-located在文件内时避免了网络使用。这也缓和了HDFS NameNode的内存的影响,因为没有小文件的写入。
3.文件中的每一列以64kB的压缩块序列写入。该序列的前缀是描述了列中所有数据块的表,以允许随机存取。
4.应用程序特定的元数据可以是文件、列或数据块级别。
5.为了数据的完整性,每个数据库都包含校验。

讨论

CIF论文描述了一种方法,这种方法是每个文件使用一个单独的数据块,这样也可以达到自定义数据块存放策略相同的效果,同时还允许HDFS rebalancing且不增加命名空间的文件数量。

格式规范

本节正式介绍了列文件格式。

数据模型

我们假设一个简单的数据模型,其中一条记录是一组命名的字段,每个字段的值是一个无类型的字节序列。 在此之上可以有一个类型体系,如同下文类型映射(Type Mapping)一节中所指定的那样。

原型

我们定义了一下原型(primitive value types):

  • long:有符号的64位类型,zig-zag编码,可变长度,其中每个字节的高位(high-order bit)确定是否存在后续字节。例如:
十进制值 十六进制字节
0 00
-1 01
1 02
-64 7f
64 80 01

 

  • bytes:前面是个long值,后面跟着该长度的字节串;
  • string:前面是个long值,后面跟着该长度的UTF-8编码的字符串。
  • 例如,三个字符的string“foo”是这样编码的:
    long类型的3(十六进制06),后面是UTF-8编码的“f”、“o”和“o”(十六进制66 6f 6f),整体:06 66 6f 6f

类型名称

下列类型名称用于描述列值:

  • null,需要0字节,有时候用于数组列(array column)。
  • boolean,1个bit,打包为字节,低位优先(little-endian);
  • int,类似long,但严格限制为32位有符号值;
  • long,64位有符号值;
  • Fixed32,32位,存储为4字节,little-endian;
  • Fixed64,64位,存储为8字节,little-endian;
  • float,32位IEEE浮点值,little-endian;
  • double,64位IEEE浮点值,little-endian;
  • string,见上;
  • bytes,见上,可用于封装更复杂的string类型(UTF-8编码,长度为前缀)。

元数据

元数据(metadata)包含如下:

  • long值,表示元数据的KV值有多少对;
  • 每一对KV中,string类型的key和bytes类型的Value。

所有以“trevni.”开头的元数据属性都是预留的。

文件元数据

文件元数据属性定义如下:

  • trevni.codec:默认的数据块压缩编解码器的名称,string类型。所有实现需要支持“null”值。可选。如不指定则被假定为“null”。下文会有更详细的编解码器描述。
  • trevni.checksum:文件中使用的校验算法名称,string类型。所有实现需支持“CRC-32”校验。可选。如不指定则被假定为“null”。下文会有更详细的校验和描述。

列元数据

列元数据属性定义如下:

  • trevni.codec 用于压缩列数据块的压缩编解码器的名称,string类型。所有实现需要支持“null”值。可选。如不指定则被假定为“null”。下文会有更详细的编解码器描述。
  • trevni.name 列的名称,string类型,必选。
  • trevni.type 列数据的类型,上面类型名称的一种。必选。
  • trevni.values 如果存在,表示此列中的每个块的初始值将被存储在该块的描述符中。不允许数组列或指定了parent的列(参见下面2个)。
  • trevni.array 如果存在,表示此列中的每一行包含了指定类型的一序列值,而不是单个值。每个序列之前都有一个整型的数值表示序列的长度。
  • trevni.parent 如果存在,表示该数组列的长度与指定的parent名称的array列的长度一样。因此,这一列的值也是序列的,但是不会存储长度。

举例说明,假如有以下JSON格式的行,所有的值都是原型,但其中一个有多个值:
{"id"=566, "date"=23423234234
"from"="foo@bar.com",
"to"=["bar@baz.com", "bang@foo.com"],
"content"="Hi!"}

这样的列可能会这样指定:

name=id              type=int
name=date         type=long
name=from        type=string
name=to              type=string         array=true
name=content  type=string

如果有一行包含了一个记录数组,如下面的“received”:
{"id"=566, "date"=23423234234
"from"="foo@bar.com",
"to"=["bar@baz.com", "bang@foo.com"],
"content"="Hi!"
"received"=[{"date"=234234234234, "host"="192.168.0.0.1"},
{"date"=234234545645, "host"="192.168.0.0.2"}]
}

那么,我们就可以在这个记录中的每个字段后面定义一个父列(parent column),即增加以下列:

name=received        type=null           array=true
name=date                 type=long          parent=received
name=host                type=string        parent=received

如果一个数组值本身包含一个数组,如下面的“sigs”:
{"id"=566, "date"=23423234234
"from"="foo@bar.com",
"to"=["bar@baz.com", "bang@foo.com"],
"content"="Hi!"
"received"=[{"date"=234234234234, "host"="192.168.0.0.1",
"sigs"=[{"algo"="weak", "value"="0af345de"}]},
{"date"=234234545645, "host"="192.168.0.0.2",
"sigs"=[]}]
}

那么可以定义嵌套的父列:

name=sigs             type=null            array=true parent=received
name=algo            type=string        parent=sigs
name=value         type=string        parent=sigs

块元数据

目前没有定义块元数据。

文件格式

一个file包括:

  • 一个file header,后面是
  • 一个或多个列(column)

一个file header包括:

  • 四个字节,ACSII ‘T’, ‘r’, ‘v’,后面是1。
  • 一个fixed64,表示文件的行数
  • 一个fixed32,表示文件的列数
  • 文件元数据
  • 每个列的列元数据
  • 每个列在文件中的开始位置,以fixed64表示

一个column包括:

  • 一个fixed32,表示这个column的的数据块(block)数量
  • 每个数据块的块描述符(block descriptor)
  • 一个或多个数据块

一个block descriptor包括:

  • 一个fixed32,表示块中的行数
  • 一个fixed32,表示编码前的块大小,单位是字节(不包含校验和)
  • 一个fixed32,表示编码后的块大小,单位是字节(不包含校验和)
  • 如果这个列的元数据表明它包含值,列的第一个值,根据这个列的类型进行序列化

一个block包括:

  • 序列化的列值。如果是数组列,则这一序列值之前是它们的int类型的长度。如果指定了编码格式,这些值和长度将以这种格式进行压缩。
  • 校验和,根据文件元数据决定。

编码格式(Codecs)

  • null:“null”表示数据不压缩,直接存储;
  • deflate:表示以RFC 1951中指定的deflate算法编码写入数据。
  • snappy:表示使用Google Snappy压缩库。

校验和算法

  • null:“null”校验和包含0字节。
  • Crc-32:每个crc-32校验和包含四字节的ISO 3309 CRC-32校验和,以块未压缩时的数据进行计算,类型是fixed32。

类型映射

为了表示在不同的序列化系统中,列文件的数据类型是如何定义的,我们定义一套标准的映射关系。在这些系统中,记录被切分为列。当记录被嵌套时,depth-first递归方式可以为每个原型指定一个单独的列。
Avro
Protocol Buffers
Thrift

实现注意事项

可能有些写列文件的技术如下列所示:
1. 使用标准的〜100MB的block,缓冲区的内存最大为block size,然后直接将文件flush到HDFS。单个reduce任务可能会创建多个输出文件。 NameNode的需要的内存与命名数量和块*副本的数量成正比。这会增加一些命名的数量,但不是block的数量,所以这应该还是大大优于一列一个文件的方式。
2. 文件的block size设置为与整个文件一样,spill每列到一个单独的本地临时文件,当文件被关闭时,追加这些文件,然后往HDFS写入一个单独的文件。这样可能会有点慢,并且当本地磁盘满了的时候可能会出问题,但是当处理值很小的列时,这会减少对HDFS的namespace的使用,减少查找次数,从而达到更好的性能。
3. 使用单独的mapreduce job转换row-major文件为column-major。map的输出是一个(row#, column#, value)元组,partitioned by row# 但是sorted by column#和 row#.而reduce可以直接输出列文件。但是这样列文件格式需改为在文件结尾处写入记录数、描述符等信息,而不是在开头。
上面第1条是最简单的实现,大部分实现应该从这个开始。

参考资料

  • CIF Column-Oriented Storage Techniques for MapReduce, Floratou, Patel, Shekita, & Tata, VLDB 2011.
  • DREMEL Dremel: Interactive Analysis of Web-Scale Datasets, Melnik, Gubarev, Long, Romer, Shivakumar, & Tolton, VLDB 2010.
  • 英文原版:Trevni: A Column File Format


    Data Warehouse For Ever原创文章,转载请注明出处

    关于hive中not like的语法错误

    hive的低版本中不支持not like的语法,在执行类似如下语句时会报错

    create table ljp as
    select gmt_created, first_user_name, servicer_name
    from bds_c04_s_ocs_sessions
    where first_user_name not like ‘游客%’;

    错误信息如下:

    FAILED: Parse Error: line 16:4 cannot recognize input near ‘first_user_name’ ‘not’ ‘like’ in expression specification

    解决方案有两种:
    一、修改语句,将not放在表达式前面,即改为这样:not (a like ‘B%’)
    二、或升级hive版本,如在hive 1.1.7版本中,即可兼容not like语法。其他如not in等语句,也类似。详见社区的issue:https://issues.apache.org/jira/browse/HIVE-1740

    END


    Data Warehouse For Ever原创文章,转载请注明出处