Hive的排序关键字是SORT BY,它有意区别于传统数据库的ORDER BY也是为了强调两者的区别–SORT BY只能在单机范围内排序。考虑以下表定义:
CREATE TABLE if not exists t_order( id int, -- 订单编号 sale_id int, -- 销售ID customer_id int, -- 客户ID product _id int, -- 产品ID amount int -- 数量 ) PARTITIONED BY (ds STRING);
在表中查询所有销售记录,并按照销售ID和数量排序:
set mapred.reduce.tasks=2; Select sale_id, amount from t_order Sort by sale_id, amount;
这一查询可能得到非期望的排序。指定的2个reducer分发到的数据可能是(各自排序):
Reducer1: Sale_id | amount 0 | 100 1 | 30 1 | 50 2 | 20 Reducer2: Sale_id | amount 0 | 110 0 | 120 3 | 50 4 | 20
因为上述查询没有reduce key,hive会生成随机数作为reduce key。这样的话输入记录也随机地被分发到不同reducer机器上去了。为了保证reducer之间没有重复的sale_id记录,可以使用DISTRIBUTE BY关键字指定分发key为sale_id。改造后的HQL如下:
set mapred.reduce.tasks=2; Select sale_id, amount from t_order Distribute by sale_id Sort by sale_id, amount;
1.) 不分发数据,使用单个reducer:
set mapred.reduce.tasks=1;
2.) 修改Partitioner,这种方法可以做到全排序。这里可以使用Hadoop自带的TotalOrderPartitioner(来自于Yahoo!的TeraSort项目),这是一个为了支持跨reducer分发有序数据开发的Partitioner,它需要一个SequenceFile格式的文件指定分发的数据区间。如果我们已经生成了这一文件(存储在/tmp/range_key_list,分成100个reducer),可以将上述查询改写为
set mapred.reduce.tasks=100; set hive.mapred.partitioner=org.apache.hadoop.mapred.lib.TotalOrderPartitioner; set total.order.partitioner.path=/tmp/ range_key_list; Select sale_id, amount from t_order Cluster by sale_id Sort by amount;
CREATE TABLE if not exists t_sale ( id int, name string, loc string );
则生成按sale_id分发的区间文件的方法是:
create external table range_keys(sale_id int) row format serde 'org.apache.hadoop.hive.serde2.binarysortable.BinarySortableSerDe' stored as inputformat 'org.apache.hadoop.mapred.TextInputFormat' outputformat 'org.apache.hadoop.hive.ql.io.HiveNullValueSequenceFileOutputFormat' location '/tmp/range_key_list'; insert overwrite table range_keys select distinct sale_id from source t_sale sampletable(BUCKET 100 OUT OF 100 ON rand()) s sort by sale_id;