hive 關聯表和外表的區別:
1. 外部表需要external關鍵之,location是數據文件默認存放位置,不管是不管是select 還是load的數據都存放在這里。導入數據到外部表,數據並沒有mv到數據倉庫目錄,而是在loacation目錄。
2.內部表建表時也能加上location,作用和外部表一樣,都是表數據的存放路徑,不同的是drop table時內部表會將hdfs上的數據也刪掉,外部表僅僅是刪除表的元數據,原始數據不會刪除。
Hive 外表的優點:
1.安全,外部表不用擔心表刪除帶來的風險。在Hive當中,對於外部表來說,我們在刪除表數據的時候,刪除的僅僅是表的元數據信息,但是卻不能刪除掉表中的真實數據。
2.靈活,方便數據共享,使用更加靈活。
外部數據
hive> dfs -ls /dw/im/prod/sessionContent;
Found 7 items
drwxr-xr-x - icosc hadoop 0 2018-11-07 23:55 /dw/im/prod/sessionContent/2018-11-07
drwxr-x--x - hadoop hadoop 0 2018-11-08 23:55 /dw/im/prod/sessionContent/2018-11-08
drwxr-x--x - hadoop hadoop 0 2018-11-09 23:55 /dw/im/prod/sessionContent/2018-11-09
drwxr-x--x - hadoop hadoop 0 2018-11-10 23:55 /dw/im/prod/sessionContent/2018-11-10
drwxr-x--x - hadoop hadoop 0 2018-11-11 23:55 /dw/im/prod/sessionContent/2018-11-11
drwxr-x--x - hadoop hadoop 0 2018-11-12 23:55 /dw/im/prod/sessionContent/2018-11-12
drwxr-x--x - hadoop hadoop 0 2018-11-13 13:55 /dw/im/prod/sessionContent/2018-11-13
hive>
創建Hive外表
根據數據的存放位置,我在建表 sql 中指定 location
use temp;
create external table tmp_im_session_content(
`eventType` string comment "會話類型",
`channelId` int comment "渠道 id",
`sourceType` int comment "",
`sessionId` string COMMENT "會話session id",
`cId` string COMMENT "會話 id",
`userId` string COMMENT "用戶id",
`vccId` string COMMENT "企業 id",
`total` int COMMENT "",
`index` int COMMENT '標識本次是第幾次發送會話內容,從1開始 (每次最多發送10條聊天內容)',
`content` array<struct<`from`:int,`to`:int,content:string,msgtype:int,`sequence`:bigint,createtime:bigint,errorcode:int,errorstring:string,status:int>> COMMENT '會話內容'
)
PARTITIONED BY (`date` string)
location "/dw/im/prod/sessionContent"
TBLPROPERTIES ("comment"="IM 會話內容 by gz");
數據裝載和驗證
我們在建表的時候,已經指定了location,但是我們去查的時候,發現並沒有數據。
hive> select
> a.*
> from temp.tmp_im_session_content a
> where `date` = '2018-11-07'
> limit 40;
OK
Time taken: 0.107 seconds
hive>
為什么???????????????????
這是因為雖然在HDFS當中數據已經存在了,但是在Hive的元數據倉庫中並沒有,所以我們查不到數據。
給Hive 外表數據倉庫裝載數據
-- 創建分區並指定 location
alter table temp.tmp_im_session_content add if not exists partition (`date`='2018-11-07') location '/dw/im/prod/sessionContent/2018-11-07';
-- 驗證數據
select
eventType,
channelId,
sourceType,
sessionId,
cId,
userId,
vccId,
total,
index,
content
from temp.tmp_im_session_content a
where `date` = '2018-11-07'
limit 40;
select
a.*
from temp.tmp_im_session_content a
where `date` = '2018-11-07'
limit 40;
-- 驗證 ok
修復分區
Hive提供了一個"Recover Partition"的功能。原理相當簡單,執行后,Hive會檢測如果HDFS目錄下存在,但表的metastore中不存在的partition元信息,更新到metastore中。如下:
msck repair table temp.tmp_im_session_content;
問題又來了,查詢:
hive> select
> a.*
> from temp.tmp_im_session_content a
> where `date` = '2018-11-08'
> limit 40;
OK
Time taken: 0.107 seconds
hive>
還是沒數據。這里就不得不說源數據存放的格式了。我創建的外表分區字段是 date,但是我們的源文件路徑是/dw/im/prod/sessionContent/2018-11-08
,按照分區目錄的格式應該是/dw/im/prod/sessionContent/date=2018-11-08
。因此,用修復命令沒起作用。這就要求在寫 HDFS 的時候做好格式約定,避免后續各種意想不到的驚喜
☹。
這個問題,可以通過下面 laod data inpath 一節了解更詳細的內容
laod data inpath 裝載數據
命令如下:
laod data inpath '/dw/im/prod/sessionContent/2018-11-08' overwrite into table temp.tmp_im_session_content PARTITION (`date`='2018-11-08');
看下源文件狀態:
hive> dfs -ls /dw/im/prod/sessionContent/
> ;
Found 7 items
drwxr-xr-x - icosc hadoop 0 2018-11-07 23:55 /dw/im/prod/sessionContent/2018-11-07
drwxr-x--x - hadoop hadoop 0 2018-11-09 23:55 /dw/im/prod/sessionContent/2018-11-09
drwxr-x--x - hadoop hadoop 0 2018-11-10 23:55 /dw/im/prod/sessionContent/2018-11-10
drwxr-x--x - hadoop hadoop 0 2018-11-11 23:55 /dw/im/prod/sessionContent/2018-11-11
drwxr-x--x - hadoop hadoop 0 2018-11-12 23:55 /dw/im/prod/sessionContent/2018-11-12
drwxr-x--x - hadoop hadoop 0 2018-11-13 14:35 /dw/im/prod/sessionContent/2018-11-13
drwxr-x--x - hadoop hadoop 0 2018-11-08 23:55 /dw/im/prod/sessionContent/date=2018-11-08
發現什么沒?原來的目錄/dw/im/prod/sessionContent/2018-11-09
的目錄不見了,但是多了一個/dw/im/prod/sessionContent/date=2018-11-08
的目錄。
前面我們提到,如果分區很多的時候,可以通過msck repair table tableName
的方式來修復,但是如果格式不是 Hive 標准的分區文件存儲格式,這個命令就沒什么卵用。從剛剛的laod data
命令我們也可以看出,此命令會移動數據文件,如果不是標准的問價存儲路徑,也會根據命令進行相應的調整。這一點很重要,需要記住。
如果需要傳遞日期變量,可以:
-- 針對標准的分區文件,即顯示的按照分區字段存放的數據文件
laod data inpath '/dw/im/prod/sessionContent/date=${date}' overwrite into table temp.tmp_im_session_content PARTITION (`date`='${date}');
OR
-- 針對非標准的,未顯示按照分區字段存放的數據文件
laod data inpath '/dw/im/prod/sessionContent/${date}' overwrite into table temp.tmp_im_session_content PARTITION (`date`='${date}');
這里有個小插曲,前面我通過alter table
裝載了2018-11-07的數據,目錄沒有變化,因此有用load data
的方式處理:
laod data inpath '/dw/im/prod/sessionContent/2018-11-07' overwrite into table temp.tmp_im_session_content PARTITION (`date`='2018-11-07');
因為使用了overwrite INTO
,導致/dw/im/prod/sessionContent/date=2018-11-07
的數據被刪除了☹️
只好手動將數據又移動回來:
hadoop fs -mv /user/dev/.Trash/181113150000/dw/im/prod/sessionContent/2018-11-07 /dw/im/prod/sessionContent/date=2018-11-07
然后
laod data inpath '/dw/im/prod/sessionContent/date=2018-11-07' into table temp.tmp_im_session_content PARTITION (`date`='2018-11-07');
分區管理
從最前面 HDFS 文件可以看到,還有還6個日期目錄,我可以創建5個 alterr table 的語句來裝載數據,但是如果有365天的數據需要加載呢?如果寫多條365條alter 語句,豈不是很傻?
shell 循環創建
這種方式已經寫過,不再贅述,故而此處省略。
load data 方式加載數據
只要load data,數據都會被移動到表的location下面。
刪除外表分區管理
-- 刪除分區
alter table temp.tmp_im_session_content drop partition(`date`=2018-11-07)
總結
由於我們的源數據目錄沒有顯示的指定分區字段,因此建表的時候沒有指定 location,通過 laod data inpath 命令來裝載數據
- load data 命令會移動數據
- laod data inpath ... overwrite 命令使用不當會導致數據被刪除,如果看到本文檔,可以按照我前面的方式進行處理即可。
- 通過 alter table 的方式裝載數據,會將源文件與 對應的 Hive 表倉庫進行映射,但是不會移動數據,不會改變目錄。