exists hive中如何使用_hive 使用优化实践

本文介绍了在Hive中使用exists优化数据同步过程,避免not in导致的小文件问题和数据倾斜。通过调整SQL,如使用DISTRIBUTE BY rand(),实现了更均衡的reduce任务分配,提高了数据同步效率。

背景

在工作中需要同步pg数据库下的某张表到hive,使用的工具是开源的sqoop,业务表的数据表包含最近一年的数据,数据表的行数为366,830,898,数据表的字段个数为71个,数据表在pg中的空间大小为110G;pg中表没有唯一主键,同一个id的数据可能会出现多次,且都是业务允许的正常场景。

分析

全量同步数据

每次将pg的整表全量同步到hive分区表中,这种做法同步的速度很慢,这张表后面会有依赖,会影响后续数据的产出,且会有数据表block的风险,所以不适合用全量的方式同步

增量同步数据

查看每天有改动的数据的记录数,最多为200万,数据的体积约为700M。尝试单独用sqoop同步一天的数据速度很快,sqoop同步的时候要指定map划分的split字段,所以在pg中先在查询和分割字段上加上索引。增量的数据要和前一天的全量分区做合并,因为同一个id不管在增量的表中还是全量的表中都会出现多行记录,所以并不能使用A left join B where b.id is not null的方式去处理,也不能left semi join 去处理。

尝试

not in 方式

查询在hive原始表中同时不在增量表中的id对应的数据,接着和增量的最新数据做union all。

hive -e "

insert overwrite table target_table partition(dt = '$curr_date')

select

a.*

from a

where

a.dt = '$curr_date - 1'

and a.id not in (select id from b where b.dt = '$curr_date')

union all

select

*

from b

where b.dt = '$curr_date'

"

这种方式理论上是可以实现的,实际执行中发现任务最后会生成很多的小文件。

1.  尝试手动设定reduce的数量 set mapred.reduce.tasks = 64,实际执行中并未起作用。

2.  尝试在map阶段先进行文件合并 ,例如下面的设置,map的数量确实减少了,但是reducer数量还是一样没变。同理设定reduce完成以后的文件合并,一样不起作用。

set mapred.min.split.size=100000000;

set mapred.max.split.size=100000000;

set mapred.min.split.size.per.node=50000000;

set mapred.min.split.size.per.rack=50000000;

set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;

exists 方式

将not in 改写成not exists方式如下,执行的时候会启动64个reducer去执行reduce,速度相对上面的流程会快,但是这时候出现的了轻微数据倾斜的问题,部分节点执行的较慢,影响了整个任务的执行效率,且生成的文件大小不一致,大小差异很大。

hive -e "

set mapred.reduce.tasks = 64;

insert overwrite table traget_table partition (dt = '$curr_date')

select

a.*

from a

where

a.dt = '$curr_date - 1'

and not exists (select 1 from b where b.dt = '$curr_date' and a.id = b.id)

union all

select

*

from b where b.dt = '$curr_date'

"

更新sql如下,加上 DISTRIBUTE by rand (),distribute by是控制在map端如何拆分数据给reduce端的,hive会根据distribute by后面列,对应reduce的个数进行分发,默认是采用hash算法。rand()方法会生成一个[0,1]之间的随机数,通过随机数进行数据的划分,因为每次都随机的,所以每个reducer上的数据会很均匀。如下的这种设置,reducer会有64个,且每个reducer上的数据量几乎一样。

hive -e "

set mapred.reduce.tasks = 64;

insert overwrite table traget_table partition (dt = '$curr_date')

select

a.*

from a

where

a.dt = '$curr_date - 1'

and not exists (select 1 from b where b.dt = '$curr_date' and a.id = b.id)

union all

select

*

from b where b.dt = '$curr_date'

DISTRIBUTE by rand ();

"

效果如下:

### 在 Hive 中查询或生成 user_logs 数据的方法 在 Hive 中操作 `user_logs` 数据通常涉及创建表、加载数据以及执行查询等操作。以下是具体方法和相关说明: #### 1. 创建表 如果尚未创建 `user_logs` 表,可以使用以下 SQL 语句来定义表结构[^3]: ```sql CREATE TABLE IF NOT EXISTS user_logs ( user_id STRING, action_type STRING, product_id STRING, timestamp STRING ) PARTITIONED BY (date STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'; ``` 上述代码中,`user_logs` 表被定义为分区表,按日期 (`date`) 进行分区,以优化查询性能。 #### 2. 加载数据 假设原始日志文件存储在 HDFS 的某个路径下(如 `/user/hadoop/raw_logs/`),可以通过以下命令将数据加载到 `user_logs` 表中[^1]: ```sql LOAD DATA INPATH '/user/hadoop/raw_logs/' INTO TABLE user_logs PARTITION(date='2023-10-01'); ``` 如果需要动态加载多个分区的数据,可以启用动态分区插入功能[^3]: ```sql SET hive.exec.dynamic.partition=true; SET hive.exec.dynamic.partition.mode=nonstrict; INSERT INTO TABLE user_logs PARTITION(date) SELECT user_id, action_type, product_id, timestamp, SUBSTR(timestamp, 1, 10) AS date FROM raw_logs_table; ``` #### 3. 查询数据 查询 `user_logs` 表中的数据时,可以根据需求选择不同的查询方式。例如,统计某一天的用户访问量: ```sql SELECT COUNT(DISTINCT user_id) AS unique_users FROM user_logs WHERE date = '2023-10-01'; ``` 需要注意的是,在某些情况下(如日志中存在大量重复的 `user_id`),可能会导致数据倾斜问题[^4]。为了避免这种情况,可以采用以下方法优化查询: ```sql SELECT COUNT(DISTINCT CONCAT(user_id, MOD(HASH(user_id), 10))) AS unique_users FROM user_logs WHERE date = '2023-10-01'; ``` 通过引入 `MOD(HASH(user_id), 10)`,可以分散重复的 `user_id`,从而缓解数据倾斜问题。 #### 4. 日志存储与管理 为了确保 Hive 日志的安全性和可维护性,建议将日志存储路径从默认的 `/tmp/atguigu/hive.log` 更改为自定义路径(如 `/opt/module/hive/logs`)[^2]。可以通过修改 `hive-log4j.properties` 文件实现这一目标: ```bash [atguigu@hadoop102 conf]$ cp hive-log4j.properties.template hive-log4j.properties ``` 编辑 `hive-log4j.properties` 文件,将日志路径更改为: ```properties log4j.appender.DRFA.File=/opt/module/hive/logs/hive.log ``` ### 示例代码 以下是一个完整的示例,展示如何从原始日志生成 `user_logs` 表并查询数据: ```sql -- 创建表 CREATE TABLE IF NOT EXISTS user_logs ( user_id STRING, action_type STRING, product_id STRING, timestamp STRING ) PARTITIONED BY (date STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'; -- 启用动态分区 SET hive.exec.dynamic.partition=true; SET hive.exec.dynamic.partition.mode=nonstrict; -- 插入数据 INSERT INTO TABLE user_logs PARTITION(date) SELECT user_id, action_type, product_id, timestamp, SUBSTR(timestamp, 1, 10) AS date FROM raw_logs_table; -- 查询数据 SELECT COUNT(DISTINCT CONCAT(user_id, MOD(HASH(user_id), 10))) AS unique_users FROM user_logs WHERE date = '2023-10-01'; ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值