一、行轉列的使用
1、問題
hive如何將
a b 1
a b 2
a b 3
c d 4
c d 5
c d 6
變為:
a b 1,2,3
c d 4,5,6
2、數據
test.txt
a b 1
a b 2
a b 3
c d 4
c d 5
c d 6
3、答案
1.建表
drop table tmp_jiangzl_test;
create table tmp_jiangzl_test
(
col1 string,
col2 string,
col3 string
)
row format delimited fields terminated by '\t'
stored as textfile;
load data local inpath '/home/jiangzl/shell/test.txt' into table tmp_jiangzl_test;
2.處理
select col1,col2,concat_ws(',',collect_set(col3))
from tmp_jiangzl_test
group by col1,col2;
二、列轉行
1、問題
hive如何將
a b 1,2,3
c d 4,5,6
變為:
a b 1
a b 2
a b 3
c d 4
c d 5
c d 6
2、答案
1.建表
drop table tmp_jiangzl_test;
create table tmp_jiangzl_test
(
col1 string,
col2 string,
col3 string
)
row format delimited fields terminated by '\t'
stored as textfile;
處理:
select col1, col2, col5
from tmp_jiangzl_test a
lateral view explode(split(col3,',')) b AS col5
請把下一語句用hive方式實現
SELECT a.key,a.value
FROM a
WHERE a.key not in (SELECT b.key FROM b)
答案:
select a.key,a.value from a where a.key not exists (select b.key from b)
簡要描述數據庫中的 null,說出null在hive底層如何存儲,並解釋selecta.* from t1 a left outer join t2 b on a.id=b.id where b.id is null; 語句的含義
null與任何值運算的結果都是null, 可以使用is null、is not null函數指定在其值為null情況下的取值。
null在hive底層默認是用'\N'來存儲的,可以通過alter table test SET SERDEPROPERTIES('serialization.null.format' = 'a');來修改。
查詢出t1表中與t2表中id相等的所有信息。
寫出hive中split、coalesce及collect_list函數的用法(可舉例)
Split將字符串轉化為數組。
split('a,b,c,d' , ',') ==> ["a","b","c","d"]
COALESCE(T v1, T v2, …) 返回參數中的第一個非空值;如果所有值都為 NULL,那么返回NULL。
collect_list列出該字段所有的值,不去重 select collect_list(id) from table;
寫出將 text.txt 文件放入 hive 中 test 表‘2016-10-10’ 分區的語句,test 的分區字段是 l_date。
LOAD DATA LOCAL INPATH '/your/path/test.txt' OVERWRITE INTO TABLE test PARTITION (l_date='2016-10-10')
hive 常用數據清洗函數
1,case when 的利用,清洗諸如評分等的內容,用例如下。
case
when new.comment_grade = '五星商戶' then 50
when new.comment_grade = '准五星商戶' then 45
when new.comment_grade = '四星商戶' then 40
when new.comment_grade = '准四星商戶' then 35
when new.comment_grade = '三星商戶' then 30
when new.comment_grade = '准三星商戶' then 25
when new.comment_grade = '二星商戶' then 20
when new.comment_grade = '准二星商戶' then 15
when new.comment_grade = '一星商戶' then 10
when new.comment_grade = '准一星商戶' then 5
when new.comment_grade = '該商戶暫無星級' then 0
when new.comment_grade is NULL then old.comment_grade
else new.comment_grade
END as `new.comment_grade`,
-- 使用case when行轉列
drop table if exists StudentScores2;
CREATE TABLE StudentScores2
( UserName NVARCHAR(20),
-- 學生姓名 語文 FLOAT,
-- 科目 數學 FLOAT,
-- 科目 英語 FLOAT,
-- 科目 生物 FLOAT -- 科目 );
select UserName,
max(case when subject='語文' then score else 0 end) 語文,
max(case when subject='數學' then score else 0 end) 數學,
max(case when subject='英語' then score else 0 end) 英語,
max(case when subject='生物' then score else 0 end) 生物
from StudentScores group by UserName
列轉行
Sql代碼
create table TEST_TB_GRADE2
(
ID NUMBER(10) not null,
USER_NAME VARCHAR2(20 CHAR),
CN_SCORE FLOAT,
MATH_SCORE FLOAT,
EN_SCORE FLOAT
)
select user_name, '語文' COURSE , CN_SCORE as SCORE from test_tb_grade2
union select user_name, '數學' COURSE, MATH_SCORE as SCORE from test_tb_grade2
union select user_name, '英語' COURSE, EN_SCORE as SCORE from test_tb_grade2
order by user_name,COURSE
-- 建表 CREATE TABLE StudentScores
( UserName NVARCHAR(20),
-- 學生姓名 Subject NVARCHAR(30),
-- 科目 Score FLOAT -- 成績 )
-- 使用union all 列轉行
select UserName,'語文' subject,語文 score from StudentScores2
union all
select UserName,'數學' subject,數學 score from StudentScores2
union all
select UserName,'英語' subject,英語 score from StudentScores2
union all
select UserName,'生物' subject,生物 score from StudentScores2;
2, 替換字符串中的一些內容。
regexp_replace(new.avg_price, '-', '')
替換 avg_price 中的中划線。
3, 字符串切分函數
split(a.tag_flag, '>')[1],
具體例子:
select split('a,b', ',')[0] ===> 結果 a
4, 字符串拼接函數
SELECT concat('1', '2'); ====》 結果 12
SELECT concat('1', '2', '3'); ===> 結果 123
5, 去除字符串兩端空格
trim(a.city)
6, 使用left join 或者 right join 補全數據
例如根據兩張表,其中一張表格table2含有省份和城市的信息,
其中一張表table1只有城市信息,需要補全table1 中的省份信息,可以像如下做法:
select
a.name,
b.province,
a.city
from table1 a left join table2 b on a.city = b.city;
7,其他:清除一些不符合條件的數據
可以使用等值判斷來處理數據
清除一些不符合條件的數據。
INSERT OVERWRITE table ods.js_beauty_tmp
SELECT *
from ods.js_beauty_tmp
WHERE map_lat != ''
AND map_lng != ''
AND map_lat IS NOT NULL
AND map_lng IS NOT NULL
AND map_lat != 0
AND map_lng != 0
AND map_lat not like '-%'
AND map_lng not like '-%'
and city != '其他城市'
and city != '點評實驗室';
hive常用函數
去空格函數:trim
語法: trim(string A)。去除字符串兩邊的空格
舉例:select trim(' abc ') from dual; ##返回值為abc
正則表達式替換函數:regexp_replace
語法: regexp_replace(string A, string B, string C)。將字符串A中的符合java正則表達式B的部分替換為C。
舉例:select regexp_replace('foobar', 'oo|ar', '') from dual; ##返回值為fb
正則表達式解析函數:regexp_extract
語法: regexp_extract(string subject, string pattern, int index)。將字符串subject按照pattern正則表達式的規則拆分,返回index指定的字符。
舉例:select regexp_extract('foothebar', 'foo(.*?)(bar)', 1) from dual; ##返回值為the
分割字符串函數: split
語法: split(string str, string pat)。按照pat字符串分割str,會返回分割后的字符串數組
舉例:select split('abtcdtef','t') from dual; ##返回值為["ab","cd","ef"]
集合查找函數: find_in_set
語法: find_in_set(string str, string strList)。返回str在strlist第一次出現的位置,strlist是用逗號分割的字符串。如果沒有找該str字符,則返回0
舉例:select find_in_set('ab','ef,ab,de') from dual; ##返回值為2
字符串截取函數:substr,substring
語法: substr(string A, int start),substring(string A, int start)。返回字符串A從start位置到結尾的字符串
舉例:select substr('abcde',3) from dual; ##返回值cde
字符串轉大寫函數:upper,ucase
語法: upper(string A) ucase(string A)。返回字符串A的大寫格式
字符串轉小寫函數:lower,lcase
語法: lower(string A) lcase(string A)。返回字符串A的小寫格式
帶分隔符字符串連接函數:concat_ws
語法: concat_ws(string SEP, string A, string B…)。返回輸入字符串連接后的結果,SEP表示各個字符串間的分隔符
舉例:select concat_ws(',','abc','def','gh') from dual; ##返回值為abc,def,gh
lateral view 函數
語法:lateral view udtf()說明:當表中某個字段的取值為列表或數組時,利用該函數和split、explode可以將一行數據拆分成多行數據,在此基礎上對拆分之后的數據進行聚合,
用來解決輸入一行輸出多行(On-to-many maping) 的需求。舉例:select deal_id,type,sp from deal_ppt_mark_log t lateral view explode(split(t.sources,','))a as sp
row_number() 和rank()
語法: row_number() over (partition by col_list1 order by col_list2) rank() over(partition by col_list1 order by col_list2)
說明:首先根據col_list1分組,在分組內部根據col_list2排序,row_number()函數計算的值表示每組內部排序后的順序編號,組內連續的唯一的;rank() over()函數則是跳躍排序,序號不唯一,即當有數據值值相同時,並列,
當遇到不同的數據,其序號為上一個數據的序號加上該序號的個數。如兩個並列第一,序列號為(1,1,3.......)。
select id,age,name,sex from (select id,age,name,sex,row_number() over(partition by sex order by age desc) as rank from t_rownumber) tmp where rank<=2;
類型轉換函數
日期和浮點型轉換
select id,to_date(birthday as date) as bir,cast(salary as float) from t_fun;
時間函數
select cast(current_timestamp as date);
unix時間戳轉字符串
select from_unixtime(unix_timestamp(),"yyyy/MM/dd HH:mm:ss");
行轉列函數:explode()
select explode(subjects) as sub from t_stu_subject
hive 窗口分析函數
select * from t_access;
使用Hive做數據清洗,經常需要使用正則表達式。
比較討厭的是,正則表達式匹配失敗的時候,hive完全不會報錯。
Hive使用regexp,RLIKE需要使用轉義字符
原來的寫法
SELECT * from ahhs_product_info where product_name NOT RLIKE '([\u4e00-\u9fa5])+' ;
在hive里面的寫法
SELECT * from ahhs_product_info where product_name NOT RLIKE '([\\u4e00-\\u9fa5])+' ;
Hive中rlike,like,not like區別與使用詳解
like的使用詳解
A like B只能使用簡單匹配符號 _%,”_”表示任意單個字符,字符”%”表示任意數量的字符
like的匹配是按字符逐一匹配的,使用B從A的第一個字符開始匹配,所以即使有一個字符不同都不行
RLIKE比較符
A RLIKE B ,B中的表達式可以使用JAVA中全部正則表達式,具體正則規則參考java,或者其他標准正則語法
select 1 from t_fin_demo where 'footbar’ rlike '^f.*r$’
轉換要求:行轉列需保留列名,
方式一:采用union all的形式
select
dt_month
,'valid_num' as type
,sum(valid_num) as num
from temp.temp_xw_rowtocol
group by dt_month
union all
select
dt_month
,'unvalid_num' as type
,sum(unvalid_num) as num
from temp.temp_xw_rowtocol
group by dt_month
方式二:使用lateral view和str_to_map
select
a.dt_month
,add_t.type
,add_t.num
from temp.temp_xw_rowtocol a
lateral view explode(str_to_map(concat('valid_num=',valid_num
,'&unvalid_num=',unvalid_num
),'&','='
)
) add_t as type,num
列轉行
通過group by
select
a.dt_month
,sum(case when type = 'valid_num' then num end) as valid_num
,sum(case when type = 'unvalid_num' then num end) as unvalid_num
from temp.temp_xw_coltorow a
group by a.dt_month
一、求單月訪問次數和總訪問次數
1、數據說明
數據字段說明
用戶名,月份,訪問次數
數據格式
A,2015-01,5
A,2015-01,15
B,2015-01,5
A,2015-01,8
B,2015-01,25
A,2015-01,5
A,2015-02,4
A,2015-02,6
B,2015-02,10
B,2015-02,5
A,2015-03,16
A,2015-03,22
B,2015-03,23
B,2015-03,10
B,2015-03,1
最后結果展示
用戶 月份 最大訪問次數 總訪問次數 當月訪問次數
A 2015-01 33 20 20
A 2015-02 33 43 10
A 2015-03 38 81 38
B 2015-01 30 30 30
B 2015-02 30 45 15
B 2015-03 44 89 44
2、數據准備
(1)創建表
use myhive;
create external table if not exists t_access(
uname string comment ‘用戶名’,
umonth string comment ‘月份’,
ucount int comment ‘訪問次數’
) comment ‘用戶訪問表’
row format delimited fields terminated by “,”
location “/hive/t_access”;
(2)導入數據
load data local inpath “/home/hadoop/access.txt” into table t_access;
(3)驗證數據
select * from t_access;
3、結果需求
現要求出:
每個用戶截止到每月為止的最大單月訪問次數和累計到該月的總訪問次數
4、需求分析
此結果需要根據用戶+月份進行分組
(1)先求出當月訪問次數
–求當月訪問次數
create table tmp_access(
name string,
mon string,
num int
);
insert into table tmp_access
select uname,umonth,sum(ucount)
from t_access t group by t.uname,t.umonth;
select * from tmp_access;
(2)tmp_access進行自連接視圖
create view tmp_view as
select a.name anme,a.mon amon,a.num anum,b.name bname,b.mon bmon,b.num bnum from tmp_access a join tmp_access b
on a.name=b.name;
select * from tmp_view;
(3)進行比較統計
select anme,amon,anum,max(bnum) as max_access,sum(bnum) as sum_access
from tmp_view
where amon>=bmon
group by anme,amon,anum;
二、學生課程成績
1、說明
use myhive;
CREATE TABLE course (
id int,
sid int ,
course string,
score int
) ;
// 插入數據
// 字段解釋:id, 學號, 課程, 成績
INSERT INTO course VALUES (1, 1, ‘yuwen’, 43);
INSERT INTO course VALUES (2, 1, ‘shuxue’, 55);
INSERT INTO course VALUES (3, 2, ‘yuwen’, 77);
INSERT INTO course VALUES (4, 2, ‘shuxue’, 88);
INSERT INTO course VALUES (5, 3, ‘yuwen’, 98);
INSERT INTO course VALUES (6, 3, ‘shuxue’, 65);
2、需求
求:所有數學課程成績 大於 語文課程成績的學生的學號
1、使用case…when…將不同的課程名稱轉換成不同的列
create view tmp_course_view as
select sid, case course when “shuxue” then score else 0 end as shuxue,
case course when “yuwen” then score else 0 end as yuwen from course;
select * from tmp_course_view;
2、以sid分組合並取各成績最大值
create view tmp_course_view1 as
select aa.sid, max(aa.shuxue) as shuxue, max(aa.yuwen) as yuwen from tmp_course_view aa group by sid;
select * from tmp_course_view1;
3、比較結果
select * from tmp_course_view1 where shuxue > yuwen;
三、求每一年最大氣溫的那一天 + 溫度
1、說明
數據格式
2010012325
具體數據
2010012325表示在2010年01月23日的氣溫為25度
2、 需求
比如:2010012325表示在2010年01月23日的氣溫為25度。現在要求使用hive,計算每一年出現過的最大氣溫的日期+溫度。
要計算出每一年的最大氣溫。我用
select substr(data,1,4),max(substr(data,9,2)) from table2 group by substr(data,1,4);
出來的是 年份 + 溫度 這兩列數據例如 2015 99
但是如果我是想select 的是:具體每一年最大氣溫的那一天 + 溫度 。例如 20150109 99
請問該怎么執行hive語句。。
group by 只需要substr(data,1,4),
但是select substr(data,1,8),又不在group by 的范圍內。
是我陷入了思維死角。一直想不出所以然。。求大神指點一下。
在select 如果所需要的。不在group by的條件里。這種情況如何去分析?
3、解析
(1)創建一個臨時表tmp_weather,將數據切分
create table tmp_weather as
select substr(data,1,4) years,substr(data,5,2) months,substr(data,7,2) days,substr(data,9,2) temp from weather;
select * from tmp_weather;
(2)創建一個臨時表tmp_year_weather
create table tmp_year_weather as
select substr(data,1,4) years,max(substr(data,9,2)) max_temp from weather group by substr(data,1,4);
select * from tmp_year_weather;
(3)將2個臨時表進行連接查詢
select * from tmp_year_weather a join tmp_weather b on a.years=b.years and a.max_temp=b.temp;
四、求學生選課情況
1、數據說明
(1)數據格式
id course
1,a
1,b
1,c
1,e
2,a
2,c
2,d
2,f
3,a
3,b
3,c
3,e
(2)字段含義
表示有id為1,2,3的學生選修了課程a,b,c,d,e,f中其中幾門。
2、數據准備
(1)建表t_course
create table t_course(id int,course string)
row format delimited fields terminated by “,”;
(2)導入數據
load data local inpath “/home/hadoop/course/course.txt” into table t_course;
3、需求
編寫Hive的HQL語句來實現以下結果:表中的1表示選修,表中的0表示未選修
id a b c d e f
1 1 1 1 0 1 0
2 1 0 1 1 0 1
3 1 1 1 0 1 0
4、解析
第一步:
select collect_set(course) as courses from id_course;
第二步:
set hive.strict.checks.cartesian.product=false;
create table id_courses as select t1.id as id,t1.course as id_courses,t2.course courses
from
( select id as id,collect_set(course) as course from id_course group by id ) t1
join
(select collect_set(course) as course from id_course) t2;
啟用嚴格模式:hive.mapred.mode = strict // Deprecated
hive.strict.checks.large.query = true
該設置會禁用:1. 不指定分頁的orderby
2. 對分區表不指定分區進行查詢
3. 和數據量無關,只是一個查詢模式
hive.strict.checks.type.safety = true
嚴格類型安全,該屬性不允許以下操作:1. bigint和string之間的比較
2. bigint和double之間的比較
hive.strict.checks.cartesian.product = true
該屬性不允許笛卡爾積操作
第三步:得出最終結果:
思路:
拿出course字段中的每一個元素在id_courses中進行判斷,看是否存在。
select id,
case when array_contains(id_courses, courses[0]) then 1 else 0 end as a,
case when array_contains(id_courses, courses[1]) then 1 else 0 end as b,
case when array_contains(id_courses, courses[2]) then 1 else 0 end as c,
case when array_contains(id_courses, courses[3]) then 1 else 0 end as d,
case when array_contains(id_courses, courses[4]) then 1 else 0 end as e,
case when array_contains(id_courses, courses[5]) then 1 else 0 end as f
from id_courses;
五、求月銷售額和總銷售額
1、數據說明
(1)數據格式
a,01,150
a,01,200
b,01,1000
b,01,800
c,01,250
c,01,220
b,01,6000
a,02,2000
a,02,3000
b,02,1000
b,02,1500
c,02,350
c,02,280
a,03,350
a,03,250
(2)字段含義
店鋪,月份,金額
2、數據准備
(1)創建數據庫表t_store
use class;
create table t_store(
name string,
months int,
money int
)
row format delimited fields terminated by “,”;
(2)導入數據
load data local inpath “/home/hadoop/store.txt” into table t_store;
3、需求
編寫Hive的HQL語句求出每個店鋪的當月銷售額和累計到當月的總銷售額
4、解析
(1)按照商店名稱和月份進行分組統計
create table tmp_store1 as
select name,months,sum(money) as money from t_store group by name,months;
select * from tmp_store1;
(2)對tmp_store1 表里面的數據進行自連接
create table tmp_store2 as
select a.name aname,a.months amonths,a.money amoney,b.name bname,b.months bmonths,b.money bmoney from tmp_store1 a
join tmp_store1 b on a.name=b.name order by aname,amonths;
select * from tmp_store2;
(3)比較統計
select aname,amonths,amoney,sum(bmoney) as total from tmp_store2 where amonths >= bmonths group by aname,amonths,amoney;
Hive sql編寫優化總結
Hive是將符合SQL語法的字符串解析生成可以在Hadoop上執行的MapReduce的工具。使用Hive盡量按照分布式計算的一些特點來設計sql,和傳統關系型數據庫有區別,
所以需要去掉原有關系型數據庫下開發的一些固有思維。
基本原則:
1:盡量盡早地過濾數據,減少每個階段的數據量,對於分區表要加分區,同時只選擇需要使用到的字段
select … from A
join B
on A.key = B.key
where A.userid>10
and B.userid<10
and A.dt=‘20120417’
and B.dt=‘20120417’;
應該改寫為:
select … from (select … from A
where dt=‘201200417’
and userid>10
) a
join ( select … from B
where dt=‘201200417’
and userid < 10
) b
on a.key = b.key;
2、有小表進行join,可以使用map join
3:盡量原子化操作,盡量避免一個SQL包含復雜邏輯
可以使用中間表來完成復雜的邏輯
4 jion操作 小表要注意放在join的左邊,否則會引起磁盤和內存的大量消耗
5:如果union all的部分個數大於2,或者每個union部分數據量大,應該拆成多個insert into 語句,實際測試過程中,執行時間能提升50%
insert overwite table tablename partition (dt= …)
select … from (
select … from A
union all
select … from B
union all
select … from C
) R
where …;
可以改寫為:
insert into table tablename partition (dt= …)
select … from A
WHERE …;
insert into table tablename partition (dt= …)
select … from B
WHERE …;
insert into table tablename partition (dt= …)
select … from C
WHERE …;
5:寫SQL要先了解數據本身的特點,如果有join ,group操作的話,要注意是否會有數據傾斜
如果出現數據傾斜,應當做如下處理:
set hive.exec.reducers.max=200;
set mapred.reduce.tasks= 200;—增大Reduce個數
set hive.groupby.mapaggr.checkinterval=100000 ;–這個是group的鍵對應的記錄條數超過這個值則會進行分拆,值根據具體數據量設置
set hive.groupby.skewindata=true; --如果是group by過程出現傾斜 應該設置為true
set hive.skewjoin.key=100000; --這個是join的鍵對應的記錄條數超過這個值則會進行分拆,值根據具體數據量設置
set hive.optimize.skewjoin=true;–如果是join 過程出現傾斜 應該設置為true
合理設置reduce個數
reduce個數過少沒有真正發揮hadoop並行計算的威力,但reduce個數過多,會造成大量小文件問題,數據量、資源情況只有自己最清楚,找到個折衷點,
2、讓服務器盡量少做事情,走最優的路徑,以資源消耗最少為目標
比如:
(1) 注意join的使用
若其中有一個表很小使用map join,否則使用普通的reduce join,注意hive會將join前面的表數據裝載內存,所以較小的一個表在較大的表之前,減少內存資源的消耗
(2)注意小文件的問題
在hive里有兩種比較常見的處理辦法
第一是使用Combinefileinputformat,將多個小文件打包作為一個整體的inputsplit,減少map任務數
set mapred.max.split.size=256000000;
set mapred.min.split.size.per.node=256000000
set Mapred.min.split.size.per.rack=256000000
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat
第二是設置hive參數,將額外啟動一個MR Job打包小文件
hive.merge.mapredfiles = false 是否合並 Reduce 輸出文件,默認為 False
hive.merge.size.per.task = 25610001000 合並文件的大小
(3)注意數據傾斜
在hive里比較常用的處理辦法:
Group By
默認情況下,Map階段同一Key數據分發給一個reduce,當一個key數據過大時就傾斜了。並不是所有的聚合操作都需要在Reduce端完成,很多聚合操作都可以先在Map端進行部分聚合,最后在Reduce端得出最終結果。
開啟Map端聚合參數設置
(1)是否在Map端進行聚合,默認為True hive.map.aggr = true
(2)在Map端進行聚合操作的條目數目 hive.groupby.mapaggr.checkinterval = 100000
(3)有數據傾斜的時候進行負載均衡(默認是false) hive.groupby.skewindata = true
Count(Distinct) 去重統計
數據量小的時候無所謂,數據量大的情況下,由於COUNT DISTINCT操作需要用一個Reduce Task來完成,這一個Reduce需要處理的數據量太大,就會導致整個Job很難完成,一般COUNT DISTINCT使用先GROUP BY再COUNT的方式替換
設置5個reduce個數 set mapreduce.job.reduces = 5;
執行去重id查詢 select count(distinct id) from bigtable; 調整為 select count(id)from (select id from bigtable groupby id) a;
數據傾斜:
hive在跑數據時經常會出現數據傾斜的情況,使的作業經常reduce完成在99%后一直卡住,最后的1%花了幾個小時都沒跑完,
這種情況就很可能是數據傾斜的原因,解決方法要根據具體情況來選擇具體的方案
大量經驗表明數據傾斜的原因是人為的建表疏忽或業務邏輯可以規避的
解決思路:
Hive的執行是分階段的,map處理數據量的差異取決於上一個stage的reduce輸出,所以如何將數據均勻的分配到各個reduce中,就是解決數據傾斜的根本所在
典型的業務場景
場景:如日志中,常會有信息丟失的問題,比如日志中的 user_id,如果取其中的 user_id 和 用戶表中的user_id 關聯,會碰到數據傾斜的問題
解決方法1: user_id為空的不參與關聯
select * from log a
join users b
on a.user_id is not null
and a.user_id = b.user_id
union all
select * from log a
where a.user_id is null;
解決方法2 :賦與空值分新的key值
select *
from log a
left outer join users b
on case when a.user_id is null then concat(‘hive’,rand() ) else a.user_id end = b.user_id;
ETL中如何替換distinct?
案例1如下:
hive的sql語句,執行效率特別慢,跑了一個多小時程序只是map完了,reduce進行到20%。
該Hive語句如下:
select count(distinct ip)
from (select ip as ip from comprehensive.f_client_boot_daily where year="2013" and month="10"
union all
select pub_ip as ip from f_app_boot_daily where year="2013" and month="10"
union all select ip as ip from format_log.format_pv1 where year="2013" and month="10" and url_first_id=1
) d
分析
總的數據量大約30億條。這么大的數據量,使用disticnt函數,所有的數據只會shuffle到一個reducer上,導致reducer數據傾斜嚴重。
解決辦法:
通過使用groupby,按照ip進行分組。改寫后的sql語句如下:
select count(*)
from
(select ip from (select ip as ip from comprehensive.f_client_boot_daily where year="2013" and month="10"
union all
select pub_ip as ip from f_app_boot_daily where year="2013" and month="10"
union all select ip as ip from format_log.format_pv1 where year="2013" and month="10" and url_first_id=1
) d
group by ip ) b
合理的設置reducer數量,將數據分散到多台機器上。set mapred.reduce.tasks=50;
經過優化后,速度提高非常明顯。整個作業跑完大約只需要20多分鍾的時間。
案列2如下:
用exists代替distinct
select distinct d.dp_id,d.dp_name from depts d,staffs s where d.dp_id=s.dp_id;
用exists實現如下:
select d.dp_id,d.dp_name from depts d where exists(select null from staffs s where s.dp_id=d.dp_id)
exists語句用來判斷()內的表達式是否存在返回值,如果存在就返回true,如果不存在就返回false,所以在上面語句中我們使用select null,因為返回什么數據不重要,重要有值返回就行。
另外exists的有點是,它只要括號中的表達式有一個值存在,就立刻返回true,而不用遍歷表中所有的數據。
項目中使用什么調度器?
Yarn中有三種調度器可以選擇:FIFO Scheduler
(先進先出),Capacity Scheduler(容器調度)
,FairScheduler(公平調度)
。
工作流調度器一般有 azkaban 和 Oozie ,一般項目組會針對這兩種自定義封裝后使用
就是用JAVA的上傳本地文件的方式上傳腳本 創建任務 寫好調度
跑任務的腳本平台組已經寫好了 把自己的腳本和跑任務的shell腳本一起打包就好
如何確保ETL過程數據的一致性和准確性?
這個准確性包含兩個方面:數據量的准確性,數值的正確性
數值的正確性
1.如果 ETL 處理的表比較多,那么除了用監控工具之外,我想不到有什么其它好的方法,或者可以寫寫過程之類的,讓它定期運行,獲取你關心的一些數據指標。
2.在 ETL 的過程中不對數值型數據據進行四舍五入操作,可有效防止數據精度不丟失,至於客戶想看到多少位的精度,我們可以在前端展現中對數據進行格式化。為了讓業務人員能更好的理解指標的運算過程,我們一般在展現層以文字或在線幫助的形式對可能會引起歧義的指標給出后台的計算公式,幫助用戶更好的理解指標含義
3.對照業務系統數據記錄條數和導入之后的記錄條數進行比較,一致應該是准確的,不過 DW 中的表要建好主鍵約束
4.業務系統會有一個時間戳,從昨天的時間戳重新抽取
測試:
轉換規則測試
首先是數據格式的合法性。對於數據源中時間、數值、字符等數據的處理,是否符合數據倉庫規則,是否進行統一的轉換。
其次是值域的有效性。是否有超出維表或者業務值域的范圍。
第三是空值的處理。是否捕獲字段空值,或者需要對空值進行替換為其他含義值的處理。
第四是主鍵的有效性。主鍵是否唯一。
第五是亂碼的檢查。特殊符號或者亂碼符號的護理規則。
第六是臟數據的處理。比如不符合業務邏輯的數據
關鍵字段測試
通過轉換規則,查詢關鍵字段是否正確。比如保費收入字段,看其是否乘以匯率,共保比率等.
數據量:
全量抽取,在抽取完成后 馬上比對數據的行數 全量加載一般是先清空再插入(truncate and insert)
增量抽取,增量時間的記錄行數 進行比對 增量加載一般是先刪后插(delete and insert)
測試:
做好源表和目標表數據量統計對比