文章目錄
Partition
爲什麼需要Partition
由於hive執行一條簡單的sql都會查詢整個表,所以當表的數據量非常大時查詢速度會很慢。
基於此Hive可以將表劃分爲幾個分區,即將表基於某些分區列劃分爲幾個部分。每個表可以有一個或者多個分區。例如,有一個員工信息表,如果根據部門來分區
的話,那麼查詢department="A"
時就只需要去部門爲A的分區數據中找即可,而不用掃描整個表了,極大地提高了查詢的性能。
如何創建Partition
下面是整個建表語句,不僅僅包括分區,還包括bucket等
CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name
[(col_name data_type [column_constraint_specification] [COMMENT col_comment], ... [constraint_specification])]
[COMMENT table_comment]
[PARTITIONED BY (col_name data_type [COMMENT col_comment], ...)]
[CLUSTERED BY (col_name, col_name, ...) [SORTED BY (col_name [ASC|DESC], ...)] INTO num_buckets BUCKETS]
[ROW FORMAT row_format]
[STORED AS file_format]
[LOCATION hdfs_path]
例如,創建如下的分區表,其中以yoj
來作爲分區列,yoj
表示year of join
create external table test_part(
id int, name string, dept string)
partitioned by(yoj string)
row format delimited fields terminated by ','
stored as textfile;
創建兩個輸入文件
/root/python_dirs/year/2009
目錄下有文件file2
, 內容如下
1, sunny, SC
2, animesh, HR
/root/python_dirs/year/2010
目錄下有文件file3
, 內容如下
3, sumeer, SC
4, sarthak, TP
分別將兩個文件導入到分區表裏
load data local inpath '/root/python_dirs/year/2009/file2' into table test_part partition(yoj='2009');
load data local inpath '/root/python_dirs/year/2010/file3' into table test_part partition(yoj='2010');
然後通過show partitions test_part
可查看當前test_part
表有哪些分區
在hdfs上對應的就是兩個目錄yoj=2009
和yoj=2010
可通過alter table test_part drop if exists partition(yoj="2010");
來刪除分區數據
Partition的兩種類型 static VS dynamic
兩種類型的區別在於如何導入分區數據
。通常在將大文件加載到Hive表中時,靜態分區是首選。但是,如果你不知道有幾個分區,那麼此時就可以選擇動態分區。以上面的例子簡要說明下,我們知道有兩個文件內容分別屬於yoj=2009
和yoj=2010
,所以需要手動導入兩次,那麼如果我有10個不同的yoj的值呢?那麼我們就需要手動導入10次,這時我們就可以選擇動態分區
。
生成測試數據
假設我們有如下一張經過清洗後的信息表student_dwd_ext
,可以認爲是一張寬表。
create external table student_dwd_ext(
id int, name string, sex string, age int, id_card string,
address string, phone_number string, job string,
company string, graduation_time string)
row format delimited fields terminated by '\t'
stored as textfile
location '/opt/wacos/student';
下面是通過faker
來生成測試數據的代碼,需要通過pip install faker
來安裝所需要的依賴包
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# **********************************************
#
# Filename: generate_student_data.py
#
# Author: WangTian
# Description: 批量生成學生數據
# Create: 2020-03-03 21:19:30
# Last Modified: 2020-03-03 21:19:30
#
# **********************************************
from faker import Faker
import sys
from threading import Thread
def generate_stu_by_index(start, end, faker, file_name):
'''
生成id在start和end之間的學生數據
start : int
end : int
faker : 外部傳進來, Faker類的實例對象, locale=“zh_CN”
file_name : string 要生成的文件名
'''
file_handler = open(file_name, mode="w")
id = start
f = faker
while(id <= end):
f = Faker(locale="zh_CN")
name = f.name()
sex = f.random_element(elements=("男", "女"))
age = f.random_int(min=20, max=40)
id_card = f.ssn()
address = f.address().split(" ")[0]
phone_number = f.phone_number()
job = f.job()
company = f.company()
graduation_time = f.random_element(
elements=("2012", "2013", "2014", "2015", "2016"))
row = "\t".join([str(id), name, sex, str(age), id_card,
address, phone_number, job, company,
graduation_time])
file_handler.write(row+"\n")
id = id+1
file_handler.close()
class run_thread(Thread):
def __init__(self, id, start, end, faker, file_name):
Thread.__init__(self)
self._id = id
self._start = start
self._end = end
self._faker = faker
self._file_name = file_name
def run(self):
print("threadid={}, starting......".format(self._id))
generate_stu_by_index(self._start, self._end,
self._faker, self._file_name)
print("threadid={}, exit......".format(self._id))
print("threadid={}, has generated {} rows, the generated file is {}".format(
self._id, (self._end-self._start+1), self._file_name))
def multithread_generate(num):
'''
多線程生成學生數據文件
num : 線程數
'''
i = 1
len = 100
faker = Faker(locale="zh_CN")
while(i <= num):
start = len*(i-1)+1
end = len*i
run = run_thread(i, start, end, faker,
"{}_{}_{}.txt".format(i, start, end))
run.start()
i = i+1
def main(num):
'''
main方法
'''
multithread_generate(num)
if __name__ == "__main__":
print sys.getdefaultencoding()
reload(sys)
sys.setdefaultencoding('utf8')
print sys.getdefaultencoding()
main(int(sys.argv[1]))
上面的代碼保存並命名爲generate_student_data.py
執行python generate_student_data.py 20
來生成2000條數據,其中20
表示開啓20個線程,每個線程生成100條數據。執行完後部分結果截圖如下所示。
執行load data local inpath '/root/python_dirs/*.txt' overwrite into table student_dwd_ext;
將生成的2000條數據導入到student_dwd_ext
表裏,如下所示
動態分區驗證
創建一個分區表,以graduation_time
來作爲分區列
create table student_part(
id int, name string, sex string, age int, id_card string,
address string, phone_number string, job string,
company string)
partitioned by(graduation_time string)
row format delimited fields terminated by '\t'
stored as textfile;
然後通過動態分區的方式導入數據,但是必須要開啓動態分區的設置,否則會報如下圖所示的錯誤。
# 開啓動態分區的設置
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict;
set hive.exec.max.dynamic.partitions=1000;
set hive.exec.max.dynamic.partitions.pernode=100;
insert into table student_part partition(graduation_time)
select * from student_dwd_ext;
然後對結果進行了如下的驗證
select count(*),graduation_time from student_dwd_ext group by graduation_time;
select count(*) from student_part where graduation_time="2012";
select count(*) from student_part where graduation_time="2013";
show partitions student_part;
Bucket
爲什麼要引入Bucket
我們知道Hive的Partition已經可以將表分隔成多個文件(或目錄),但是,Partition
只是在如下兩個場景中有明顯的效果
- 有限的分區數
- 分區之間數據大小幾乎是相等的
例如,當我們以country
來進行分區時,大國對應的分區數據就很大,小國對應的分區數據就很小,那麼此時Partition
就不是太理想了。
爲了解決這個問題,Hive提出了Bucketing
的概念。這是一種非常有效的技術,能將表的數據分成大小几乎相同的易於管理的多個部分。
特點及優點
Bucketing
是通過對bucketed column
做hash function
然後mod number of buckets
。
通過CLUSTERED BY
語句將表分爲多個buckets
。
每個bucket
是表目錄的一個文件。
Bucketing
可以和Partitioning
一起使用,也可以單獨使用
Bucketing
有如下幾個優點
- 將表的數據分成大小几乎相同的幾部分
- 相對於沒有做bucket的表利於做
sampling
map side join
會更快
同樣地,Bucketing
也有缺點,就是在導入數據到bucket table
時需要手工指定和導入,而且導入的時間會比導入數據到Partition表要長,因爲數據要均勻分配嘛。
創建Bucket
如下所示,clustered by
指定id
來做bucket
,然後通過sorted by
來在bucket
文件裏做排序。
其中clustered by
和sorted by
的字段必須要在表的創建定義裏定義好,這裏是Partitioning
不同的地方
create table student_bucket_part(
id int, name string, sex string, age int, id_card string,
address string, phone_number string, job string,
company string)
partitioned by(graduation_time string)
clustered by (id) sorted by (age) into 16 buckets
row format delimited fields terminated by '\t'
stored as textfile;
導入數據。如果不能成功導入到bucket表的話,可能需要顯示打開bucket
set hive.enforce.bucketing=true;
2.x的版本已經不需要
insert into table student_bucket_part partition(graduation_time)
select * from student_dwd_ext;
查看HDFS,能發現如下所示,每個bucket就是一個單獨的文件
Table Sampling VS limit
hive裏的Table Sampling
是從原始的大數據集中抽取小部分數據,和limit
非常相似。
但它們之間是有不同點的。大多數情況下,limit
會執行全表掃然後指定數目的結果,但是Sampling
會選取一部分數據來執行查詢。
關於tablesample bucket x out of y
的說明,y一般來說是bucket num的整數倍
假如bucket num爲32
TABLESAMPLE(BUCKET 6 OUT OF 8)就表示分成8個bucket爲一組,然後就選取每組的第6個即6, 14, 22, 30這4個編號的bucket
TABLESAMPLE(BUCKET 23 OUT OF 32)就表示分成32個bucket爲一組,然後就選取第23個bucket
TABLESAMPLE(BUCKET 3 OUT OF 64)就表示64個bucket爲一組,然後就選取第3個
select id,name,sex,age,job,graduation_time from student_bucket_part
tablesample(bucket 10 out of 128) where age between 20 and 30;
Partition和Bucket數據模型圖示
Join
介紹
hive的join
和sql的join
是非常類似的,都是將多個表連接起來獲取更多的字段信息。關於Hive Join
我們需要知道以下幾點:
- 只有
Equality join
纔是被允許的,即join裏的on語句只能有等於和不等於。 - 一個
query
語句裏可以有超過2張表來進行join
,這是和sql join
保持一致的。 - 有
LEFT, RIGHT, FULL OUTER join
,這是和sql join
保持一致的,不過mysql
沒有full join
。 Joins are NOT commutative! Joins are left-associative regardless of whether they are LEFT or RIGHT joins
,意思是a join b
和b join a
是不同的,這不同於數學上的加法交換律
,順序不同,哪個表去stream
也是不同的,關於stream
後面有介紹。不管是LEFT JOIN
還是RIGHT JOIN
都是從左開始join表的,至於數據以哪張表爲主就看是LEFT JOIN
還是RIGHT JOIN
。
完整的join語法如下
join_table:
table_reference [INNER] JOIN table_factor [join_condition]
| table_reference {LEFT|RIGHT|FULL} [OUTER] JOIN table_reference join_condition
| table_reference LEFT SEMI JOIN table_reference join_condition
| table_reference CROSS JOIN table_reference [join_condition] (as of Hive 0.10)
table_reference:
table_factor
| join_table
table_factor:
tbl_name [alias]
| table_subquery alias
| ( table_references )
join_condition:
ON expression
Hive Join
有四種類型,如下圖所示,和sql join
是保持一致的,具體各個join是什麼意思我就不在這裏贅述。
官方的join exmaples
- 如下的join語句都是正確的
SELECT a.* FROM a JOIN b ON (a.id = b.id)
SELECT a.* FROM a JOIN b ON (a.id = b.id AND a.department = b.department)
SELECT a.* FROM a LEFT OUTER JOIN b ON (a.id <> b.id)
SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key2)
- 如果join子句對於每個表都使用相同的列,則Hive將多個表上的join轉換爲單個map/reduce作業。
例如,下面的join語句的on條件裏都有b.key1
,所以該join會轉換成一個MR作業
SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1)
例如,下面的join語句的on條件裏沒有相同的列,所以會轉換成兩個MR作業。 第一個MR作業是a join b
,然後a join b
的結果和c
做join
作爲第二個MR作業。
SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key2)
- 在join的每個MR階段,最後一個表都是通過
reducer
傳遞的,其他前面的表被緩存到內存裏。因此將大表放在最後面是可以減少內存消耗的。
例如,下面的join語句會轉換成一個MR作業,a和b表的數據就會被緩存到內存裏,c表通過reducer
傳遞
SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1)
例如,下面的join語句會轉換成兩個MR作業。第一個MR作業緩存a表數據,b表通過reducer
傳遞;第二個MR作業緩存a join b
的結果數據,c表通過reducer
傳遞
SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key2)
- 在join的每個MR階段,要傳遞(
stream
)的表是可以顯示指定的
例如,下面的join語句會轉換成一個MR作業,如果不指定/*+ STREAMTABLE(a) */
,默認是c表去stream
,a和b表的數據就會被緩存到內存。現在指定了/*+ STREAMTABLE(a) */
,所以是b和c表的數據就會被緩存到內存,a表通過reducer
傳遞。
如果沒有顯示指定STREAMTABLE
,默認要stream
的表是最後一個。
SELECT /*+ STREAMTABLE(a) */ a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1)
LEFT, RIGHT, and FULL OUTER join
對於匹配不成功的時候是非常有效的,實際工作經常用
例如,下面的LEFT OUTER JOIN
語句對於a表的每行數據都會有返回結果。如果有b.key等於a.key
那麼返回a.val, b.val
,如果沒有b.key等於a.key
那麼返回a.val,NULL
。b表中和a.key
不匹配的數據會被丟棄。
FROM a LEFT OUTER JOIN b
必須要寫在一行來保證a一定是在b的左邊,保留a表所有數據。
FROM a RIGHT OUTER JOIN b
必須要寫在一行,保留b表所有數據。
FROM a FULL OUTER JOIN b
必須要寫在一行,保留a表和b表所有數據。
SELECT a.val, b.val FROM a LEFT OUTER JOIN b ON (a.key=b.key)
join
發生在where
語句前。所以如果想過濾join的輸出的話,可以在where
語句裏過濾,也可以在join
語句裏過濾
考慮下面的sql語句,會返回a.val,b.val
的數據集,然後在where進行過濾(既引用了a表字段也引用了b表字段)。對於沒有b.key等於a.key
的row來說,返回的數據是a.val,NULL
,而且b.ds
的值也是NULL
。也就是說b.ds='2009-07-07'
會過濾掉所有沒有b.key等於a.key
的a的row數據(那就有可能過濾多了)
SELECT a.val, b.val FROM a LEFT OUTER JOIN b ON (a.key=b.key)
WHERE a.ds='2009-07-07' AND b.ds='2009-07-07'
那麼對於LEFT OUTER JOIN
這種情況來說,應該採用下面的語句,join的結果已經預先就過濾掉了。RIGHT and FULL join
也是同理。
SELECT a.val, b.val FROM a LEFT OUTER JOIN b
ON (a.key=b.key AND b.ds='2009-07-07' AND a.ds='2009-07-07')
- Joins are NOT commutative! Joins are left-associative regardless of whether they are LEFT or RIGHT joins. 這句話我在上面join的介紹裏就已經說明的很清楚了。
例如,a和b先做join,得到的結果再去做LEFT OUTER JOIN c
,所以即使a.key
和c.key
存在但是b.key
不存在,這條數據也會在a join b
的階段被幹掉。
SELECT a.val1, a.val2, b.val, c.val
FROM a
JOIN b ON (a.key = b.key)
LEFT OUTER JOIN c ON (a.key = c.key)
LEFT SEMI JOIN
以一種有效的方式實現了IN/EXISTS
子查詢功能。LEFT SEMI JOIN
的限制是右邊的表只能在join condition (ON-clause)
裏引用,不能在WHERE
或者SELECT
等語句裏使用
例如,
SELECT a.key, a.value
FROM a
WHERE a.key in
(SELECT b.key
FROM B);
就可以重寫成
SELECT a.key, a.val
FROM a LEFT SEMI JOIN b ON (a.key = b.key)
- 如果join的表裏面有一個小表,那麼就可以將該join轉換成一個只有map的job,不需要reducer。
例如,b表足夠小,/*+ MAPJOIN(b) */
就會將b表的所有數據加載到內存,對於a表的每個mapper,b表數據可以直接讀取。限制是a FULL OUTER JOIN b
不能執行,因爲map join操作僅僅只能stream一張表,FULL OUTER JOIN
就需要stream兩張表了。不建議使用/*+ MAPJOIN(b) */
,而應該使用參數hive.auto.convert.join=true
,開啓後hive會在運行時自動的將join轉換成map join
(如果可以的話)
SELECT /*+ MAPJOIN(b) */ a.key, a.value
FROM a JOIN b ON a.key = b.key
- 如果join的表是在on語句的列上做了bucket,且兩個表的bucket的數目是倍數關係,那麼兩表的bucket之間可以相互join
例如,a表和b表都有4個bucket,下面的join會轉換成一個只有map的job。對於A的每個mapper,不用獲取B的所有數據,處理A的bucket-1的mapper僅僅只需要獲取B的bucket-1。但是這並不是默認行爲,需要通過設置set hive.optimize.bucketmapjoin = true
來開啓
SELECT /*+ MAPJOIN(b) */ a.key, a.value
FROM a JOIN b ON a.key = b.key
- 如果join的表是在on語句的列上做了bucket和sort,而且兩表有同樣的bucket數,那麼就可以優化成
sort-merge join
。在mapper裏,對應的bucket會相互join。
例如,a表和b表都有4個bucket,下面的join會轉換成一個只有map的job。但是需要開啓參數來進行優化
set hive.input.format=org.apache.hadoop.hive.ql.io.BucketizedHiveInputFormat;
set hive.optimize.bucketmapjoin = true;
set hive.optimize.bucketmapjoin.sortedmerge = true;
SELECT /*+ MAPJOIN(b) */ a.key, a.value
FROM A a JOIN B b ON a.key = b.key
Map Join
原理介紹
Hive裏的Map Join
可以加速查詢,這個feature
也叫做Map Side Join
。上面的官方例子裏也簡要提到了Map Join
的用法,這裏是從運行原理、參數、限制等方面來更加詳細的介紹該feature
。
join有一個開銷非常大的操作就是shuffle
,這就降低了hive的查詢速度。我們可以用Map Side Join
來加速查詢,因爲在join的表裏有一個小表是可以完整的加載到內存裏的。所以該join可以轉換成一個只有map沒有reducer的job,消去了shuffle過程。
在MR task
前,第一步就是創建一個MapReduce local task
。這個本地的MR task
會從HDFS上讀取小表數據,然後加載到內存的hash table
裏,然後變成一個hash table
文件。接着在MR task
啓動前將這個hash table
文件通過Hadoop Distributed Cache
發送到每個mapper的本地磁盤上。這樣,所有的mapper就能加載這個hash table
文件到mapper內存裏然後在map階段做join。
參數
hive.auto.convert.join
<property>
<name>hive.auto.convert.join</name>
<value>true</value>
<description>Whether Hive enables the optimization about converting common join into mapjoin based on the input file size</description>
</property>
該參數默認值是true,當join的表裏找到一個大小小於25 MB(hive.mapjoin.smalltable.filesize
)的表,那麼就將該join轉換爲map join
。
hive.auto.convert.join.noconditionaltask
<property>
<name>hive.auto.convert.join.noconditionaltask</name>
<value>true</value>
<description>
Whether Hive enables the optimization about converting common join into mapjoin based on the input file size.
If this parameter is on, and the sum of size for n-1 of the tables/partitions for a n-way join is smaller than the
specified size, the join is directly converted to a mapjoin (there is no conditional task).
</description>
</property>
<property>
<name>hive.auto.convert.join.noconditionaltask.size</name>
<value>10000000</value>
<description>
If hive.auto.convert.join.noconditionaltask is off, this parameter does not take affect.
However, if it is on, and the sum of size for n-1 of the tables/partitions for a n-way join is smaller than this size,
the join is directly converted to a mapjoin(there is no conditional task). The default is 10MB
</description>
</property>
假定有3表或者更多的表一起join的場景。如果所有的表都是小表且開啓hive.auto.convert.join
,Hive會生成3個或多個map-side join
。然而,如果第n個表的大小小於10 MB(hive.auto.convert.join.noconditionaltask.size
),我們可以將這3個或多個map-side join
變成一個map-side join
。
hive.auto.convert.join.use.nonstaged
可以將其設置爲true來避免這些情況的pres-taging
,這些情況包括要map join的表不用過濾或者projection
。當前,該參數不適用於vectorization
和tez引擎
<property>
<name>hive.auto.convert.join.use.nonstaged</name>
<value>false</value>
<description>
For conditional joins, if input stream from a small alias can be directly applied to join operator without
filtering or projection, the alias need not to be pre-staged in distributed cache via mapred local task.
Currently, this is not working with vectorization or tez execution engine.
</description>
</property>
Hive Map Side Join的限制
- 主要的限制是我們是無法將
Full outer join
轉化爲map-side join
的。這個在上面官方例子裏說過。 - 對於
left-outer join
變成map-side join
,右邊的表大小需要小於25 MB。 - Union Followed by a MapJoin
- Lateral View Followed by a MapJoin
- Reduce Sink (Group By/Join/Sort By/Cluster By/Distribute By) Followed by MapJoin
- MapJoin Followed by Union
- MapJoin Followed by Join
- MapJoin Followed by MapJoin
測試
先創建一張表company_new
create table company_new
row format delimited fields terminated by '\t'
stored as textfile
AS
select count(*) ct,company from student_part group by company order by ct desc;
將student_dwd_ext表和company_new表進行join,查看執行計劃
explain select a.*, b.ct from student_dwd_ext a, company_new b where a.company=b.company and a.company="凌雲科技有限公司";
如下圖所示,只有Map階段,沒有reduce,說明已經變成了map-side join
在關閉掉hive.auto.convert.join
後再次查看其查詢計劃,如下圖所示,就是正常的join,有Reduce
set hive.auto.convert.join=false;
explain select a.*, b.ct from student_dwd_ext a, company_new b where a.company=b.company and a.company="凌雲科技有限公司";
查詢時間上也有差距,這裏我就不展示圖片了。
Bucket Map Join
當要join的表很大,且join on的列剛好是bucket的列,還需要一個表的bucket的數目是另一個表的bucket數目的倍數,那麼我們就可以使用Bucket Map Join
。
假如一個表有2個bucket,那麼另一個表的bucket的數目必須是2的倍數。而且表的每個bucket是可以放的進內存例如a表的bucket-a1大小要足夠小,這樣才能執行map side join
,否則就只能執行普通的join。這樣,在mapper端就只需要獲取相對應的bucket就行了,而不用獲取整個表。這樣就能大大提高性能。還有一點需要保證的是,數據不能在bucket map join
裏做排序。
默認情況下,bucket map join
是不會開啓的,需要設置下面的參數來開啓優化。
set hive.optimize.bucketmapjoin = true;
set hive.input.format=org.apache.hadoop.hive.ql.io.BucketizedHiveInputFormat;
bucket map join
的使用場景總結爲以下五點
- 所有的表是非常大的
- 表join on的列恰好是bucket的列
- 一個表的bucket的數目是另一個表的bucket數目的倍數
- 表的每個bucket必須要足夠小(即對應的bucket數目應該要很大)
- 數據不能排序
bucket map join
的缺點也是非常明顯,也就是上面的第2點,只能用在特定的sql join語句裏。
Sort Merge Bucket Map Join
使用要求如下
- 所有的表是非常大的
- 表join on的列恰好是bucket的列同時也是
sort
的列 - 所有join的表的bucket的數目必須是相同的
需要設置以下參數來開啓
set hive.input.format=org.apache.hadoop.hive.ql.io.BucketizedHiveInputFormat;
set hive.auto.convert.sortmerge.join=true;
set hive.optimize.bucketmapjoin = true;
set hive.optimize.bucketmapjoin.sortedmerge = true;
Sort Merge Bucket Map Join
的缺點也是非常明顯,限制很高。
join優化的總結
上面的這些優化參數並不一定會帶來更快的查詢速度。假如選擇Sort Merge Bucket Map Join
,在執行join前會首先對join的表進行排序,這就是增加的開銷。map join也會增加內存的開銷。所以這都是一個trade-off
的過程。
有可能簡單的join就有很好的性能,而上面的優化參數沒有效果。所以這時就可以考慮調整MapReduce或者Hive配置如內存使用、並行度等來優化常規的join。
參考網址
faker簡單使用
faker官網
python-multithreading
hive-static-vs-dynamic-partition
data-flair-hive-parititions
LanguageManual+Sampling
tablesamplebucket-x-out-of-y
bucketing-in-hive
LanguageManual+Joins
map-join-in-hive
LanguageManual+JoinOptimization
hive-efficient-join-of-two-tables