大資料5.1 - hive離線分析

項羽齊發表於2018-04-08

1.1 分桶

1.1.1 分桶表概念

  分割槽和分桶可以同時,分桶是更細粒度的分配方式。分割槽是追求效率,分桶又解決什麼問題呢?海量資料的分開儲存。

  對於每一個表(table)或者分割槽, Hive可以進一步組織成桶,也就是說桶是更為細粒度的資料範圍劃分。Hive也是針對某一列進行桶的組織。Hive採用對列值雜湊,然後除以桶的個數求餘的方式決定該條記錄存放在哪個桶當中。

  把表(或者分割槽)組織成桶(Bucket)有兩個理由:

  獲得更高的查詢處理效率。桶為表加上了額外的結構,Hive 在處理有些查詢時能利用這個結構。具體而言,連線兩個在(包含連線列的)相同列上劃分了桶的表,可以使用 Map 端連線 (Map-side join)高效的實現。比如JOIN操作。對於JOIN操作兩個表有一個相同的列,如果對這兩個表都進行了桶操作。那麼將儲存相同列值的桶進行JOIN操作就可以大大減少JOIN的資料量。

  使取樣(sampling)更高效。在處理大規模資料集時,在開發和修改查詢的階段,如果能在資料集的一小部分資料上試執行查詢,會帶來很多方便。

1.1.2 建立分桶表

建立帶桶的表:

use jtdb;

create table tb_teacher(id int,name string) clustered by (id) into 4 buckets row format delimited fields terminated by ',';

預設桶功能是關閉的

Hive的底層是MapReduce,預設Reduce就一個,就只能輸出一個位置,強制多個 reduce 進行輸出。

hive> set hive.enforce.bucketing=true;

teacher.txt

1,王海濤

2,花倩

3,張慎正

4,齊雷

5,劉昱江

6,陳子樞

分桶表不允許直接從外部匯入資料,先建立臨時表,通過臨時表把資料插過去

hive> create table tb_teacher_tmp(id int,name string) row format delimited fields terminated by ',';

hive> load data local inpath '/usr/local/src/teacher.txt' into table tb_teacher_tmp;

檢視資料

hive> select * from tb_teacher_tmp;

OK

1 王海濤

2 花倩

3 張慎正

4 齊雷

5 劉昱江

6 陳子樞

Time taken: 0.728 seconds, Fetched: 6 row(s)

hive>

假設我們現在的tb_teacher_tmp表的資料太多了,這時就可以用分桶。

匯入資料

hive> insert overwrite table tb_teacher select * from tb_teacher_tmp;

注意分桶時會產生多個reduce,時間會比較慢,稍等片刻

 

 

1.1.3 存放結構

 

 

1.1.4 資料塊取樣

所謂桶其實就是產生了不同的檔案,tablesample為樣本

table_sample: TABLESAMPLE (BUCKET x OUT OF y [ON colname])  

colname上分桶的行隨機進入1y個桶中,返回屬於桶x的行。

例如:

hive> select * from tb_teacher tablesample(bucket 1 out of 4 on id);

hive> select * from tb_teacher tablesample(bucket 1 out of 2 on id);

 

按百分比返回資料返回一半資料

hive> select * from tb_teacher tablesample(50 percent);

1.2 利用hive進行資料的離線分析

1.2.1 準備環境

如果重啟了伺服器,需要重新啟動各服務

 

程式

路徑

啟動命令

 

ZooKeeper

/usr/local/src/zk

/zookeeper-3.4.8/bin

./zkServer.sh start

./zkServer.sh status

 

Hadoop+HDFS

/usr/local/src/hadoop

/hadoop-2.7.1/sbin

Start-all.sh

 

Flume

/usr/local/src/flume

/apache-flume-1.6.0-bin/conf

下面有,暫時不用啟動

 

啟動專案

必須在Flume後面啟動

啟動jtlogserver專案

 

Hive

/usr/local/src/hive

/apache-hive-1.2.0-bin/bin

./hive

檢查防火牆狀態

[root@hadoop01 ~]# firewall-cmd --state #檢查防火牆狀態

not running #返回值,未執行

關閉防火牆

systemctl stop firewalld.service #關閉防火牆服務

systemctl disable firewalld.service #禁止防火牆開啟啟動

檢查程式

[root@hadoop01 bin]# jps

3034 ResourceManager #yarn

2882 SecondaryNameNode #Hadoop

4591 Application #Flume

2713 DataNode #Hadoop

5772 QuorumPeerMain #ZooKeeper

2586 NameNode #Hadoop

1.2.2 資料清洗

由於資料的來源和格式的不同和欄位的缺失等等問題,在處理資料之前,對資料要進行加工處理,這個過程被稱為資料的清洗。保障資料的格式統一,欄位不會有明顯的缺失。

先要跟業務人員溝通,基於業務處理這些資料。

資料量大就用MR進行清洗,資料量小可以用SQLHive進行清洗,也可以用shell指令碼進行清洗。方式很多,哪種都可以,根據實際情況進行處理。

 

並不是所有資料都需要,可以先去除,只保留url+urlname+uvid+ssid+sscount+sstime+cip

1.2.3 建立外部分割槽表

1.2.3.1 業務系統處理過程

利用外部表的方式,由瀏覽器訪問頁面從而觸發埋點js,從而呼叫logServlet,從而寫log4j日誌資訊到flume中,flume又直接寫入到HDFS中,然後hive對指定的目錄建立外部表。從而hive就獲取到使用者的網站流量日誌資訊。

1.2.3.2 修改配置檔案

利用flume的攔截器在heads中新增時間戳,這樣就可以獲取並格式化這個時間戳%Y-%m-%d,還要注意hive指定的外部表路徑一致(建立動態的路徑)。

 

flume-jt.properties

a1.sources.r1.interceptors = t1

a1.sources.r1.interceptors.t1.type = timestamp

 

a1.sinks.k1.type = hdfs

a1.sinks.k1.hdfs.path = hdfs://hadoop01:9000/flux/reportTime=%Y-%m-%d

 

注意:如果是配置的Hadoop HA,則對應flume要做調整

先要將hadoop叢集的core-site.xmlhdfs-site.xml放在flumeconf目錄下,然後訪問時要寫成hdfs://nsns為在hdfs-site.xml中定義的

<property>

<name>dfs.nameservices</name>

<value>ns</value>

</property>

修改地址為:

a1.sinks.k1.hdfs.path = hdfs://ns/flux/reportTime=%Y-%m-%d

1.2.3.3 啟動flumeagent

cd /usr/local/src/flume/apache-flume-1.6.0-bin/conf

[root@hadoop01 conf]# ../bin/flume-ng agent -c ./ -f ./flume-jt.properties -n a1 -Dflume.root.logger=INFO,console &

引數說明:

-c --conf配置目錄 ./當前路徑

-f --config-file 配置檔案

-n –name 指定agent的名稱

1.2.3.4 建立外部分割槽表

cd /usr/local/src/hive/apache-hive-1.2.0-bin/bin

./hive #啟動hive

hive> create database jtlogdb;

hive> use jtlogdb;

hive> create external table flux (url string,urlname string,title string,chset string,src string,col string,lg string, je string,ec string,fv string,cn string,ref string,uagent string,stat_uv string,stat_ss string,cip string) partitioned by (reportTime string) row format delimited fields terminated by '|' location '/flux';

1.1.1.1 訪問頁面a.jsp

 

 

1.2.3.5 查詢資料

hive> select * from flux;

查詢不到資料,因為分割槽資訊還不能識別。

1.2.3.6 增加分割槽資訊

hive> alter table flux add partition (reportTime='2018-03-01') location '/flux/reportTime=2018-03-01';

hive> select * from flux;

 

注意:Xshell會自動在最下面加資訊, 執行後面命令時刪除即可。

 

配置錯誤可以刪除重新配置:

alter table flux drop partition(reportTime='2018-03-01');

1.2.3.7 刪除HDFS重來

前面測試,會導致資料沒有按照我們設定的標準,不方便檢視,刪除重來。

刪除HDFS目錄

./hdfs dfs -rm -r /fluxx/reportTime=2018-03-01

刪除分割槽:

alter table flux drop partition (reportTime='2018-03-01');

1.2.3.8 訪問頁面

資料有點少

先清除IE瀏覽器快取

 

 

 

注意:訪問地址不能用localhost,前面已經介紹過,IE瀏覽器處理特殊localhost再次請求時不會傳送引數,所以必須用IP地址或者域名。

3a

 

2b

 

 

 

 

 

不同的瀏覽器多訪問幾次,訪問2a.jsp,再訪問1b.jsp,把谷歌瀏覽器關掉(當瀏覽器關閉,設定ss_id會話就關閉,cookie臨時檔案就被刪除,如果再次開啟就是一個新會話),開啟訪問1b.jsp,共計9條訪問。

 

 

注意:Flume不是一條就立即形成一個hdfs檔案,而是有一定的緩衝或者時間。所以造成FlumeData HDFS檔案中資料的條數不一樣。

 

1.1.1.1 查詢資料

hive> select * from flux;

1.1.1.1 注意事項

一定要等所有資料寫完,再建立外部分割槽表!!

這裡是一個大大的坑flumehfds過程中,我們不能建立外部表,一旦建立flume後面寫入的資料就不會出現在外部表中。就會造成hdfs的記錄數和外部表中的記錄數不一致。

怎麼解決呢?一般每天凌晨兩點時,無人訪問時,我們再建立外部表;或者發現記錄數不一致時,重新建立外部表。

drop table fluxx;

drop table dataclear;

1.2.4 建立資料庫倉庫

1.1.1.2 建立新表

hive> create table dataclear(reportTime string,url string,urlname string,uvid string,ssid string,sscount string,sstime string,cip string) row format delimited fields terminated by '|';

表名:dataclear

欄位:型別就都設定為stringsscount也設定為string

row formate delimited fileds terminated by ‘|’用下劃線分割資料

hive> show tables;

OK

dataclear

flux

Time taken: 0.023 seconds, Fetched: 2 row(s)

hive>

 

把一個欄位拆分成3個欄位,利用hive的內建函式split進行拆分

hive> select stat_ss from flux limit 1; #方便廁所只取一條

OK

7447647975_5_1519085238365

Time taken: 0.106 seconds, Fetched: 1 row(s)

 

hive> select stat_ss,split(stat_ss,"_") from flux limit 1;

OK

7447647975_5_1519085238365 ["7447647975","5","1519085238365"]

Time taken: 0.123 seconds, Fetched: 1 row(s)

 

hive> select stat_ss,split(stat_ss,"_"),split(stat_ss,"_")[0] from flux limit 1;

OK

7447647975_5_1519085238365 ["7447647975","5","1519085238365"] 7447647975

Time taken: 0.111 seconds, Fetched: 1 row(s)

1.1.1.3 獲取陣列中的資料

hive> insert overwrite table dataclear select reportTime,url,urlname,

stat_uv,split(stat_ss,"_")[0],split(stat_ss,"_")[1],split(stat_ss,"_")[2],cip from flux;

 

 

執行很慢

 

1.2.5 業務處理資料分析

1.2.5.1 PV

select count(*) as pv from dataclear

 where reportTime='2018-03-01';

實際就是記錄個數

1.2.5.2 UV

獨立訪客數量一天之內所有的訪問的數量一天之內uvid去重後的總數

select count(distinct uvid) as uv from dataclear

 where reportTime='2018-03-01';

不同瀏覽器,結果2

1.2.5.3 VV

獨立會話數,一天之內所有的會話的數量,一天之內ssid去重後的總數

select count(distinct ssid) as vv from dataclear

 where reportTime='2018-03-01';

一個IE瀏覽器,一個chrome瀏覽器,結果2。如果中間可以把瀏覽器快取情況,這樣又是一個新的會話。MR計算時間比較長。

1.2.5.4 BR

跳出率,一天之內跳出的會話總數/會話總數,會話總數就是vv

跳出的會話總數,就是訪問頁面的個數,怎麼計算呢?

hive中所有的子表都必須有別名。

 

select br_taba.a/br_tabb.b as br from

(

  select count(*) as a from

    (

      select ssid from dataclear

       where reportTime='2018-03-01'

       group by ssid having count(ssid)=1

    ) as br_tab

) as br_taba,

(

  select count(distinct ssid) as b from dataclear

   where reportTime='2018-03-01'

) as br_tabb;

 

select br_taba.a/br_tabb.b as br from (select count(*) as a from (select ssid from dataclear where reportTime='2018-03-01' group by ssid having count(ssid)=1) as br_tab) as br_taba,(select count(distinct ssid) as b from dataclear where reportTime='2018-03-01') as br_tabb;

 

轉成6MR過程

 

保留4位小數

 

select round(br_taba.a/br_tabb.b,4) as br from (select count(*) as a from (select ssid from dataclear where reportTime='2018-03-01' group by ssid having count(ssid)=1) as br_tab) as br_taba,(select count(distinct ssid) as b from dataclear where reportTime='2018-03-01') as br_tabb;

分解為6job,在不同伺服器中平行計算,所以非常耗時:

 

1.2.5.5 NewIP

新增ip總數,一天內所有ip去重後在歷史資料中從未出現過的數量

select count(distinct dataclear.cip) from dataclear

 where dataclear.reportTime='2018-03-01'

 and cip not in

 (select dc2.cip from dataclear as dc2

 where dc2.reportTime<'2018-03-01');

 

select count(distinct dataclear.cip) from dataclear where dataclear.reportTime='2018-03-01' and cip not in (select dc2.cip from dataclear as dc2 where dc2.reportTime<'2018-03-01');

就一個IP來訪問,所以結果是1

注意:子查詢中不能和主查詢中有相同欄位名稱,這樣SQL無法區分。所以對子查詢表重新命名dc2,這樣欄位就能判斷其出處。

1.2.5.6 NewCust

新增客戶數一天內所有的uvid去重在歷史資料中從未出現的總數

select count(distinct dataclear.uvid) from dataclear

 where dataclear.reportTime='2018-03-01'

 and uvid not in

 (select dc2.uvid from dataclear as dc2 where

 dc2.reportTime < '2018-03-01');

 

select count(distinct dataclear.uvid) from dataclear where dataclear.reportTime='2018-03-01' and uvid not in (select dc2.uvid from dataclear as dc2 where dc2.reportTime < '2018-03-01');

一個IE一個chrome,結果為2

1.2.5.7 AvgTime

平均訪問時長,一天內所有會話的訪問時長的平均值,一個會話的時長=會話中所有訪問的時間的最大值-會話中所有訪問時間的最小值

select round(avg(atTab.usetime),4) as avgtime from

(

 select max(sstime) - min(sstime) as usetime from dataclear

  where reportTime='2018-03-01'

  group by ssid

) as atTab;

 

select round(avg(atTab.usetime),4) as avgtime from (select max(sstime) -min(sstime) as usetime from dataclear where reportTime='2018-03-01' group by ssid) as atTab;

1.2.5.8 AvgDeep

平均訪問深度,一天內所有會話訪問深度的平均值,一個會話的訪問深度

select round(avg(deep),2) as viewdeep from

(

  select count(distinct urlname) as deep from flux

   where reportTime='2018-03-01'

   group by split(stat_uv,'_')[0]

) as tviewdeep;

 

select round(avg(deep),2) as viewdeep from (select count(distinct urlname) as deep from flux where reportTime='2018-03-01' group by split(stat_uv,'_')[0]) as tviewdeep;

相關文章