详情页标题前

阿里云云原生大数据计算服务 MaxCompute计算成本优化-云淘科技

详情页1

本文为您介绍如何通过对SQL作业和MapReduce作业的优化减少计算成本。

您可以在计算前对计算成本进行预估,控制计算成本。详细的预估方法,请参见TCO工具。也可以配置消费预警,预防意料之外的高额费用。预警方法设置请参见消费监控告警。如果计算成本过高,您可以参考下面的方法进行优化,以控制计算成本。

SQL作业计算成本控制

对于SQL计算作业,大部分费用较高的SQL都是由全扫描引起的。另外,调度频繁也会引起SQL作业费用的增加,调度频繁可能会产生任务的堆积,在后付费的情况下会造成排队现象,如果任务多又出现了排队,那么第二天的账单就会异常。通过如下策略进行SQL作业计算成本控制:

  • 避免频繁调度。MaxCompute是批量计算的服务,距离实时的计算服务还是存在一定距离的。如果间隔时间变短,计算频率增加,再加上使用SQL的不良习惯就会导致计算费用飙升,产生费用较高的账单。所以请尽量避免频繁调度,如果要进行频繁调度请通过CostSQL等方式预估一下SQL的开销到底有多大,不然会造成较大预估外的开销。
  • 控制全表扫描。您可以通过以下几种策略来控制全表扫描问题:
    • 设置参数关闭全表扫描功能。目前支持Session级别和Project级别的控制。
      --禁止session 级别全表扫描。
      set odps.sql.allow.fullscan=false;
      --禁止project级别全表扫描。
      SetProject odps.sql.allow.fullscan=false;
    • 使用剪裁。在读数据的时候,只读取查询中需要用到的列,而忽略其他列,避免使用SELECT *引起全表扫描。
      SELECT a,b FROM T WHERE e < 10;

      其中,T包含5个列(a,b,c,d,e),列c,d将会被忽略,只会读取a,b,e列。

    • 使用分区剪裁。分区剪裁是指对分区列指定过滤条件,使得只读取表的部分分区数据,避免全表扫描引起的错误及资源浪费。
      SELECT a,b FROM T WHERE partitiondate='2017-10-01';
    • SQL关键字的优化。计费的SQL关键字包括:JOIN、GROUP BY、ORDER BY、DISTINCT、INSERT INTO。您可以根据以下建议进行优化:
      • 在进行JOIN的时候,一定要先进行分区剪裁再进行JOIN,不然的话就可能会先做全表扫描。分区裁剪失效请参考分区剪裁失效的场景分析。
      • 减少FULL OUTER JOIN 的使用,改为UNION ALL。
        SELECT COALESCE(t1.id, t2.id) AS id, SUM(t1.col1) AS col1
         , SUM(t2.col2) AS col2
        FROM (
         SELECT id, col1
         FROM table1
        ) t1
        FULL OUTER JOIN (
         SELECT id, col2
         FROM table2
        ) t2
        ON t1.id = t2.id
        GROUP BY COALESCE(t1.id, t2.id);
        --可以优化为如下语句。
        SELECT t.id, SUM(t.col1) AS col1, SUM(t.col2) AS col2
        FROM (
         SELECT id, col1, 0 AS col2
         FROM table1
         UNION ALL
         SELECT id, 0 AS col1, col2
         FROM table2
        ) t
        GROUP BY t.id;
      • 在UNION ALL内部尽可能不使用GROUP BY,改为在外层统一GROUP BY。
        SELECT t.id, SUM(t.val) AS val
        FROM (
         SELECT id, SUM(col3) AS val
         FROM table3
         GROUP BY id
         UNION ALL
         SELECT id, SUM(col4) AS val
         FROM table4
         GROUP BY id
        ) t
        GROUP BY t.id;
        可以优化为---------------------------
        SELECT t.id, SUM(t.val) AS val
        FROM (
         SELECT id, col3 AS val
         FROM table3
         UNION ALL
         SELECT id, col4 AS val
         FROM table4
        ) t
        GROUP BY t.id;
      • 临时导出的数据如果需要排序,尽量在导出后使用Excel等工具进行排序,避免使用ORDER BY。
      • 尽量避免使用DISTINCT关键字,改为多套一层GROUP BY。
        SELECT COUNT(DISTINCT id) AS cnt
        FROM table1;
        可以优化为---------------------------
        SELECT COUNT(1) AS cnt
        FROM (
         SELECT id
         FROM table1
         GROUP BY id
        ) t;
      • 尽量避免使用INSERT INTO方式写入数据,可以考虑增加一个分区字段。通过降低SQL复杂度,来节省SQL的费用。
  • 避免使用运行查询的方式预览表数据。如果您想预览表数据,可以使用表预览的方式查看数据,而不会产生费用。如果您使用DataWorks,在数据地图页面,可以预览表以及查看表的详情,具体方法请参见查看表详情。如果您使用MaxCompute Studio,双击表就可以进行表数据预览。
  • 计算时合理的选择工具。由于MaxCompute的查询响应是分钟级,不适合直接用于前端查询,计算出的结果数据同步到外部存储中保存,对于大部分用户来说,关系型数据库是最优先的选择。轻度计算推荐使用MaxCompute,重度计算(即直接出最终结果。前端展示时,不做任何判断、聚合、关联字典表、甚至不带WHERE条件)推荐使用RDS等关系型数据库。

MapReduce作业计算成本控制

通过如下策略进行MapReduce作业计算成本控制:

  • 设置合理的参数。
    • split size

      Map默认的split size是256 MB,split size的大小决定了Map个数多少,如果用户的代码逻辑比较耗时,Map需要较长时间结束,可以通过JobConf#setSplitSize方法适当调小split size。然而split size也不宜设置太小,否则会占用过多的计算资源。

    • MapReduce Reduce Instance

      单个job默认Reduce Instance个数为Map Instance个数的1/4,用户设置作为最终的Reduce Instance个数,范围[0, 2000],数量越多,计算时消耗越多,成本越高,应合理设置。

  • MapReduce减少中间环节

    如果有多个MapReduce作业之间有关联关系,前一个作业的输出是后一个作业的输入,可以考虑采用Pipeline的模式,将多个串行的MapReduce作业合并为一个,这样可以用更少的作业数量完成同样的任务。一方面减少中间表造成的多余磁盘IO,提升性能;另一方面减少作业数量使调度更加简单,增强流程的可维护性,具体使用方法请参见Pipeline示例。

  • 对输入表列裁剪
    对于列数特别多的输入表,Map阶段处理只需要其中的某几列,可以通过在添加输入表时明确指定输入的列,减少输入量。例如只需要c1,c2列,可以参考如下设置。

    InputUtils.addTable(TableInfo.builder().tableName("wc_in").cols(new String[]{"c1","c2"}).build(), job);

    设置后,在Map中读取到的Record就只有c1,c2列,如果之前是使用列名获取Record数据,不会有影响,而用下标获取的需要注意这个变化。

  • 避免资源重复读取

    资源的读取尽量放置到Setup阶段读取,避免资源多次读取的性能损失,另外系统也有64次读取的限制,资源的读取请参见使用资源示例。

  • 减少对象构造开销
    对于Map、Reduce阶段每次都会用到的Java对象,避免在Map/Reduce函数里构造,可以放到Setup阶段,避免多次构造产生的开销。

    {
        ...
        Record word;
        Record one;
    
        public void setup(TaskContext context) throws IOException {
    
    
          // 创建一次就可以,避免在map中每次重复创建。
          word = context.createMapOutputKeyRecord();
    
          one = context.createMapOutputValueRecord();
    
          one.set(new Object[]{1L});
    
        }
        ...
    }
  • 合理使用Combiner
    如果Map的输出结果中有很多重复的Key,可以合并后输出,Combiner后可以减少网络带宽传输和一定Shuffle的开销。如果Map输出本来就没有多少重复的,就不要用Combiner,用了反而可能会有一些额外的开销。Combiner实现的是和Reducer相同的接口,例如一个WordCount程序的Combiner可以定义如下。

    /**
       * A combiner class that combines map output by sum them.
       */
      public static class SumCombiner extends ReducerBase {
    
        private Record count;
    
        @Override
        public void setup(TaskContext context) throws IOException {
          count = context.createMapOutputValueRecord();
        }
    
        @Override
        public void reduce(Record key, Iterator values, TaskContext context)
            throws IOException {
          long c = 0;
          while (values.hasNext()) {
            Record val = values.next();
            c += (Long) val.get(0);
          }
          count.set(0, c);
          context.write(key, count);
        }
      }
  • 合理选择Partition Column或自定义Partitioner
    合理选择Partition Columns,可以使用JobConf#setPartitionColumns这个方法进行设置(默认是Key Schema定义的Column),设置后数据将按照指定的列计算HASH值分发到Reduce中, 避免数据倾斜导致作业长尾现象,如有必要也可以选择自定义Partitioner,自定义Partitioner的使用方法如下。

    import com.aliyun.odps.mapred.Partitioner;
    
    public static class MyPartitioner extends Partitioner {
    
    @Override
    public int getPartition(Record key, Record value, int numPartitions) {
      // numPartitions即对应reducer的个数
      // 通过该函数决定map输出的key value去往哪个reducer。
      String k = key.get(0).toString();
      return k.length() % numPartitions;
    }
    }

    在jobconf里进行设置如下。

    jobconf.setPartitionerClass(MyPartitioner.class)

    需要在jobconf里明确指定Reducer的个数。

    jobconf.setNumReduceTasks(num)
  • 合理使用JVM内存参数

    过于追求调优,把MapReduce任务内存设置过大也会造成成本上升。标准配置是1 Core 4G ,odps.stage.reducer.jvm.mem=4006,当CPU与内存比超过1:4时,对应的费用也会大幅升高。

内容没看懂? 不太想学习?想快速解决? 有偿解决: 联系专家

阿里云企业补贴进行中: 马上申请

腾讯云限时活动1折起,即将结束: 马上收藏

同尘科技为腾讯云授权服务中心。

购买腾讯云产品享受折上折,更有现金返利:同意关联,立享优惠

转转请注明出处:https://www.yunxiaoer.com/159276.html

(0)
上一篇 2023年12月10日
下一篇 2023年12月10日
详情页2

相关推荐

  • 阿里云日志服务SLS导入OSS数据-云淘科技

    本文介绍导入OSS数据到日志服务所涉及的使用限制。 采集限制 限制项 说明 单个文件大小 如果是Snappy压缩格式的文件(非Framing-format格式),最大支持350 MB。 如果是其他格式的文件,最大支持5 GB。 单个文件大小超过限制时,导入任务会忽略整个文件。 单条数据大小 单条数据最大为3 MB。数据大小超过限制时,该数据会被丢弃。 数据处…

    阿里云日志服务SLS 2023年12月10日
  • 阿里云日志服务SLS术语表-云淘科技

    本文介绍日志服务所涉及的术语。 基础资源 术语 说明 项目(Project) 项目是日志服务的资源管理单元,是进行多用户隔离与访问控制的主要边界。更多信息,请参见项目(Project)。 日志库(Logstore) 日志库是日志服务中日志数据的采集、存储和查询单元。更多信息,请参见日志库(Logstore)。 时序库(MetricStore) 时序库是日志服…

    阿里云日志服务SLS 2023年12月10日
  • 信息流广告,信息流部分建议宽度830px,只针对默认列表样式,顺序随机
  • 阿里云RDS数据库设置可维护时间段-云淘科技

    为保障云数据库RDS实例的稳定性,后端系统会不定期对实例进行维护操作。默认可维护时间段为02:00~06:00,您可以根据业务规律,将可维护时间段设置在业务低峰期,以免维护过程中可能对业务造成的影响。 其他引擎设置可维护时间段请参见: SQL Server设置可维护时间段 PostgreSQL设置可维护时间段 MariaDB设置可维护时间段 注意事项 在进行…

    阿里云数据库 2023年12月9日
  • 阿里云云原生大数据计算服务 MaxComputeGET_JSON_OBJECT-云淘科技

    在一个标准JSON字符串中,按照指定方式抽取指定的字符串。 使用说明 GET_JSON_OBJECT函数的作用是在一个标准JSON字符串中,按照JSON PATH抽取指定的字符串。当前函数的入参支持两种类型: 入参为JSON类型:基于最新支持的JSON数据类型,采用更为规范的JSON PATH。 入参为STRING类型:原有的JSON PATH解析方式。 入…

  • 阿里云RDS数据库查询SQL Server数据库(tds_fdw)-云淘科技

    您可以使用tds_fdw插件访问SQL Server数据库的数据。 前提条件 请确保实例大版本满足要求,本插件具体支持的实例大版本,请参见支持插件列表。 如实例大版本已满足要求,但仍提示不支持,请升级内核小版本,具体操作,请参见升级内核小版本。 将RDS PostgreSQL的专有网络网段(例如172.xx.xx.xx/16)添加到SQL Server实例的…

    阿里云数据库 2023年12月9日

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信
本站为广大会员提供阿里云、腾讯云、华为云、百度云等一线大厂的购买,续费优惠,保证底价,买贵退差。