零起步的Hadoop实践日记(hive in action)

【Schema设计】

首先碰到的问题就是如何设计表结构。关键字:partition,cluster,row format

建表需要对如下几个feature弄情况,合理使用

partition 
    就是按某个字段分文件夹

index 
    据说索引并不成熟,至少有一点,每次导数据后倒要重建索引,感觉挺傻的

store as rcfile
    这个结构结合了行表和列表的优势,对比Text和Sequence 俩种存储

external table 
    如果数据不止在hive处理需要其他的工具,使用外部表

bucket 
    为了并行处理,文件夹下的文件会散列到bucket个文件中

实验表

OK
CREATE  TABLE dailystats(
  uid int, 
  stime float, 
  etime float, 
  calories float, 
  steps int, 
  activevalue int, 
  pm25suck float, 
  rundist float, 
  rundura float, 
  cycdist float, 
  cycdura float, 
  walkdist float, 
  walkdura float, 
  runcal float, 
  cyccal float, 
  walkcal float, 
  goadcal float, 
  goalsteps int, 
  goalactiveval int, 
  locations array<string>)
PARTITIONED BY ( 
  day int)
CLUSTERED BY ( 
  uid) 
INTO 64 BUCKETS
ROW FORMAT DELIMITED 
  FIELDS TERMINATED BY ',' 
  COLLECTION ITEMS TERMINATED BY '|' 
  LINES TERMINATED BY '
' 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.mapred.TextInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
  'hdfs://localhost:8020/user/hive/warehouse/dailystats'
TBLPROPERTIES (
  'numPartitions'='4', 
  'numFiles'='4', 
  'transient_lastDdlTime'='1395466580', 
  'numRows'='0', 
  'totalSize'='1146993293', 
  'rawDataSize'='0')

这张表是Text存储,新建另一张表,存储格式为RCFile,其余完全相同。数据量165w,导入的原始文件大小为1G。

吐槽一点RCFile和Sequence格式的表只能从Text表中导入,也就是说,无论如何必须有张Text冗余,当然用完可以删了。

第一轮 RCFile vs Text

select count(1) from dailystats;

Text:68.69 seconds  59.325 seconds 55.135 seconds

RCFile:53.372 seconds 54.468 seconds  52.432 seconds

select * from dailystats where uid=90562

Text:46.282 seconds

RCFile:47.527 seconds

select count(1) from dailystats where uid<5000;

Text:59.373 seconds

RCFile:49.974 seconds

性能上感觉优势并不大,只能算优化。

"实践证明RCFile目前没有性能优势, 只有存储上能省10%的空间, 作者自己都承认. Facebook用它也就是为了存储,. RCFile目前没有使用特殊的压缩手段, 例如算术编码, 后缀树等, 没有像InfoBright那样能skip 大量io" (by 参考)


第二轮 Index

给user表(200w)加上uid的索引

select count(1) from users;

无索引:Time taken: 27.662 seconds

有索引:Time taken: 21.207 seconds

select count(1) from users where gender = 1;

无索引:Time taken: 22.275 seconds

有索引:Time taken: 22.315 seconds

select count(1) from users where age > 10;

无索引:Time taken: 24.27 seconds

有索引:Time taken: 23.629 seconds

select * from users where uid=71078;

无索引:Time taken: 16.631 seconds

有索引:Time taken: 14.93 seconds

我的结论就是,用处真不大。

第三轮 Partition

partition适合按某个字段来分表,其实就是放到不同文件夹,partition不能太多,一般按日期是比较合适的一种,这样一年才365个左右,如果按用户来做分区,按我的场景,每天要载入数万用户。本机测试,一个用户文件再小基本都是6秒,这样一万个用户差不多就要一天了,稍微上点规模的应用,用户都不可能小于这个。但是一个用户一条SQL就提交任务是不合理的,有办法大大优化:

Scheme设计

一种常规的模式是按天分区,这样十年下来多3600个文件夹,按照每个文件夹,文件,数据块需要消耗namenode 150byte的内存,才500+k,但是如果需要按用户分区,200w用户就会消耗280M内存,对于内存有点压力。

Hive 导入数据

如何是导入多个数据源,可以合并到一条SQL用分号隔开,这样多个数据源的导入只需要一次提交,效率大大提高。

俩个测试

1)有1w用户,每个用户有6行左右数据一个partition,这样如果每个用户执行一次load data local inpath,提交一次较耗时,结论是速度是6秒/每个用户。每天需要导入6万秒,耗时几乎一整天。如果合并为一行:

合并为一个SQL文件,一个SQL占一行,测试约5分钟导入了约1200个用户,速度是 0.25秒/每个用户,提高到24倍

但是一般情况下,仍然不建议对这样场景的uid分区

Hive Stream

类似Hadoop的Stream(本来就是借用Hadoop的东西)。简单讲,Hive的stream已经把Map写了(按select出来的第一个字段为key value),自己只要写Reduce部分,比较不容易处理的就是用本地数据调试好的本地代码上传到Hive中,常常闹幺蛾子。日志不容易找到,找到日志是最靠谱的。先记录着现在碰到的问题有:

  • 本地可以int(1.2345),hive上不可以,换成float(1.2345)
  • list[0] = p ,p也是一个列表,这个时候换成list.append(p) 
  • 调用函数dist(tpoi[i][0],tpoi[i][1],p[0],p[1]),本地都知道是参数和返回都是float,但是换成这样才能跑通: float(dist(float(tpoi[i][0]),float(tpoi[i][1]),float(p[0]),float(p[1])))

PS:用百度地图API,用经纬度取得地点名称。结果返回的json打印时候出现 UnicodeEncodeError: 'ascii' codec can't encode character u'xf3' in position 11: ordinal not in range(128)

加上

import sys
reload(sys)
sys.setdefaultencoding("utf-8")

解决方案参考地址

原文地址:https://www.cnblogs.com/aquastar/p/3616283.html