hive表建表设计层面调优主要合理设计组织数据方便后续高效计算,比如建表的字段数据类型、文件存出格式、文件压缩格式等
利用分区优化
分区表是在某一个或者几个维度上对数据进行分类存储,一个分区表对应一个目录。如果筛选条件里有分区字段,那么Hive只需要遍历对应分区目录下的文件即可,不需要遍历全局数据,使得处理的数据量大大减少,从而提高查询效率。
也就是说,当一个hive表的查询大多数情况下,会根据某一个字段进行筛选是,那么非常适合创建为分区表,该字段即为分区字段。
sql1:select ... ... from table where country = 'china'
sql2:select ... ... from table where country = 'china'
sql3:select ... ... from table where country = 'china'
sql4:select ... ... from table where country = 'china'
分门别类:这个country字段的每个值就单独形成一个分区,其实每个分区就对应HDFS的一个目录,在创建表时通过启用partitioned by 实现,用来partition 的维度并不是实际数据的某一列,具体分区的标志是有插入内容是给定的,当要查询某一分区的内容时可以采用where语句,形似tablename.partition_column = a 来实现。
-
创建分区表
create table tmp.page_view (view_time INT, user_id BIGINT, page_url STRING, ip STRING COMMENT 'IP Address of the user') PARTITIONED BY (dt STRING,country STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY ' 01' STORED AS TEXTFILE;
-
导入数据并指定分区值
load data local input '/root/data/input/pv_2021-01-26.txt' into table tmp.page_view partition(dt='2021-01-26',country='US');
-
查询指定分区值数据
SELECT t1.* from tmp.page_view where t1.dt='2021-01-26' and t1.country='US'
总结:
- 当意识到字段经常来做where建立分区表,并且使用此字段作为分区
- 在查询时,使用分区字段过滤可以避免全表扫描,只需扫描查询分区的数据即可
利用分桶优化
Hive Buck 分桶是指将数据以某特定列的值为key进行hash,hash到指定数目的桶中,这样做的目的和分区类似,使得筛选是不用全局遍历所有数据,只要遍历所在桶数据即可,并且可以支持高效采样。
采样多少百分比的数据,采样多少条数据,采样多大量数据
Doris等OLAP引擎中colocation join两张表的相同分桶编号的数据在同一个节点
分桶与分区类似,都是把数据分成多个不同类别,区别就是规则不同,具体如下
-
分区是按照字段值进行分区,一个分区包含此分区值的所有数据,并不会含有非此分区值的数据
-
分桶默认规则是按照Hash散列进行分桶,一个分桶包含多个不同值,且某个值的所有数据只会在一个分桶中。
-
创建分桶表
create table( view_time INT, user_id BIGINT, page_url STRING, refer_url STRING, ip STRING COMMENT 'IP Address of the User') COMMENT 'This is the page view table' PARTITION BY (dt STRING,country STRING) CLUSTERED BY (user_id) STORED BY (view_time) INTO 32 BUCKETS ROW FORMAT DELIMITED FIELDS TERMINATED BY ' 01' COLLECTION ITEMS TERMINATED BY ' ' MAP KEYS TERMINATED ' ' STORED AS SEQUENCEFILE;
-
分桶语法说明
CLUSTERED BY(userid) SORTED BY(view_time) INTO 32 BUCKETS
- CLUSTERED BY(user_id):表示按照字段user_id来进行分桶
- SORTED BY (view_time):表示按照字段view_time进行桶内排序
- INTO 32 BUCKETS :设置分桶个数
-
分桶划分,两张表倍数关系
create table order(cid int,price float) clustered by(cid) into 32 buckets; create table customer(id int,first string) clustered by(id) into 32 buckets; select price from order t join customer s on t.cid = s.id
通常情况下,Sampling 在全体数据上进行采样这样效率自然会很低,则需要去访问所有数据,如果一张表已经将某一列进行分桶,就可以进行指定序号分桶进行数据采样,这样就会减少数据扫描数量。
如下示例通过分桶方式对表page_view中的32个桶中的第3桶全部数据进行采样
select * from tmp.page_view tablesample(BUCKET 3 OUT OF 32);
如下示例所示就是采样了page_view中32个桶中的第三个桶的一半数据
select * from tmp.page_view tablesample(bucket 3 out of 64);
-
利用分桶采样
在大规模数据量数据分析及建模任务中,往往针对全量数据进行挖掘分析时会对十分耗时和占用集群资源,因此一般情况下只需要抽取一下部分数据进行分析及建模操作。Hive提供了数据抽样(SAMPLING)的功能,能够根据一定规则进行数据抽样目前支持数据块抽样、分桶抽样和随机抽样,具体如下所示
-
数据快抽样(tablesample函数)
-
tablesample(n percent) 根据Hive表数据的大小按比例抽取数据,并保存到Hive表中,如抽取Hive表中10%的数据
create table page_view_new as select * from page_view tablesample(10 percent);
注意:测试中发现,select语句不能带where条件且不支持子查询,可以通过新建中间表或者使用随机抽样解决
-
tablesample(n M) 指定抽取数据大小,单位为M
-
tablesample(n rows) 指定抽取数据行数,其中 n 代表每个map任务均取 n 行数据,map 数量可通过hive表的简单查询语句进行确认(关键词:number of mappers:x)
数据块抽样缺点是数据不随机,只是按照文件中的顺序返回指定量的数据,对于分区表从头开始进行抽样可能造成只是抽样出前几个分区数据,但是数据块抽样速度快。
-
-
分桶抽样
hive中分桶其实就是根据某一个字段Hash取模,放入指定数量的桶中,比如将表page_view按照user_id分成32个桶,其算法是hash(user_id)%32,将hash(user_id) % 32 = 0的数据放到第一个桶中,以此类推将数据分别存放到32个桶。创建分桶语句:
CLUSTER BY 语句
分桶抽样语法
TABLESAMPLE (BUCKET x OUT OF y [ON colname])
其中x是要抽样的桶编号,桶编号从1开始,colname表示抽样的列,y表示桶的数量
例如:将表随机分成10组,抽取其中第一个桶的数据
select * from page_view tablesample(bucket 1 out of 10 on rand());
-
随机抽样(rand函数)
-
使用rand函数进行随机抽样,limit关键字限制抽样返回的数据,其中rand函数前的distribute 和 sort 关键字可以保证数据在Mapper和Reducer阶段是随机分布的,如下
select * from page_view where user_id = '001' distribute by rand() sort by rand() limit num; --推荐使用方式
-
使用order 关键词
select * from page_view where user_id = '001' order by rand() limit num;
-
-
文件存储格式
常见数据存储格式分为以下三类
- 水平行存储结构 (horizontal row-store structure)
- 垂直列存储结构 (vertical column-store structure)
- PAX 混合存储结构 (hybrid PAX store structure)
hive默认存储格式textfile,在hive创建表create table语句中,可以通过stored as 进行指定表数据存储格式。Apache hive支持Hadoop 中使用的几种文件格式,例如TextFile、SequenceFile、RCFile、Avro、ORC、ParquetFile
存储格式一般需要根据业务进行选择,在实际使用中,绝大数表都采用TextFile与ParquetFile两种存储格式之一,TextFile是最简单存储格式,它是纯文本记录存储,也是hive默认存储格式,虽然磁盘开销大、查询效率也低,但是它更多地是作为跳板来使用。RCFile、ORC、Parquet等格式的表都不能由文件直接导入数据,必须由TextFile来作为中转。Parquet 和 ORC 都是Apache旗下开源列式存储格式,列式存储格式比起传统的行式存储更合适批量OLAP查询,并且支持更好的压缩和编码。
创建表时(特别是宽表),尽量使用ORC、ParquetFile这些列式存储格式,因为列式存储表每一列的数据在物理上是存储在一起的,Hive查询时会只遍历所需要的列数据,大大减少处理的数据量。
- TextFile存储格式
- 存储方式为行式存储,是hive的默认数据存储格式,若建表时不指定存储格式则存储为TextFile
- 每一行为一条记录,每行都以换行符 “ ” 结尾,数据不做压缩时,磁盘占用和数据解析开销比较大
- 可以结合Gzip、Bzip2等压缩方式一起使用(查询时系统会自动检查进行自动解压),推荐选用可切分压缩算法
- Sequence File存储格式
- Hadoop API提供的一种二进制文件,使用方便、可分割、可压缩的特点。
- 支持三种压缩选择:NONE、RECORD、BLOCK。RECORD压缩率低,一般建议使用BLOCK。
- RC File存储格式
- 数据存储按行分块,每块按照列存储。
- 首先将数据按行分块,保证同一个record在一个块上,避免读一个记录需要读取多个block。
- 其次快数据列式存储,有利于数据压缩和快速的列存取。
- 相对来说,RCFile对于提升任务执行性能提升不大,但是能节省一定的存储空间。可以使用升级版的ORC格式
- 数据存储按行分块,每块按照列存储。
- ORC File存储格式
- 数据按行分块,每块按照列存储
- Hive提供数据存储的新格式,是RCFile的升级版,性能有大幅度提升,而且数据可以压缩存储且压缩快,快速列存取。
- Parquet File存储格式
- 列式存储
- Parquet对于大型查询的类型查询效率高,对于扫描特定表格中的特定列查询,Parquet特别有用。
- Parquet 一般使用snappy、Gzip压缩,默认为snappy。
- Parquet 支持Impala查询
- 表的文件存储格式尽量采用Parquet或ORC,不仅减少占用存储空间,而且还有优化了查询、压缩和表关联性能。
文件压缩格式
Hive 语句最终是转化为 MapReduce 程序来执行的,而 MapReduce 的性能瓶颈在与 网络IO 和 磁盘IO,要解决性能瓶颈,最主要的是 减少数据量,对数据进行压缩是个好方式。压缩虽然是减少了数据量,但是压缩过程要消耗 CPU,但是在 Hadoop 中,往往性能瓶颈不在于 CPU,CPU 压力并不大,所以压缩充分利用了比较空闲的 CPU。
- 常用压缩方法对比
压缩方式 | 是否可拆分 | 是否自带 | 压缩率 | 速度 | Hadoop是否自带 |
---|---|---|---|---|---|
gzip | 否 | 是 | 很高 | 比较快 | 是 |
lzo | 是 | 是 | 比较高 | 很快 | 否,需安装 |
snappy | 否 | 是 | 比较高 | 很快 | 否,需安装 |
bzip2 | 是 | 否 | 最高 | 慢 | 是 |
-
如何选择压缩方式
- 压缩比率
- 压缩解压速度
- 是否支持split
支持分割的文件可以并行多个Mapper程序处理大数据操作,大多数文件不支持可分割是因为这些文件只能从头开始读
-
是否压缩
- 计算密集型不压缩,否则增加了CPU负担
- 网络密集型推荐压缩,减少网络传输
-
各个压缩方式对应的class类
压缩格式 | 类 |
---|---|
zlib | org.apache.hadoop.io.compress.DefaultCodec |
gzip | org.apache.hadoop.io.compress.GzipCodec |
bzip2 | org.apache.hadoop.io.compress.Bzip2Codec |
lzo | org.apache.hadoop.io.compress.lzo.LzoCodec |
lz4 | org.apache.hadoop.io.compress.LzoCodec |
snappy | org.apache.hadoop.io.compress.SnappyCodec |
-
压缩使用
-
job输出文件按照block以及Gzip的方式进行压缩
--默认值是false set mapreduce.output.fileoutputformat.compress=true; --默认值是record set mapreduce.output.fileoutputformat.compress.type=BLOCK --默认值是org.apache.hadoop.io.compress.DefaultCodec set mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.GzipCodec
-
Map输出结果也可以进行Gzip压缩
--启用map端输出压缩 set mapred.map.output.compress=true --默认值是org.apache.hadoop.io.compress.DefaultCodec set mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.GzipCodec
-
对hive输出结果和中间都进行压缩
set hive.exec.compress.output=true --默认值是false,不压缩 set hive.exec.compress.intermediate=true --默认值是false,为true必须设置MR压缩
-