当前位置:   article > 正文

Doris-06-监控、报警以及Doris的优化_doris enable_query_memory_overcommit

doris enable_query_memory_overcommit

监控和报警

Doris 可以使用 Prometheus 和 Grafana 进行监控和采集,官网下载最新版即可。

  • Prometheus 官网下载:https://prometheus.io/download/
  • Grafana 官网下载:https://grafana.com/grafana/download

Doris 的监控数据通过 FE 和 BE 的 http 接口向外暴露。监控数据以 key-value 的文本形式对外展现。每个 key 还可能有不同的 Label 加以区分。当用户搭建好 Doris 后,可以在浏览器,通过以下接口访问监控数据.

Frontend: fe_host:fe_http_port/metrics,如 http://hadoop1:8030/metrics

Backend: be_host:be_web_server_port/metrics,如 http://hadoop1:8040/metrics

整个监控架构如下图:

prometheus的配置修改:配置两个 targets 分别配置 FE 和 BE,并且定义 labels 和 groups 指定组。如果有多个集群则再加 -job_name 标签,进行相同配置:

vim /opt/module/prometheus-2.26.0/prometheus.yml 
# my global config
global:
  scrape_interval:     15s # 全局的采集间隔,默认是 1m,这里设置为 15s
  evaluation_interval: 15s # 全局的规则触发间隔,默认是 1m,这里设置 15s

# Alertmanager configuration
alerting:
  alertmanagers:
  - static_configs:
    - targets:
      # - alertmanager:9093

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'PALO_CLUSTER' # 每一个 Doris 集群,我们称为一个 job。这里可以给 job 取一个名字,作为 Doris 集群在监控系统中的名字。
    metrics_path: '/metrics' # 这里指定获取监控项的 restful api。配合下面的 targets 中的 host:port,Prometheus 最终会通过 host:port/metrics_path 来采集监控项。
    static_configs: # 这里开始分别配置 FE 和 BE 的目标地址。所有的 FE 和 BE 都分别写入各自的 group 中。
      - targets: ['fe_host1:8030', 'fe_host2:8030', 'fe_host3:8030']
        labels:
          group: fe # 这里配置了 fe 的 group,该 group 中包含了 3 个 Frontends

      - targets: ['be_host1:8040', 'be_host2:8040', 'be_host3:8040']
        labels:
          group: be # 这里配置了 be 的 group,该 group 中包含了 3 个 Backends

  - job_name: 'PALO_CLUSTER_2' # 我们可以在一个 Prometheus 中监控多个 Doris 集群,这里开始另一个 Doris 集群的配置。配置同上,以下略。
    metrics_path: '/metrics'
    static_configs: 
      - targets: ['fe_host1:8030', 'fe_host2:8030', 'fe_host3:8030']
        labels:
          group: fe 

      - targets: ['be_host1:8040', 'be_host2:8040', 'be_host3:8040']
        labels:
          group: be 
              
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

之后可以启动Prometheus和Grafana进行监控查看。

优化

查看 QueryProfile

利用查询执行的统计结果,可以更好的帮助我们了解 Doris 的执行情况,并有针对性的进行相应 Debug 与调优工作。

FE 将查询计划拆分成为 Fragment 下发到 BE 进行任务执行。BE 在执行 Fragment 时记录了运行状态时的统计值,并将 Fragment 执行的统计信息输出到日志之中。 FE 也可以通过开关将各个 Fragment 记录的这些统计值进行搜集,并在 FE 的 Web 页面上打印结果。

使用方式:

  • 开启 profile:

    set enable_profile=true;
    
    • 1
  • 执行一个查询:

    SELECT t1 FROM test JOIN test2 where test.t1 = test2.t2;
    
    • 1
  • 通过 FE 的 UI 查看:

    http://hadoop1:8030/QueryProfile/

参数说明:

(1)Fragment

  • AverageThreadTokens: 执行Fragment使用线程数目,不包含线程池的使用情况
  • Buffer Pool PeakReservation: Buffer Pool使用的内存的峰值
  • MemoryLimit: 查询时的内存限制
  • PeakMemoryUsage: 整个Instance在查询时内存使用的峰值
  • RowsProduced: 处理列的行数

(2)BlockMgr

  • BlocksCreated: BlockMgr创建的Blocks数目
  • BlocksRecycled: 重用的Blocks数目
  • BytesWritten: 总的落盘写数据量
  • MaxBlockSize: 单个Block的大小
  • TotalReadBlockTime: 读Block的总耗时

(3)DataStreamSender

  • BytesSent: 发送的总数据量 = 接受者 * 发送数据量
  • IgnoreRows: 过滤的行数
  • LocalBytesSent: 数据在Exchange过程中,记录本机节点的自发自收数据量
  • OverallThroughput: 总的吞吐量 = BytesSent / 时间
  • SerializeBatchTime: 发送数据序列化消耗的时间
  • UncompressedRowBatchSize: 发送数据压缩前的RowBatch的大小

(4)ODBC_TABLE_SINK

  • NumSentRows: 写入外表的总行数
  • TupleConvertTime: 发送数据序列化为Insert语句的耗时
  • ResultSendTime: 通过ODBC Driver写入的耗时

(5)EXCHANGE_NODE

  • BytesReceived: 通过网络接收的数据量大小
  • MergeGetNext: 当下层节点存在排序时,会在EXCHANGE NODE进行统一的归并排序,输出有序结果。该指标记录了Merge排序的总耗时,包含了MergeGetNextBatch耗时。
  • MergeGetNextBatch:Merge节点取数据的耗时,如果为单层Merge排序,则取数据的对象为网络队列。若为多层Merge排序取数据对象为Child Merger。
  • ChildMergeGetNext: 当下层的发送数据的Sender过多时,单线程的Merge会成为性能瓶颈,Doris会启动多个Child Merge线程并行归并排序。记录了Child Merge的排序耗时 该数值是多个线程的累加值。
  • ChildMergeGetNextBatch: Child Merge节点从取数据的耗时,如果耗时过大,可能的瓶颈为下层的数据发送节点。
  • DataArrivalWaitTime: 等待Sender发送数据的总时间
  • FirstBatchArrivalWaitTime: 等待第一个batch从Sender获取的时间
  • DeserializeRowBatchTimer: 反序列化网络数据的耗时
  • SendersBlockedTotalTimer(*): DataStreamRecv的队列的内存被打满,Sender端等待的耗时
  • ConvertRowBatchTime: 接收数据转为RowBatch的耗时
  • RowsReturned: 接收行的数目
  • RowsReturnedRate: 接收行的速率

(6)SORT_NODE

  • InMemorySortTime: 内存之中的排序耗时
  • InitialRunsCreated: 初始化排序的趟数(如果内存排序的话,该数为1)
  • SortDataSize: 总的排序数据量
  • MergeGetNext: MergeSort从多个sort_run获取下一个batch的耗时 (仅在落盘时计时)
  • MergeGetNextBatch: MergeSort提取下一个sort_run的batch的耗时 (仅在落盘时计时)
  • TotalMergesPerformed: 进行外排merge的次数

(7)AGGREGATION_NODE

  • PartitionsCreated: 聚合查询拆分成Partition的个数
  • GetResultsTime: 从各个partition之中获取聚合结果的时间
  • HTResizeTime: HashTable进行resize消耗的时间
  • HTResize: HashTable进行resize的次数
  • HashBuckets: HashTable中Buckets的个数
  • HashBucketsWithDuplicate: HashTable有DuplicateNode的Buckets的个数
  • HashCollisions: HashTable产生哈希冲突的次数
  • HashDuplicateNodes: HashTable出现Buckets相同DuplicateNode的个数
  • HashFailedProbe: HashTable Probe操作失败的次数
  • HashFilledBuckets: HashTable填入数据的Buckets数目
  • HashProbe: HashTable查询的次数
  • HashTravelLength: HashTable查询时移动的步数

(8)HASH_JOIN_NODE

  • ExecOption: 对右孩子构造HashTable的方式(同步or异步),Join中右孩子可能是表或子查询,左孩子同理
  • BuildBuckets: HashTable中Buckets的个数
  • BuildRows: HashTable的行数
  • BuildTime: 构造HashTable的耗时
  • LoadFactor: HashTable的负载因子(即非空Buckets的数量)
  • ProbeRows: 遍历左孩子进行Hash Probe的行数
  • ProbeTime: 遍历左孩子进行Hash Probe的耗时,不包括对左孩子RowBatch调用GetNext的耗时
  • PushDownComputeTime: 谓词下推条件计算耗时
  • PushDownTime: 谓词下推的总耗时,Join时对满足要求的右孩子,转为左孩子的in查询

(9)CROSS_JOIN_NODE

  • ExecOption: 对右孩子构造RowBatchList的方式(同步or异步)
  • BuildRows: RowBatchList的行数(即右孩子的行数)
  • BuildTime: 构造RowBatchList的耗时
  • LeftChildRows: 左孩子的行数
  • LeftChildTime: 遍历左孩子,和右孩子求笛卡尔积的耗时,不包括对左孩子RowBatch调用GetNext的耗时

(10)UNION_NODE

  • MaterializeExprsEvaluateTime: Union两端字段类型不一致时,类型转换表达式计算及物化结果的耗时

(11)ANALYTIC_EVAL_NODE

  • EvaluationTime: 分析函数(窗口函数)计算总耗时
  • GetNewBlockTime: 初始化时申请一个新的Block的耗时,Block用来缓存Rows窗口或整个分区,用于分析函数计算
  • PinTime: 后续申请新的Block或将写入磁盘的Block重新读取回内存的耗时
  • UnpinTime: 对暂不需要使用的Block或当前操作符内存压力大时,将Block的数据刷入磁盘的耗时

(12)OLAP_SCAN_NODE

OLAP_SCAN_NODE 节点负责具体的数据扫描任务。一个 OLAP_SCAN_NODE 会生成一个或多个 OlapScanner 。每个 Scanner 线程负责扫描部分数据。查询中的部分或全部谓词条件会推送给 OLAP_SCAN_NODE。这些谓词条件中一部分会继续下推给存储引擎,以便利用存储引擎的索引进行数据过滤。另一部分会保留在 OLAP_SCAN_NODE 中,用于过滤从存储引擎中返回的数据。

OLAP_SCAN_NODE 节点的 Profile 通常用于分析数据扫描的效率,依据调用关系分为 OLAP_SCAN_NODE、OlapScanner、SegmentIterator 三层。

OLAP_SCAN_NODE (id=0):(Active: 1.2ms, % non-child: 0.00%)
  - BytesRead: 265.00 B                 # 从数据文件中读取到的数据量。假设读取到了是10个32位整型,则数据量为 10 * 4B = 40 Bytes。这个数据仅表示数据在内存中全展开的大小,并不代表实际的 IO 大小。 
  - NumDiskAccess: 1                    # 该 ScanNode 节点涉及到的磁盘数量。
  - NumScanners: 20                     # 该 ScanNode 生成的 Scanner 数量。
  - PeakMemoryUsage: 0.00               # 查询时内存使用的峰值,暂未使用
  - RowsRead: 7                         # 从存储引擎返回到 Scanner 的行数,不包括经 Scanner 过滤的行数。
  - RowsReturned: 7                     # 从 ScanNode 返回给上层节点的行数。
  - RowsReturnedRate: 6.979K /sec       # RowsReturned/ActiveTime
  - TabletCount : 20                    # 该 ScanNode 涉及的 Tablet 数量。
  - TotalReadThroughput: 74.70 KB/sec   # BytesRead除以该节点运行的总时间(从Open到Close),对于IO受限的查询,接近磁盘的总吞吐量。
  - ScannerBatchWaitTime: 426.886us     # 用于统计transfer 线程等待scaner 线程返回rowbatch的时间。
  - ScannerWorkerWaitTime: 17.745us     # 用于统计scanner thread 等待线程池中可用工作线程的时间。
  OlapScanner:
    - BlockConvertTime: 8.941us         # 将向量化Block转换为行结构的 RowBlock 的耗时。向量化 Block 在 V1 中为 VectorizedRowBatch,V2中为 RowBlockV2。
    - BlockFetchTime: 468.974us         # Rowset Reader 获取 Block 的时间。
    - ReaderInitTime: 5.475ms           # OlapScanner 初始化 Reader 的时间。V1 中包括组建 MergeHeap 的时间。V2 中包括生成各级 Iterator 并读取第一组Block的时间。
    - RowsDelFiltered: 0                # 包括根据 Tablet 中存在的 Delete 信息过滤掉的行数,以及 unique key 模型下对被标记的删除行过滤的行数。
    - RowsPushedCondFiltered: 0         # 根据传递下推的谓词过滤掉的条件,比如 Join 计算中从 BuildTable 传递给 ProbeTable 的条件。该数值不准确,因为如果过滤效果差,就不再过滤了。
    - ScanTime: 39.24us                 # 从 ScanNode 返回给上层节点的时间。
    - ShowHintsTime_V1: 0ns             # V2 中无意义。V1 中读取部分数据来进行 ScanRange 的切分。
    SegmentIterator:
      - BitmapIndexFilterTimer: 779ns   # 利用 bitmap 索引过滤数据的耗时。
      - BlockLoadTime: 415.925us        # SegmentReader(V1) 或 SegmentIterator(V2) 获取 block 的时间。
      - BlockSeekCount: 12              # 读取 Segment 时进行 block seek 的次数。
      - BlockSeekTime: 222.556us        # 读取 Segment 时进行 block seek 的耗时。
      - BlocksLoad: 6                   # 读取 Block 的数量
      - CachedPagesNum: 30              # 仅 V2 中,当开启 PageCache 后,命中 Cache 的 Page 数量。
      - CompressedBytesRead: 0.00       # V1 中,从文件中读取的解压前的数据大小。V2 中,读取到的没有命中 PageCache 的 Page 的压缩前的大小。
      - DecompressorTimer: 0ns          # 数据解压耗时。
      - IOTimer: 0ns                    # 实际从操作系统读取数据的 IO 时间。
      - IndexLoadTime_V1: 0ns           # 仅 V1 中,读取 Index Stream 的耗时。
      - NumSegmentFiltered: 0           # 在生成 Segment Iterator 时,通过列统计信息和查询条件,完全过滤掉的 Segment 数量。
      - NumSegmentTotal: 6              # 查询涉及的所有 Segment 数量。
      - RawRowsRead: 7                  # 存储引擎中读取的原始行数。详情见下文。
      - RowsBitmapIndexFiltered: 0      # 仅 V2 中,通过 Bitmap 索引过滤掉的行数。
      - RowsBloomFilterFiltered: 0      # 仅 V2 中,通过 BloomFilter 索引过滤掉的行数。
      - RowsKeyRangeFiltered: 0         # 仅 V2 中,通过 SortkeyIndex 索引过滤掉的行数。
      - RowsStatsFiltered: 0            # V2 中,通过 ZoneMap 索引过滤掉的行数,包含删除条件。V1 中还包含通过 BloomFilter 过滤掉的行数。
      - RowsConditionsFiltered: 0       # 仅 V2 中,通过各种列索引过滤掉的行数。
      - RowsVectorPredFiltered: 0       # 通过向量化条件过滤操作过滤掉的行数。
      - TotalPagesNum: 30               # 仅 V2 中,读取的总 Page 数量。
      - UncompressedBytesRead: 0.00     # V1 中为读取的数据文件解压后的大小(如果文件无需解压,则直接统计文件大小)。V2 中,仅统计未命中 PageCache 的 Page 解压后的大小(如果Page无需解压,直接统计Page大小)
      - VectorPredEvalTime: 0ns         # 向量化条件过滤操作的耗时。
      - ShortPredEvalTime: 0ns          # 短路谓词过滤操作的耗时。
      - PredColumnReadTime: 0ns         # 谓词列读取的耗时。
      - LazyReadTime: 0ns               # 非谓词列读取的耗时。
      - OutputColumnTime: 0ns           # 物化列的耗时。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

(13)Buffer pool

  • AllocTime: 内存分配耗时
  • CumulativeAllocationBytes: 累计内存分配的量
  • CumulativeAllocations: 累计的内存分配次数
  • PeakReservation: Reservation的峰值
  • PeakUnpinnedBytes: unpin的内存数据量
  • PeakUsedReservation: Reservation的内存使用量
  • ReservationLimit: BufferPool的Reservation的限制量

合理设置分桶分区数

  • 一个表的 Tablet 总数量等于 (Partition num * Bucket num)。
  • 一个表的 Tablet 数量,在不考虑扩容的情况下,推荐略多于整个集群的磁盘数量。
  • 单个 Tablet 的数据量理论上没有上下界,但建议在 1G - 10G 的范围内。如果单个 Tablet 数据量过小,则数据的聚合效果不佳,且元数据管理压力大。如果数据量过大,则不利于副本的迁移、补齐,且会增加 Schema Change 或者 Rollup 操作失败重试的代价(这些操作失败重试的粒度是 Tablet)。
  • 当 Tablet 的数据量原则和数量原则冲突时,建议优先考虑数据量原则。
  • 在建表时,每个分区的 Bucket 数量统一指定。但是在动态增加分区时(ADD PARTITION),可以单独指定新分区的 Bucket 数量。可以利用这个功能方便的应对数据缩小或膨胀。
  • 一个 Partition 的 Bucket 数量一旦指定,不可更改。所以在确定 Bucket 数量时,需要预先考虑集群扩容的情况。比如当前只有 3 台 host,每台 host 有 1 块盘。如果Bucket 的数量只设置为 3 或更小,那么后期即使再增加机器,也不能提高并发度。

举一些例子:假设在有 10 台 BE,每台 BE 一块磁盘的情况下。如果一个表总大小为 500MB,则可以考虑 4-8 个分片。5GB:8-16 个。50GB:32 个。500GB:建议分区,每个分区大小在 50GB 左右,每个分区 16-32 个分片。5TB:建议分区,每个分区大小在50GB 左右,每个分区 16-32 个分片。

注:表的数据量可以通过 show data 命令查看,结果除以副本数,即表的数据量。

向量化执行引擎

过去 Apache Doris 的 SQL 执行引擎是基于行式内存格式以及基于传统的火山模型进行设计的,在进行 SQL 算子与函数运算时存在非必要的开销,导致 Apache Doris 执行引擎的效率受限,并不适应现代 CPU 的体系结构。向量化执行引擎的目标是替换 Apache Doris 当前的行式 SQL 执行引擎,充分释放现代 CPU 的计算能力,突破在 SQL 执行引擎上的性能限制,发挥出极致的性能表现。基于现代 CPU 的特点与火山模型的执行特点,向量化执行引擎重新设计了在列式存储系统的 SQL 执行引擎:

  • 重新组织内存的数据结构,用 Column 替换 Tuple,提高了计算时 Cache 亲和度,分支预测与预取内存的友好度
  • 分批进行类型判断,在本次批次中都使用类型判断时确定的类型,将每一行类型判断的虚函数开销分摊到批量级别。
  • 通过批级别的类型判断,消除了虚函数的调用,让编译器有函数内联以及 SIMD 优化的机会

从而大大提高了 CPU 在 SQL 执行时的效率,提升了 SQL 查询的性能。

可以参考:https://blog.csdn.net/qq_35423190/article/details/123129172

(1)使用方式

set enable_vectorized_engine = true;
set batch_size = 4096;
  • 1
  • 2

batch_size 代表了 SQL 算子每次进行批量计算的行数。Doris 默认的配置为 1024,这个配置的行数会影响向量化执行引擎的性能与 CPU 缓存预取的行为。官方推荐配置为 4096。

(2)准备测试表

CREATE TABLE IF NOT EXISTS test_db.user
(
    `user_id` LARGEINT NOT NULL COMMENT "用户 id",
    `username` VARCHAR(50) NOT NULL COMMENT "用户昵称",
    `city` VARCHAR(20) NOT NULL COMMENT "用户所在城市",
    `age` SMALLINT NOT NULL COMMENT "用户年龄",
    `sex` TINYINT NOT NULL COMMENT "用户性别",
    `phone` LARGEINT NOT NULL COMMENT "用户电话",
    `address` VARCHAR(500) NOT NULL COMMENT "用户地址",
    `register_time` DATETIME NOxT NULL COMMENT "用户注册时间" )
UNIQUE KEY(`user_id`, `username`)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 10
PROPERTIES("replication_num" = "1");

insert into test_db.user values\
(10000,'wuyanzu',' 北 京 ',18,0,12345678910,' 北 京 朝 阳 区 ','2017-10-01 
 07:00:00'),\ (20000,'wuyanzu',' 北 京 ',19,0,12345678910,' 北 京 朝 阳 区 ','2017-10-01 
               07:00:00'),\ (30000,'zhangsan','北京',20,0,12345678910,'北京海淀区','2017-11-15 
                             06:10:20');
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

(3)查看效果

explain select name from user where user_id > 20000
  • 1

开启了向量化执行引擎之后,在 SQL 的执行计划之中会在 SQL 算子前添加一个 V 的标识。

(4)注意事项

  • NULL 值

    由于 NULL 值在向量化执行引擎中会导致性能劣化。所以在建表时,将对应的列设置为 NULL 通常会影响向量化执行引擎的性能。这里推荐使用一些特殊的列值表示 NULL 值,并在建表时设置列为 NOT NULL 以充分发挥向量化执行引擎的性能。

  • 与行存执行引擎的部分差异

    在绝大多数场景之中,用户只需要默认打开 session 变量的开关,就可以透明地使用向量化执行引擎,并且使 SQL 执行的性能得到提升。但是,目前的向量化执行引擎在下面一些微小的细节上与原先的行存执行引擎存在不同,需要使用者知晓。这部分区别分为两类。

    • a 类 :行存执行引擎需要被废弃和不推荐使用或依赖的功能

      Float 与 Double 类型计算可能产生精度误差,仅影响小数点后 5 位之后的数字。如果对计算精度有特殊要求,请使用 Decimal 类型。

      DateTime 类型不支持秒级别以下的计算或 format 等各种操作,向量化引擎会直接丢弃秒级别以下毫秒的计算结果。同时也不支持 microseconds_add 等,对毫秒计算的函数。

      有符号类型进行编码时,0 与-0 在 SQL 执行中被认为是相等的。这可能会影响distinct,group by 等计算的结果。

      bitmap/hll 类型在向量化执行引擎中:输入均为 NULL,则输出的结果为 NULL 而不是 0。

    • b 类: 短期没有在向量化执行引擎上得到支持,但后续会得到开发支持的功能

      不支持原有行存执行引擎的 UDF 与 UDAF。

      string/text 类型最大长度支持为 1MB,而不是默认的 2GB。即当开启向量化引擎后,将无法查询或导入大于 1MB 的字符串。但如果关闭向量化引擎,则依然可以正常查询和导入。

      不支持 select … into outfile 的导出方式。

      不支持 external broker 外表。

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号