Files
doris/docs/design/show_load_warnings.md
2017-08-11 17:51:21 +08:00

7.7 KiB

[TOC]

Show Load Warnings

背景

在palo3.1以后,Palo存在三种数据导入方式:

  • 通过hadoop load批量导入;
  • 通过http进行小批量导入;
  • 使用broker进行数据导入;

对于用户来说,查看导入时的错误信息非常必要。根据这些错误信息,用户进行数据的整理和修改。

目前Palo支持的导入方式和对应的错误详情查看方式如下表:

导入方式 内网 公有云
Hadoop HTTP,访问be
小批量导入 HTTP,访问map-reduce作业的error_log
Pull-Load ---------- ------

这里有两个问题,一个是没有提供统一的导入错误查看接口。另一个问题,也是最严重的,对于公有云用户,目前无法自行查看导入的错误详细信息。

需求

无论是通过何种方式进行导入的数据,用户有统一的接口进行查看错误信息,并且在内网和外网都可以进行查看。

用户查看错误信息都是以“数据行”为单位,所以信息展示时也以“数据行”为单位。

但是对于一个导入任务,不需要展示其所有的数据数据行,只需要展示部分即可。总的正确行数和错误行数仍然在SHOW LOAD的EtlInfo中查看。

方案概述

1. 对外复用现有的mysql协议。

因为我们对用户提供的是mysql协议接口,所以为了保证内网和外网用户都能访问,使用mysql协议输出即可。这既复用了现有的协议,又不需要额外部署服务。

2. 内部将这些错误信息输出到统一的“介质”。

在一个导入任务中,可能会有多台机器进行ETL。这些机器可能是BE(小批量导入时),也可能是hadoop的机器(map-reduce任务的mapper),每台机器保存自己处理数据时遇到的错误,即这些错误信息分布在多台机器上。

如果在用户请求时再进行收集和展示,需要向多台机器发送请求,效率不高。

另外,M-R作业的error-log,还需要解析日志文件,然后处理后才能用于展示,比较繁琐。

为了解决这个问题,我们在错误信息生成时,就进行统一收集,而不再保存在本地。
即当每台机器在执行ETL时发现原始数据有误时,都将相应的错误信息数据到相同的地方。所以这个“统一的介质”是一个服务,收集其他机器推送的数据。

另一方面,这个“统一的介质”要能够进行高效的查询,从而当用户查询时能够快速的响应。

详细设计

1. mysql接口设计

SHOW LOAD WARNINGS
[FROM DB_NAME]
[
    WHERE
    [LABEL = "your_label"]
    [LOAD_JOB_ID = your_job_id]
]
[LIMIT limit];

注:这里使用Warning,而没有使用Error。因为当用户允许一定的filter时,即使有错误的数据行,但导入任务并不一定会失败,这符合Warning的语义,而Error的感觉是一旦出现error导入作业一定失败。

说明:

  1. 如果不指定 db_name,使用当前默认db
  2. 如果使用 LABEL = ,则精确匹配指定的 label。当导入失败重试时,同一个label可以对应多个job,默认只显示最新的一个job。
  3. 如果使用 LOAD_JOB_ID = ,则精确匹配指定的 job
  4. 如果指定了 LIMIT,则显示 limit 条匹配记录。否则全部显示。(后面可以考虑,即使不设置limit,也最多展示一定条数,比如100行)

2. 结果显示

JobId Label ErrorMsgDetail
*** **** **********

每行都对应原始数据中的一行。

3. 设置mysql服务地址

格式:

ALTER SYSTEM SET LOAD_ERROR_URL= "mysql://user:password@host:port[/database[/table]]"

如果以后不再使用mysql,只需要将"mysql"修改对应的协议栈即可。

如果不指定database,那么默认使用$cluster_id作为数据库名;
如果不指定table,那么默认使用"load_errors"作为表名。

4. 底层引擎选择

如上所述,“介质”是一个服务,还需要能够高效查询。

为了实现简单,可以Mysql或者ES,我们这次使用Mysql。

如果以后Mysql性能成为瓶颈,可以再考虑ES,或者自己实现一个简单存储服务,比如利用rocksdb,或者自己封装一个HashMap。

部署Mysql时,要保证能够同时被M-R的mapper和FE&BE都可以访问。公司内网的palo集群,就在内网部署mysql; 公有云的palo集群,就在公有云可访问的网段部署;

5. 表设计

create table load_errors (
    job_id BIGINT NOT NULL,
    error_msg VARCHAR(100) NOT NULL,
    INDEX(job_id)
)

为了便于快速查询,在job_id列上建立了index。

将来如果需要,可以再增加一个type列,表示错误类型。(目前小批量导入,没有严格的统计区分错误类型。)

6. 数据量预估

目前线上nmg集群导入量最大,7天的导入任务数量为13.5万,平均每天不到2w。

假设每个任务平均最大输出为100条,那么每天错误信息的条数约为200W,Mysql应该不会成为瓶颈。

7. 插入方式

INSERT INTO load_errors (job_id, error_msg)
VALUES( **, "" );

8. 查询方式

SELECT job_id, error_msg 
FROM load_errors 
WHERE job_id in (xx, xx);

9. TTL(历史数据清理)

为了简单起见,我们按时清理历史数据。执行和删除label相同的逻辑即可,保留7天。

这里并不需要和删除label耦合,即如果错误信息的条数增长很快,那么可以将保留时间降低,从而提高效率。

注意一点:删除时考虑到Mysql的性能,不进行批量删除,而是利用jobid的递增特性,每次删除100条,发多次。

10. 可靠性保证

这里保留的错误信息只需要提供尽力而为的原则。即便是数据丢失,是不会对系统运行造成不良影响的。所以我们暂时不需要部署Mysql Slave。

如果Mysql服务宕机,我们可以很快的搭建一个新的Mysql继续提供服务。

为了能够保证Mysql切换时不需要重新修改线上配置,Mysql的地址使用BGW。这样切换机器时,只需要修改域名映射即可。

另外,在向Mysql写入时,为了不阻塞导入,也是采用尽力而为的写法,如果写入mysql失败,那么就放弃写入,导入作业可以继续进行。

11. 多集群共用

目前在公有云,每个用户都会创建自己的Palo集群,不可能为每个集群单独部署mysql服务。另外,公司内网也有多个palo集群,可以共用一个Mysql。

我们部署的mysql是公用的,这需要一定的隔离机制。

在Palo中,公司内网用户通过DB进行隔离;公有云的用户通过集群进行隔离。

为了统一,在导入错误信息的Mysql服务中,可以通过DB来区分不同集群,即在Mysql中每个集群对应一个DB。

DB的命名为:{$cluster_id}

说明:因为cluster_id是一个随机的64位整数,理论上不同的集群上是可能有相同的,但是概率极低。另外,即便是重复了,结果就是这两个集群会共用一个集群。只有在jobid也重复的情况下,才会出现显示内容混乱,这个概率就更低了。

这样,在DPP,FE,BE中,都只需要感知 cluster_id 就可以定位到对应的DB。

实现

主要分以下几个方面:

  1. FE:Java, 实现解析用户"show load error"命令,拼装并查询Mysql,然后组装成最后结果返回用户。load_error_url信息要持久化。
  2. BE:C++, 封装写入接口,共小批量导入和Pull_Load调用。
  3. DPP:Python, 封装写入接口,mapper在输出错误时调用。