实践Hive的点点滴滴
實踐Hive
- 遇見問題
- 簡單概念
- 可以怎么用?
- 簡單客戶端命令
- 創建Hive表
- hive創建無分區內部表
- hive創建分區內部表
- hive創建分區
- hive創建外部表
- 新增、加載數據
- csv文件上傳數據到Hive
- 備份數據
- 遷移到MySQL
- 查詢插入
- 查詢
- hive查詢分區
- hive查詢簡單優化
- 分區表的left join 優化
- 更新
- hive新增表字段
- hive表數據去重(推薦方案2)
- 其他更新操作
- 刪除
- hive刪除分區
- 真正刪除
- 刪庫跑路
遇到問題,解決問題。技術故事往往起于此。
遇見問題
? ? 如果業務突飛猛進,數據量猛增,出于對成本的控制,或許要考慮一下大數據組件了,To C的業務和To B的業務往往存在很大的差距。如何用當前可承受成本拿出適合的解決方案是首要思考任務。
? ? 我需要對相當大的表做查詢,可是直接從生產MySQL上查詢無疑會增加它的壓力。數據量越來越多,MySQL使用成本逐漸增高,現在我需要尋求更好的解決方案。
首先想到的是Hadoop體系,它的解決方案是合理利用多個廉價的服務器來完成我的計算任務,這樣我就不必花費大價錢買超高性能服務器。這就是Hadoop希望的``在這之前你可能會考慮以下問題。
簡單概念
? ? 前面已經知道數據是存在HDFS里的。而Hive并不算是一個完整的數據庫,它的核心功能是實現將SQL轉換為MapReduce的任務進行運算。這使得完全不會Hadoop的同學,只要會寫寫SQL語句即可完成一些大數據離線查詢計算功能。我們稱這個SQL語句叫HiveSQL或HSQL,別擔心,這不是一個新的語言,他和MYSQL語句差不了多少。
? ?不過遺憾是,你不能像使用MySQL一樣操作Hive。也沒有MySQL給你提供的引擎查詢優化。不像普通的關系型數據庫,大數據通常不允許做行級的記錄增刪改操作。Hive一般是用作于維護海量的數據,對靜態的數據慢慢的做分析,形成報表啊啥的。如果你的業務需要實時修改數據,你應該考慮考慮其他的組件了,比如HBase。
可以怎么用?
? ? 很多也業務希望對一些數據做統計,不需要實時,可以包容到第二天才統計輸出給用戶,如日報表等。一般我們需要的原始數據在類似MySQL的關系型數據庫,那么每天凌晨(根據實際情況)就可以跑定時任務的將昨日數據遷移到hive上,每天的數據可以按照日期(或其他)做一個分區。遷移完之后接著我們可以預估遷移完成的時間(當然也可以使用hue管理起來,串行的跑),采用定時任務在hive的服務器上寫定時腳本執行SQL統計當天分區數據輸出到統計表,再預估其完成時間將統計表結果遷移回MySQL.保證第二天用戶正常的顯示。
當然你可以對過往的數據做一些統計分析等。
? ? 以上我們不依賴Java或其他應用來處理大數據,這對于某些業務是有好處的,至少我們使用大數據查詢統計的時候不用關心生產應用,使用hive也就可以不用開發工程師的參與,節約成本,但是當你的公司逐漸變大的時候,管理維護大量的腳本絕對不是個好事,這時候就可以不采取這種方式了。
如果你的目標是想在實踐中操作hive,那么下面的這些命令還是要會的,當然你可能正在使用CDH,或者是使用Hue等等,它們都能讓你更快的接觸到實戰Hive。之后你可能考慮的問題就是根據具體的業務查詢了,如果你使用MapReduce引擎計算,你必須了解MapReduce的原理,否則你將很難寫出優秀的SQL,并且在對幾個小時才能查詢出結果的MapReduce嗤之以鼻。
有時候我們可以嘗試使用基于內存的Impala,不過要特別注意監控它的運行情況。目前CDH5版本我沒有找到可以配置Impala的限制內存地方,如果你認為你的查詢不需要太多內存,你可以方心大膽的使用它,它會比用MapReduce查詢快上甚至幾十倍。
這里給出一些簡單的hive操作。
簡單客戶端命令
在裝有hive客戶端的服務器上,使用命令hive即可訪問hive庫,注意之后的命令都要加;號執行哦。
首先我們可以查看下數據庫show databases;,
之后我么可以選擇使用某個數據庫use database_name;
然后顯示下show tables;
查看表結構 desc table_name;
查看表創建語句show create table database_name.table_name;
創建庫語句:CREATE DATABASE database_name;
有時候我們希望不進入客戶端執行命令,比如寫腳本執行hql
不進入客戶端一次性執行.sql或.hql文件:hive -f /xxx.sql
不進入客戶端執行語句:hive -e "select db.xx ..."
如果返回的數據量過多,可以將結果輸入文件中,使用-S開啟靜默模式 hive -S -e "select ... limit 20" >> /xx/xx.log
hive客戶端可以使用dfs命令 如dfs -ls /;
hql腳本中使用 --標識注釋
在hive 進入客戶端之前,會自動執行$HOME/.hiverc 文件中的命令。
使用自定義變量:--define key=value 或者--hivevar key=value 這個值會放到hivevar命名空間
可以使用set命令顯示或者修改變量值
使用set設置或替換變量值: set --hivevar:test=bar;
命名空間:
hivevar : 可讀可寫,用戶自定義變量
hiveconf : Hive相關配置屬性
system: Java定義的配置屬性
env:shell環境定義的環境變量
創建Hive表
hive創建無分區內部表
CREATE TABLE `table_name`(`id` string COMMENT '主鍵ID',`phone` string COMMENT '號碼',`stat_time` timestamp COMMENT '統計時間',`total_count` int COMMENT '總量') ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' STORED AS TEXTFILE;簡單說明
這里我們創建的表信息屬于hive的元數據,元數據通常是存在MySQL這樣的數據庫中的(可以配置),以方便hive表元數據的修改。我們要告訴hive存儲數據的格式:這里存儲為一個文本文件(如果需要壓縮可以使用:STORED AS SEQUENCEFILE),使用一個統一的分隔符分割數據很重要,ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' 這里使用的是\t 分隔,讀者可根據實際情況選擇自己的分割符號。
你還可以使用like關鍵字對一個表結構做復制(不復制數據)。
hive創建分區內部表
CREATE TABLE `table_name`(`id` string COMMENT '主鍵ID',`phone` string COMMENT '號碼',`stat_time` timestamp COMMENT '統計時間',`total_count` int COMMENT '總量') PARTITIONED BY (`date_time` string) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' STORED AS TEXTFILE;簡單說明
PARTITIONED BY (date_time string) hive有分區表的概念,根據具體的業務可選擇不同的分區方式,這里按照日期來分區,hive在創建表后會自動在尾部增加分區字段,這里是分區的字段名為date_time.
同樣你可以創建外部分區表,這里不多說。
hive創建分區
ALTER TABLE database_name.table_name ADD IF NOT EXISTS PARTITION (date_time='${hiveconf:input_date}');這里使用當前的日期創建分區,這是常用的方式。我們可以設定一個定時任務,根據具體的業務,比如:我們可以每天定時的來執行這個語句,達到每天都創建一個新的分區的目的。
hive創建外部表
? ? Hive創建的表包括內部表和外部表,外部表需要使用關鍵字EXTERNAL修飾。兩者區別是:內部表默認會把數據存在hive.metastore.warehouse.dir配置的位置文件夾中,其刪除表操作會刪除對應的數據,而外部表刪除操作僅刪除元數據信息。
? ? 一般情況下,我們會默認配置Hive從HDFS的某個路徑中讀取數據,如果我們需要從文件系統的其他地方拿數據做統計怎么辦?比如我們可能使用Pig維護一些數據,但是這些數據我突然想用hive來做些計算查詢,那我們可以創建一個外部表,將其指向Pig存放數據地址。在hive中你只具有使用權限,沒法刪除其數據。
? ? 我們只需要在創建表的時候給其加上location指定讀取數據地址即可。
新增、加載數據
? ? Hive不支持行級的增刪改,我們只能一次寫入數據,overwrite關鍵字會在加入之前刪除之前的數據即覆蓋,如果是分區表,那之前的分區內容也會被覆蓋。雖然我們無法刪除部分數據,往往會采取這種覆蓋的方式來達到刪除一些數據的目的。
csv文件上傳數據到Hive
load data local inpath ‘服務器絕對路徑/data_info.csv’ into table table_name; -- 加載數據并覆蓋已有數據 load data local inpath ‘服務器絕對路徑/data_info.csv’ overwrite into table table_name;當然你也可以使用其他文件。實際上這里的應用與備份或轉移數據。
備份數據
分區表數據備份
insert overwrite local directory '${backupdir}/${database}/${tablename}/${dateTime}' row format delimited fields terminated by '\t' select * from ${database}.${tablename} where ${tablepartitionfield}=${dateTime};? ? 以上${backupdir}表示將數據備份到某個文件夾里,如果你的數據量很大,注意掛載下盤,擴張其容量大小??梢允褂冒⒗镌苙as盤掛載上去。
分區表數據導回
上面我們把數據備份了,接下來如果要導回分區表同樣給你一個解決方案:寫腳本
腳本的核心代碼如下,其他變量的部分自己研究吧。
如果你會了上述,未分區表的備份及導入也就沒啥難度了。
遷移到MySQL
? ? 實際上,這并不是屬于Hive的功能,只不過如果你不知道如果把MySQL的數據遷移到Hive中,那么你可能直接就萎了。實際上這樣的遷移工具有很多,我比較熟悉的使用datax,sqoop,這是很容易做到的。如果你需要定時遷移,可以使用crontab 去執行腳本
當你安裝好datax之后,只需要找個目錄寫一個json文件,然后將執行命令配置到腳本里,執行即可。這里不詳細說明,這部分網上資料很多,自己研究哈。
查詢插入
這里我們常在版本更新中會新增字段,那么舊的hive數據就缺失了那個字段。這樣在遷移的時候可能導致字段缺失異常。那么我們需要將舊的數據加上字段值,這里簡單處理加為NULL。overwrite關鍵字會將之前的數據覆蓋處理。后面更新章節的時候也會說到。
查詢插入的一般情況:
insert overwrite table table_name [partition (time = '20200907') select * from table_name2 t2 where t2.filed_name = 'xx';如果使用追加方式,不要加overwrite, 可以使用into
insert into database_name.table_name(字段...) select * from where xxx查詢
簡單的客戶端查詢命令
-- 查詢表詳細信息 describe extended xxdb.table_name; -- 顯示表結構,包括數據存儲位置 show create table xxdb.table_name;hive查詢分區
-- 顯示分區信息 show partitions tablename; -- 根據分區查詢數據 select table_coulm from table_name where partition_name = '2020-08-25'; -- 聯合查詢多個分區 select * from table_name where partition_name = '2020-08-22' union all select * from table_name where partition_name = '2020-08-23';hive查詢簡單優化
? ? hive所使用的SQL語句不會自己進行優化,因此對于查詢時優化SQL時必要的。使用EXPLAIN關鍵字查看你的語句是怎么MapReduce的,下面給出幾個常用語句的優化方式:
查詢的時候初步估算下跑大約50億以上的數據查詢需要~起碼到第二天了。
之后查閱資料發現采用distinct關鍵字的時候查詢number of reducers等于1,這就沒辦法并行玩了。(order by關鍵字也是這樣)
于是改用優化方式:使用子查詢
? 果然,效率提高了至少3倍以上。不過即便是如此40億數據也查了半天,更好的優化肯定是有的,但是提升的大概也不太高。如果可能的話使用Impala會快很多很多倍。但是線上統計害怕爆內存,于是暫時先放棄了。
這里我們將查詢的結果寫入一個新的表,然后我們再通過新的表統計就好啦。注意這里使用了group by語句,可能會出現數據傾斜的情況,可以嘗試在sql前面加上 Set hive.groupby.skewindata = true
分區表的left join 優化
我們看一個簡單的sql
select t.name,t.idfrom database_name.table_name_one cleft join database_name.table_name_one_two t on c.xx_id = t.id where c.create_time >= '20210330' and c.create_time <= '20210430' ;其中table_name_one 是一個大的分區表,table_name_one_two是個小表,此時我們的目標是根據小表某個字段關聯大表找到符合要求的數據拉取出來,但是如果直接執行這個sql你會發現mapper數非常大,執行太慢,磁盤IO飆升,原因在于這個sql在left join的時候其實是把兩個表的所有數據拉出來匹配,實際上我們往往只需要大表的某個分區(create_time為分區字段)即可,優化如下:
select t.name,t.idfrom database_name.table_name_one cleft join (select name,xx_id from database_name.table_name_one_two where create_time >= '20210330' and create_time <= '20210430' ) t on c.xx_id = t.id ;只需要把where塞到join里就好,mapper數立馬從一萬個降低到4個,真是差之毫厘,謬之千里呀。
如果這兩個表是一對多的關系,那么得到的數據會有重復,這時候做個分組就好
至于為什么沒有更多優化內容了呢?因為后來我一直在用Impala查詢了。
更新
注意:hive的某些元數據(數據庫名,數據庫所在位置)信息是不可修改的,但我們可以增加刪除字段。另外,如果你新增了字段,也僅僅是在元數據上做了修改表結構操作,那么舊的數據依然沒有該字段的值,因此你的新數據和舊數據的字段數可能不一樣,導致一些遷移失敗問題。你可以insert overwrite table將舊的數據重新寫入表。
hive新增表字段
業務中經常會在發布新版本的時候給統計表新增字段。
可以使用以下語句:
??另外,如果你的表是分區表,上述操作需要指定分區。否則你可能只會在當前分區新增字段,而其他分區不會增加,你可以嘗試cascade關鍵字
alter table `database_name`.`table_name` add columns( `new_name` string COMMENT '新字段' ) cascade;??你還可以使用replace,注意這個操作會將你的表所有字段全部替換為replace columns()括號里面的字段,所以如果你僅僅想新增字段,也必須把舊的字段加上。記得使用desc table_name;檢查你的表結構,確保它正確。
alter table `database_name`.`table_name` replace columns( `new_name` string COMMENT '字段1',`new_name2` string COMMENT '字段2', );將自己的歷史數據重新寫入表,值默認為NULL。(這一點要自己在hive中做配置)
insert overwrite table `database_name`.`table_name` select * from `database_name`.`table_name`;hive表數據去重(推薦方案2)
??有時候我們各種瞎操作,搞完發現hive表中存在臟數據,要做去重操作。這里給出我的一些實踐:
方案1:注意,這個方案時可能會改變表的結構,我在使用這個方案完,再使用遷移工具遷移的時候發現報錯。原因可能是創建表的分隔符不同導致,因此這里還需要去將表格式改成和之前一樣?;蛘咴趧摻ū淼臅r候指明格式,下面語句沒有寫,請自行研究。
方案2:先將該表重命名,再重新創建一個空表,和之前結構一樣。然后將舊表數據去重后插入新表,舊的就不用了。
-- ALTER TABLE table_name RENAME TO table_name_bak; insert into table_name(表字段) select distinct * FROM table_name_bak;其他更新操作
-- 表重命名 ALTER TABLE table_name RENAME TO table_name_bak;刪除
hive刪除分區
alter table table_name drop partition(date_time='2020-07-22');注意:刪除后其實HDFS會生成一個Trash備份文件,如果你是真的不需要這些數據了,以方便釋放磁盤,你必須把這個備份文件干掉。
真正刪除
# 首先在裝hdfs的節點服務器找到存hive表的位置 hdfs dfs -du -s -h /opt/cdh/hive/warehouse/xxx.db # 在drop之后,會在目錄下生成一個.Trash文件 hdfs dfs -du -h /user/root/.Trash # 把這個文件刪除,就真的沒有了,慎重 hdfs dfs -rm -r /user/root/.Trash刪庫跑路
對于內部表,會刪除表中數據和元數據。對于外部表僅刪除元數據。
-- cascade表示如果數據庫存在表就先刪除表,然后刪除庫,溫馨提示,慎重刪庫! drop database name cascade; -- 數據表刪除 注意這里不能用delete 注意drop table之后,表當中的數據依然保留在hdfs備份文件上,可以復原.把這個備份文件干掉就真的沒了。 drop table if exists table_name;??本文寫的不太多,僅僅是我幾個月實踐和學習的基礎內容,學習的環境也有很多限制,有自己的實踐,也參考了一些書籍,全篇沒啥圖片(因為懶),還有很多地方不完善,比如Hive的相關安裝、配置,這里沒有描述,因為方案有很多,我自己是使用的CDH,配置起來也比較簡單。總之學的差不多了,先拿出來溜溜看吧。文章寫的比較亂,多多包涵。講道理你要是能看到這里,就說明你可能只看了這里。^-^.
水平有限,如果你覺得上述有任何疑問、不足、錯誤的地方,歡迎在評論區指正。
總結
以上是生活随笔為你收集整理的实践Hive的点点滴滴的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从JDK 6升级到JDK 7过程中遇到的
- 下一篇: C++ Socket编程实例解析