什么是数据倾斜 数据倾斜的表现 发生数据倾斜的原因 如何解决数据倾斜

数据倾斜

  • 什么是数据倾斜
  • 数据倾斜的表现
  • 发生数据倾斜的原因
  • 如何解决数据倾斜
    • 聚合类group by操作,发生数据倾斜
    • 空值产生的数据倾斜
    • Reduce join 改为Map join
    • 少用count(distinct),先用group 去重 再count子查询,
    • 特殊值分开处理法
    • 大表 join 大表
    • 不同数据类型关联产生数据倾斜
    • 多表 union all 会优化成一个 job
    • 优化in/exists语句
    • 排序选择

什么是数据倾斜

  • Hadoop能够进行对海量数据进行批处理的核心,在于它的分布式思想,也就是多台服务器(节点)组成集群,进行分布式的数据处理
  • 举例:如果有10亿数据,一台电脑可能要10小时,现在集群有10台,可能1小时就够了,但是有可能大量的数据集中到一台或几台上,要5小时,发生了数据倾斜

数据倾斜的表现

  • Mapreduce任务
    • reduce阶段 卡在99.99%不动
    • 各种container报错OOM(内存溢出)
    • 读写数据量很大,超过其他正常reduce
  • spark任务
    • 个别task执行很慢
    • 单个执行特别久
    • shuffle出错
    • sparkstreaming做实时算法使,会有executor出现内存溢出,但是其他的使用率很低

发生数据倾斜的原因

  • shuffle是按照key,来进行values的数据的输出、拉取和聚合的,一旦发生shuffle,所有相同key的值就会拉到一个或几个节点上,个别key对应的数据比较多,就容易发生单个节点处理数据量爆增的情况。
  • key分布不均匀
    • 存在大量相同值的数据
    • 存在大量异常值或者空值
  • 业务数据本身的特性
    • 例如某个分公司或某个城市订单量大幅提升几十倍甚至几百倍,对该城市的订单统计聚合时,容易发生数据倾斜。
  • 某些SQL语句本身就有数据倾斜
    • 两个表中关联字段存在大量空值(解决方法:去除或者加随机数),或是关联字段的数据不统一(解决方法:把数字类型转为字符串类型,统一大小写)
    • join 一个key集中的小表 (解决方法:reduce join 改成 map join)
    • group by维度过小 某值的数量过多 (解决方法:两阶段聚合,放粗粒度)
    • count distinct 某特殊值过多 (解决方法:先用group by)
  • 数据频率倾斜——某一个区域的数据量要远远大于其他区域。
  • 数据大小倾斜——部分记录的大小远远大于平均值。

如何解决数据倾斜

聚合类group by操作,发生数据倾斜

  • map段部分聚合

    • 开启Map端聚合参数设置set hive.map.aggr=true
    • 在Map端进行聚合操作的条目数目set hive.grouby.mapaggr.checkinterval=100000
    • 有数据倾斜的时候进行负载均衡(默认是false)set hive.groupby.skewindata = true
  • 阶段拆分-两阶段聚合 需要聚合的key前加一个随机数的前后缀,这样就均匀了,之后再按照原始的key聚合一次

  • 生成的查询计划有两 个 MapReduce 任务。在第一个 MapReduce 中,map 的输出结果集合会随机分布到 reduce 中, 每个 reduce 做部分聚合操作,并输出结果。相同的 Group By Key 有可 能分发到不同的 reduce 中,从而达到负载均衡的目的;第二个 MapReduce 任务再根据预处 理的数据结果按照 Group By Key 分布到 reduce 中(这个过程可以保证相同的 Group By Key 分布到同一个 reduce 中),最后完成最终的聚合操作。

  • 假设 key = 水果
    select count(substr(a.key,1,2)) as key
    from(
    	select concat(key,'_',cast(round(10*rand())+1 as string)) tmp
    	from table
    	group by tmp
    )a
    group by key
    

空值产生的数据倾斜

  • 1.在查询的时候,过滤掉所有为NULL的数据,比如:
    SELECT * FROM log a
    JOIN bmw_users b ON a.user_id IS NOT NULL AND a.user_id = b.user_id
    UNION ALL
    SELECT *FROM log a WHERE a.user_id IS NULL;
    
    2.查询出空值并给其赋上随机数,避免了key值为空(数据倾斜中常用的一种技巧)
    SELECT *FROM log a
    LEFT JOIN bmw_users b ON 
    CASE WHEN a.user_id IS NULL THEN concat(‘dp_hive’, rand()) ELSE a.user_id END = b.user_id;
    

Reduce join 改为Map join

  • 适用于小表和大表 join,将较小RDD中的数据直接通过collect算子拉取到Driver端的内存中来,然后对其创建一个Broadcast变量;接着对另外RDD执行map类算子,在算子函数内,从Broadcast变量中获取较小RDD 的全量数据,与当前RDD的每一条数据按照连接key进行比对,如果连接key相同的话,那么就将两个RDD的数据用你需要的方式连接起来。
  • 设置自动选择MapJoin set hive.auto.convert.join = true;默认为true
  • reduce join: 先将所有相同的key,对应的values,汇聚到一个task中,然后再进行join。
  • map reduce:broadcast出去那个小表的数据以后,就会在每个executor的block manager中都驻留一份+map算子来实现与join同样的效果。不会发生shuffe,从根本上杜绝了join操作可能导致的数据倾斜的问题

少用count(distinct),先用group 去重 再count子查询,

  • 采用sum() group by的方式来替换count(distinct)完成计算。

  • select count(distinct a) from test ;
    select count x.a 
    from (select a from test group by a ) x 
    
    select a, count(distinct b) as c from tbl group by a;
    select a, count(*) as c from (select a, b from tbl group by a, b) group by a;
    

特殊值分开处理法

  • 当需要把用户表和日志表关联起来时,再日志表中有很多没注册的用户表,可以分开处理

  • select *from
    (select * from logs where user_id = 0)a
    join
    (select * from users where user_id = 0)b
    on a.user_id = b.user_id
    union all
    select * from logs a join users b on a.user_id <> 0 and a.user_id = b.user_id;
    

大表 join 大表

  • 将有大表中倾斜Key对应的数据集单独抽取出来加上随机前缀,另外一个RDD每条数据分别与随机前缀结合形成新的RDD笛卡尔积,相当于将其数据增到到原来的N倍,N即为随机前缀的总个数)然后将二者Join后去掉前缀。然后将不包含倾斜Key的剩余数据进行Join。最后将两次Join的结果集通过union合并,即可得到全部Join结果。
  • RDD扩容

不同数据类型关联产生数据倾斜

  • 一张表 s8_log,每个商品一条记录,要和商品表关联。**s8_log 中有字符串商品 id,也有数字的商品 id。**字符串商品 id 类型是 string 的,但商品中的数字 id 是 bigint 的。

  • 问题的原因是把 s8_log 的商品 id 转成数字 id 做 Hash(数字的 Hash 值为其本身,相同的字符串的 Hash 也不同)来分配 Reducer,所以相同字符串 id 的 s8_log,都到一个 Reducer 上了。

  • -- 把数字类型转换成字符串类型
    SELECT *
    FROM s8_log a
    LEFT JOIN r_auction_auctions b ON a.auction_id = CAST(b.auction_id AS string);
    

多表 union all 会优化成一个 job

  • 推广效果表要和商品表关联,效果表中的 auction id 列既有商品 id,也有数字 id,和商品表关联得到商品的信息。
SELECT *
FROM effect a
  JOIN (
      SELECT auction_id AS auction_id
      FROM auctions
      UNION ALL
      SELECT auction_string_id AS auction_id
      FROM auctions
  ) b
  ON a.auction_id = b.auction_id;
  • 结论: 这样子比分别过滤数字 id,字符串 id ,然后分别和商品表关联性能要好。这样写的好处:1个 MR 作业,商品表只读取一次,推广效果表只读取一次。把这个 sql 换成 MR 代码的话,map 的时候,把 a 表的记录打上标签 a ,商品表记录每读取一条,打上标签 t,变成两个 对,。所以商品表的 HDFS(Hadoop Distributed File System) 读只会是一次。

  • 问题:比如推广效果表要和商品表关联,效果表中的 auction_id 列既有 32 为字符串商 品 id,也有数字 id,和商品表关联得到商品的信息。 比分别过滤数字 id,字符串 id 然后分别和商品表关联性能要好。

	SELECT * FROM effect a 
	JOIN 
	(SELECT auction_id AS auction_id FROM auctions 
	UNION All 
	SELECT auction_string_id AS auction_id FROM auctions) b 
	ON a.auction_id=b.auction_id;	
  • 场景:有一张user表,为卖家每天收到表,user_id,ds(日期)为key,属性有主营类目,指标有交易金额,交易笔数。每天要取前10天的总收入,总笔数,和最近一天的主营类目。
   SELECT user_id, substr(MAX(CONCAT(ds, cat)), 9) AS main_cat, SUM(qty), SUM(amt) FROM users
   WHERE ds BETWEEN 20120301 AND 20120329
   GROUP BY user_id

优化in/exists语句

  • hive1.2.1也支持in/exists操作,但还是推荐使用hive的一个高效替代方案:left semi join

排序选择

  • cluster by: 对同一字段分桶并排序,不能和sort by连用;
  • distribute by + sort by: 分桶,保证同一字段值只存在一个结果文件当中,结合sort by 保证每个reduceTask结果有序;
  • sort by: 单机排序,单个reduce结果有序
  • order by:全局排序,缺陷是只能使用一个reduce

你可能感兴趣的:(数据仓库,面经,数据库,大数据)