Spark运用与注意

在java中Spark以main函数的方式运行spark程序,进行大数据读取、计算、统计与相关落盘操作。

遇到的问题与注意点:

  1. spark运行倾斜。

    数据分布不均匀,导致task数据运行时间差异过大,此种情况可以对数据集JavaRDD进行rePartition,rePartition支持自定义的分区方式,从而将数据更好地分布到不同的task中,进行均匀计算。

  2. mongo数据读取及写入过慢。

    采用hdfs落盘与读取可提高大数据读取效率,mongo使用过慢。

  3. SparkConf与SparkSession的紧密运用。

    SparkConf conf = new SparkConf().setAppName("SparkJob");
    conf.set("es.index.auto.create", "true")
          .set("es.nodes", esClusterInfo[1])
          .set("es.nodes.client.only", "true")
          .set("es.batch.write.retry.wait", "30s")
          .set("es.batch.size.bytes", "5mb")
          .set("es.port", Integer.toString(Constants.ES_WRITE_NODE_HTTP_PORT))
          .set("spark.executor.heartbeatInterval","30s");
    
    SparkSession ss = SparkSession.builder()
          .config(conf)
          .enableHiveSupport()
          .getOrCreate();
    
    final Logger logger = ss.log();
    

    SparkConf可以进行spark相关的配置,如spark连接ES、ES读写相关设置。SparkSession是spark快捷使用的上下文,设置Hive支持等,通过getOrCreate()进行获取。
    ss.log获取spark自带的log日志进行打印。

  4. 使用sql进行hive数据读取。

    hive数据的读取使用ss.sql(String sqlStr)进行读取,读取后为Row类型的Dataset数据集,可通过toJavaRDD转换为Java可识别的数据集类型,从而进行map、reduce等操作。

  5. map操作请使用mapPartitions进行分区遍历。

    mapPartitions,设置分区数据,分区内可使用局部变量与频繁创建的对象,避免资源浪费。注意分区数据大小,小心OOM。

  6. Dataset与JavaRDD有很多实用性方法。

    Dataset与JavaRDD可以互相转化。
    Dataset.toJavaRDD();
    Dataset = SparkSession.createDataset(JavaRDD.rdd(), Encoders.bean(T.class));

    Dataset有很多统计方法,如groupBy、max等,可以组合操作。
    JavaRDD有很多mapReduce方法,如filter、map、mapPartition等。

  7. gson设置时间格式化与时区问题。

    在应用过程中发现,由于大数据机器时区不同导致gson序列化时间出现类似8小时差异。
    解决办法采用gson的序列化器与反序列化器,时间格式化时区设置。

    public class DateDeserializer implements JsonDeserializer {
    
        private static final Logger logger = LoggerFactory.getLogger(DateDeserializer.class);
    
        @Override
        public Date deserialize(JsonElement element, Type arg1, JsonDeserializationContext arg2) throws JsonParseException {
    
            String date = element.getAsString();
    
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
            formatter.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
    
            try {
                return formatter.parse(date);
            } catch (ParseException e) {
                logger.error("日期转化出现错误!",e);
                return null;
            }
        }
    }
    
    public class DateSerializer implements JsonSerializer {
    
    private static final Logger logger = LoggerFactory.getLogger(DateSerializer.class);
    
    @Override
    public JsonElement serialize(Date date, Type typeOfSrc, JsonSerializationContext context) {
    
        if(date==null){
            return null;
        }
    
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        formatter.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
    
        return new JsonPrimitive(formatter.format(date));
    
        }
    }
    
    private static DateSerializer dateSerializer = new DateSerializer();
    private static DateDeserializer dateDeserializer = new DateDeserializer();
    
    private static Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, dateSerializer).registerTypeAdapter(Date.class, dateDeserializer).create();
    

    设置GsonBuilder进行类型适配器设置,设置特定时间格式序列化的方式。

  8. ES与spark进行协作。

    JavaEsSpark.saveJsonToEs(JavaRDD, resouceUrlStr, ImmutableMap.of("es.mapping.id", Constants.ES_GOODS_INDEX_ID_NAME));
    

    第一个入参为数据集,第二个为es中数据库表地址,第三个为特别设置项,如选定某一个key作为ES数据的id。
    JavaEsSpark为spark为ES及java选定的ES操作工具,相关配置信息如esNode等都以spark中的config为准,如SparkConfig设置的信息。

  9. ES连接方式,分为HTTP与TCP两种。

    新的ES版本主要支持TCP的客户端方式,使用TransportClient进行操作。

    try {
        logger.info("ES连接初始化TransportClient...,clusterFlag:["+clusterFlag+"]。");
        //设置集群名称
        Settings settings = Settings.builder().put("cluster.name", clusterName).put("transport.type","netty3").build();
        //创建client
        transportClient  = new PreBuiltTransportClient(settings);
        String esIps[] = esServerIps.split(Constants.ES_WRITE_NODE_IPS_SPLIT);
        for (String esIp : esIps) {
            TransportAddress transportAddress =  new InetSocketTransportAddress(InetAddresses.forString(esIp),Constants.ES_WRITE_NODE_TCP_PORT);
            transportClient.addTransportAddresses(transportAddress);
        }
        logger.info("ES连接初始化TransportClient...success");
    }catch (Exception e){
        logger.error("ES连接初始化TransportClient...fail", e);
        throw e;
    }
    
  10. spark读取文件与数据。

    JavaSparkContext javaSparkContext = new JavaSparkContext(ss.sparkContext());
    JavaRDD = javaSparkContext.objectFile(hdfsUrlStr);
    JavaRDD = javaSparkContext.textFile(hdfsUrlStr);
    

    两种文件读取方式,支持对象序列化与json序列化。

你可能感兴趣的:(常见问题)