即席查询平台的设计与实现

1. 即席查询平台简介

  • 即席查询平台是数据平台针对即席查询(Ad-Hoc)场景推出的一个解决方案。

  • 用户侧提供 SQL 的查询、结果数据的生命周期管理。

  • 运维测统一表权限、Hadoop 组账号、Yarn 队列等。

2. ZUE 的定位与同类产品的比较

离线数据开发平台的技术演进:

  • 石器时代:Hive 客户端直连

  • 青铜时代:beeline 客户端 + Hive Server

  • 英雄时代:HUE + Hive Server

  • 军团时代:ZUE + Moses + Hive Server

开发工具几个阶段的比较:

Hive Cli Beeline HUE ZUE
交互方式 终端 终端 WEB WEB
SQL 支持 全类型 全类型 全类型 部分语句
表权限控制 table.in 元数据与权限
组账号管理 支持 支持
异步查询 不支持 不支持 不支持 支持
查询结果缓存 不支持 不支持 弱支持 支持
资源隔离与限制 无限制 无限制 无限制 有限制

2.1 SQL 的支持

  • Hive Cli、Beeline、HUE 是全功能的客户端工具,提供了完整的 Hive Query 语法的支持以及 HDFS 的管理命令

  • ZUE 限制了可以执行的 SQL 子句类型,把 ZUE 定位为 DML 的平台,将 DDL 划分给元数据管理系统负责。

  • ZUE 仅支持 SELECTCREATE TABLE ASEXPLAIN 等少数语句,并且禁止通过 ZUE 向线上生产表写入数据。

  • 元数据针对 DDL 提供了更加丰富的业务规范与控制,如业务划分、层级划分、表权限控制、变更历史、Location 限制等等。

2.2 表权限控制

  • Hive Cli 和 Beeline 都不具备表权限的功能。

  • HUE 本身不带读写权限的控制,table.in 作为一种 Hack 的方案先天不足(黑名单)。

  • ZUE 用词法解析获取读写的表,结合元数据做权限的控制。

  • 元数据提供了完整的权限申请工作流。

2.3 组账号管理

  • Hive Cli 和 Beeline 没有统一的组账号管理方式,默认以个人账号提交。

  • HUE 的组账号与个人账号等价,无法灵活的切换组账号。

  • Hive Cli、Beeline 和 HUE 大量个人账号的存在导致许多数据出现 owner 的权限问题。

  • ZUE 收敛组账号,从属于业务的数据由统一的组账号进行读写,避免权限问题的发生。

2.4 异步查询

  • Hive Cli、Beeline 和 HUE 的查询操作都是同步,一个客户端会话同一时间只能进行一个查询。关闭客户端或者会话都会导致查询被取消。

  • ZUE 采取异步查询的方式,在同一个窗口可以提交多个查询,多个查询可以并发运行。

2.5 查询结果缓存

  • Hive Cli 和 Beeline 只能手动将查询结果导出保存,否则同一条 SQL 必须重跑才能看到结果。

  • HUE 的查询结果只能在会话过程中查看,一旦页面关闭结果也就丢失了。

  • ZUE 的查询结果缓存 24 小时,重复查看和下载不需要耗费新的计算资源。

2.6 资源隔离与限制

  • Hive Cli、Beeline 和 HUE 无法对用户的查询进行队列的限制,默认提交至 default 队列。

  • Hive Cli、Beeline 和 HUE 运行时可以任意指定队列,无法针对业务线进行资源隔离和计费。

  • ZUE 针对业务线进行队列的限定,保证业务线之间资源的隔离与安全。

3. 功能与架构设计

3.1 HUE 的功能架构缺陷

  • HUE 虽然支持了很多功能,但是本质和 Beeline 并无不同,都是 Thrfit 接口中会话的一种可视化呈现。

  • Thrfit 的会话存在于 HiveServer 中,因此用户的请求必须通过一致性哈希路由到同一台服务器。缺陷在于:1)如果前端请求的路由策略不正确,请求到了其他服务器则该会话的上下文丢失(SET xxx)并且该会话下进行中的查询终止;2)如果某台服务器上 HUE 重启,意味着该服务器上的 Thrfit 会话终结,该服务器上的查询全部自动终止。

  • HUE 的水平扩展实质只能是多个单机节点对总体查询和负载进行分片,单机长时间保持状态。

  • HUE 查询请求量提升时更容易出现线程问题,导致整体不可使用。

  • HUE 虽然界面干净、交互友好,但是本质是适合小团队使用的单机系统。

3.2 ZUE 的整体设计架构

  • 将有状态与无状态的部分分离。无状态接口以容器方式部署,可伸缩性好,迭代升级易维护。长时间保持状态与 HiveServer 保持连接的部分抽象出查询中心模块,部署在物理机上。

  • Moses 查询中心通过暴露 RESTFul 的接口提供异步查询的能力,查询完成之后回调请求方的接口。

  • Moses 查询中心每个结点对等,通过持久化与 HiveServer 的会话信息实现服务重启过程中会话不丢失,不影响用户查询。

  • 使用 HDFS 和 Redis 来缓存查询的结果,缓存生命周期结束后自动回收存储空间。

4. 实现方案一些细节

4.1 语法限制与表权限

  • 对 SQL 进行词法解析,得到抽象语法树。

  • 对抽象语法树进行后续遍历获得语句的类型以及读写的表。

4.1.1 词法分析实例

举例:

 insert overwrite table tb.tb_2 select * from tb.tb_1;

解析成抽象语法树,采用后续遍历进行打印可以得到结果:

 nil
    TOK_QUERY
       TOK_FROM
          TOK_TABREF
             TOK_TABNAME
                tb
                tb_1
       TOK_INSERT
          TOK_DESTINATION
             TOK_TAB
                TOK_TABNAME
                   tb
                   tb_2
          TOK_SELECT
             TOK_SELEXPR
                TOK_ALLCOLREF
    <EOF>

几种 token 代表的含义:

  • TOK_TAB:写入目标表 tb.tb_2

  • TOK_TABREF:查询数据来源表 tb.tb_1

  • TOK_INSERT:语句的类型。

4.1.2 其他工具与缺陷

Hive 自己提供了一个血缘解析的工具:org.apache.hadoop.hive.ql.tools.LineageInfo

缺陷:

  1. 缺少上下文的支持,比如上文使用 USE <db> 语句,那么解析出来的输入表只包含表名,缺少库名。

  2. 反引号转义符需要另行清理。

  3. 对于 CTE 语句中的别名无法处理。

4.2 异步查询

  • 与 HUE 的区别:将用户会话与 Thrift 会话解耦,WEB 交互无状态,封装 Thrfit 会话。

  • 从 JDBC 获得的启示:底层原理同 Beeline 一致,对 Thrift 进行封装,通过覆盖特定的会话配置支持会话恢复重连,查询中心作为一个查询中间件可重启升级不影响用户查询。

     config.put("hive.server2.session.check.interval", "1h");
     config.put("hive.server2.close.session.on.disconnect", "false");
     config.put("hive.server2.idle.session.timeout", "24h");
     config.put("hive.server2.idle.operation.timeout", "24h");
     private synchronized TOperationHandle submitQuery(String sql) throws TException {
         log.info("Start to submit sql of task {} with content:\n{}", this.taskMeta.getTaskId(), sql);
         TExecuteStatementReq execReq = new TExecuteStatementReq(this.sessionHandle, sql);
         execReq.setRunAsync(true);
 
         TExecuteStatementResp execResp = this.client.ExecuteStatement(execReq);
         log.info("execResp = " + execResp);
         this.checkStatus(execResp.getStatus());
 
         return execResp.getOperationHandle();
     }

4.3 结果缓存

结果缓存分为三种:

  1. 较复杂 SQL 或者大数据量的查询结果通过改写 SQL 将结果以 Avro 的形式存储在 HDFS 上,定时回收存储空间。

  2. 简单 SQL 如单表查询直接将结果缓存于 Redis 中,设置缓存的过期时间。

  3. 使用 CTAS 创建的临时表,定时从临时库中清除。

5. 本季度的迭代方向

  • 更友好的故障排查(Yarn Application 日志)与异常诊断。

  • 更多元化的大数据计算引擎的集成(如 Presto)。

6. FAQ

  1. code 2 如何排查

  2. Method Not Found

  3. job counter 不准

  4. read timeout