Apache Lens 介绍与核心概念
介绍 Apache Lens 2.2.0-beta 的统一分析平台定位、Cube 语义层架构、双驱动执行模型与租户会话体系,并结合 Ubuntu 22.04 实测运行日志说明各组件的真实行为。
Apache Lens 介绍与核心概念
一、项目概览与定位
Apache Lens 的自我定位是"统一分析平台"(Unified Analytics Platform):在数据仓库之上提供统一的查询入口与语义建模,把多维分析(OLAP)、即席查询(Ad-hoc)等分析形态收纳到同一套服务与 API 之下,让上层应用不必关心底层引擎是 Hive 还是别的什么。它由印度移动广告公司 InMobi 的数据团队孵化——彼时该公司正面对"报表门户数量爆炸、口径各自为政"的典型大数据中台问题,Lens 的立方体建模正是把"指标口径收敛到建模层"这一诉求的工程化产物。理解这层出身,才能理解它为什么把会话、配额、调度这些"平台化"能力与查询执行并列为核心组件:它的目标从来不是一个更快的引擎,而是一层面向多团队的服务化壳。
从能力构成看,官方对它的完整口径还包括三层含义:把"撰写查询、理解数据、调度作业"的日常分析工作统一到一个服务框架里。它解决的核心问题是:业务分析师面对的是 Hive、关系库等异构后端,同类分析要在不同引擎间反复改写 SQL、手工管理会话与排队,Lens 则在其上提供一层统一的语义模型与执行编排——分析师面向"立方体(Cube)"写查询,Lens 负责把逻辑查询翻译成具体引擎可执行的形式,选择合适的驱动执行,并回传统一格式的结果。在 Ubuntu 22.04 实测环境中,Lens 2.2.0-beta 服务启动后管理端点返回 STARTED,查询提交、改写、驱动路由、结果落盘的全链路都能在日志中逐环节观察到,这正是该平台架构的直接体现。
需要先明确项目的现状:Lens 于 2015 年 8 月成为 Apache 顶级项目,2020 年 8 月宣布退役,2021 年 4 月迁移至 Apache Attic 完成,最后的发行版本是 2015 年 7 月的 2.2.0-beta-incubating(停留在孵化期版本号与命名规范上,这也侧面说明项目成熟度尚未到达正式版)。Attic 官方页面将其概括为"Lens was Unified analytics platform"。因此本文所有内容均应放在"适合学习与遗产系统维护,不建议新项目采用"的前提下阅读:它是理解语义层、多维建模与查询编排思想的优质样本,但不再有任何官方维护与安全更新。
二、发展历程与退役现状
回看节奏曲线:2014 年 5 月进入孵化器时,Hive 生态正处于"SQL on Hadoop"需求井喷期,Lens 踩中了语义层空缺的时间窗;2015 年毕业同年即发布 2.2.0-beta,起点不可谓不快。但此后社区代码贡献高度依赖发起公司,外部共建未能形成,叠加 2015 年后 MPP 引擎与云数仓快速分流注意力,项目在顶级项目状态上实质沉寂了五年,直至 2020 年按 Attic 流程退役。它也是"单发起方孵化项目难以长青"这一 Apache 治理命题的常见案例,与本文配套的部署与实战两篇,记录了在这条时间线的终点上把它完整跑起来的全部实操细节。
Lens 由 InMobi 发起并捐献给 Apache 软件基金会,2014 年 5 月进入孵化器,2015 年 8 月毕业成为顶级项目。从版本轨迹看,2.0-beta 与 2.1-beta 两个孵化版本相继发布后,2.2.0-beta 于 2015 年 7 月发布,此后社区开发节奏明显放缓,最终于 2020 年 8 月经董事会决议退役、2021 年 4 月完成 Attic 迁移。项目源码仓库、官网与发行包至今仍可访问:archive.apache.org 的 incubator/lens 目录下保留着 2.0、2.1、2.2 三个版本的完整发行档,实测下载的 apache-lens-2.2.0-beta-incubating-bin.tar.gz 为 43,129,316 字节,其官方 SHA512 校验值(大写 BSD 风格书写)与本地 sha512sum 计算结果逐位一致,说明发行完整性未受退役影响。
退役对使用者有三个实际影响。第一,发行目录已从 Apache 主分发站摘除,下载需走 archive.apache.org 的归档路径,且 2.2.0-beta 的目录名是 2.2-beta-incubating 而文件名带 2.2.0,手工拼接链接时容易写错。第二,Lens 的运行时依赖链锁定在 2015 年前后的生态:源码 POM 中声明 Hive 版本为 0.13.3-inm(InMobi 内部 fork,托管于 GitHub 上的第三方仓库)、Hadoop 版本 2.4.0,官方二进制包内的服务端 war 不捆绑 Hive/Hadoop jar,必须由部署环境提供——这在当年是常见做法,在今天则需要额外构造依赖目录。第三,安全层面不会再有任何补丁,与它绑定的 Hadoop 2.x、Hive 0.13 均已停止维护,遗产系统应置于内网受控环境运行。本文实测过程中搭建的可运行环境与全部排障记录,可以为需要在存量环境延续 Lens 服务的团队提供直接参考。
三、总体架构:从语义层到驱动层
Lens 的架构自上而下分四层。最顶层是客户端与接入层,官方提供两种形态:交互式命令行客户端(lens-cli,一个基于 Spring Shell 的会话式终端)与 REST API(服务默认监听端口并提供 /lensapi 前缀的接口族),二者面向同一套服务。第二层是服务编排层,由 LensServer 统筹一组命名服务:会话服务(session)、查询执行服务(query)、元数据服务(metastore)、调度服务(scheduler)、配额服务(quota)、统计服务(stats)与事件、指标等基础服务,实测启动日志按 Initialized services: [CLIService, event, metrics, stats, session, query, metastore, scheduler, quota...] 的顺序逐一初始化。第三层是语义层,即 lens-cube 模块实现的立方体建模与查询改写引擎,是 Lens 最具特色的部分。最底层是驱动层,官方内置 Hive 驱动与 JDBC 驱动两个实现,由 lens.server.drivers 配置决定加载哪些。
请求的完整生命周期如下:客户端提交一条查询后,查询执行服务先用 Hive 语法解析器验证并解析(实测一条不合 Hive 语法的 SQL 会在这一步被拒绝并返回错误码 3001);随后语义层判断该查询能否映射到已建模的立方体,能映射的走立方体改写,纯 SQL 查询则原样下传;接着服务向每个候选驱动请求执行代价估计(estimate),按代价与优先级选出执行驱动——实测一条简单查询被路由到 Hive 驱动时,日志会记录 submitted to org.apache.lens.driver.hive.HiveDriver 并给出驱动侧改写后的语句;最后驱动执行并把结果写入统一的结果目录,客户端凭查询句柄(query handle)取回结果或状态。这条"解析—改写—估计—路由—执行—取数"的流水线是 Lens 区别于裸 Hive 客户端的本质所在。
这条流水线的每一环都有对应的落盘证据,可观测性在同类项目中做得相当细。服务端日志集中在 server/logs/ 目录:lensserver.log 记录服务编排层与驱动层的完整事件流(从驱动初始化到查询路由再到结果提交);lensserver-requests.log 以接近网络抓包的粒度转储每个 HTTP 请求与响应(含请求头、路径、状态行),前端联调时可以像看抓包一样核对客户端到底发了什么;query-stats.log 按小时轮转(实测文件名为 query-stats.log.2026-10-14-20 这类带时间后缀的形态),逐查询记录生命周期统计;另有按启动批次滚转的 lensserver.out.<时间戳> 文件承载标准输出,目录中的 server.pid 记录当前进程号。这套日志矩阵加上管理端点,使得对服务状态与单条查询的审计都可以完全离线完成,对遗产系统排障是重要利好。
四、核心概念:立方体、维度与事实
Lens 语义层的核心建模单元是立方体。一个立方体声明了一组可查询的度量(measure)与维度(dimension attribute),底层由事实表(fact)提供度量数据、由维度表(dimtable)提供维度明细,事实与维度通过连接链(join chain)声明关联路径,最终落到某个存储(storage)上的物化表。分析师只需 describe cube 查看可用字段,用类 SQL 语句按立方体字段撰写查询,改写引擎会根据查询的时间范围、分组字段与连接需求,自动选择合适的事实表与维度表并生成物理 SQL。这套模型正是后来各类语义层、Headless BI 产品的思想先声。
与立方体并行,Lens 也保留了原生表的透传能力:show nativetables 列出后端库中的原生表,describe nativetable 查看其结构,纯原生 SQL 查询不经过立方体改写直接下传驱动执行。实测中一条 select 1+1 这样的简单查询未触及任何立方体建模,被直接提交给 Hive 驱动执行并成功返回,说明语义层是可选增强而非强制关卡——这让 Lens 既能服务多维分析场景,也能承担普通的 SQL 网关角色。元数据服务把立方体、事实、维度表、存储的管理暴露为 REST 资源,实测不带会话参数访问 /lensapi/metastore/cubes 会返回 400,体现了按会话租户隔离元数据的访问控制设计。
值得追问的是语义层带来的价值与代价各是什么。价值端,立方体把"指标口径、连接路径、可用粒度"从分析师手里收回到建模层,同一口径跨报表复用、字段级血缘与改写透明性都建立在模型声明之上,这是它领先于同期裸 SQL 工作流的思想资产。代价端,改写引擎的覆盖面受限于建模声明的完整性:事实表与维度表的版本切换、存储裁剪(partition-wise 摘除)等能力在 2.2.0-beta 尚属演示级,复杂查询一旦超出声明范围就得退回原生 SQL,两套心智并存提高了使用门槛。今天流行的语义层产品以更完备的实现承接了同一思想,但 Lens 在 2015 年就把这套闭环跑通,其设计文档与源码仍然是理解语义层"应包含哪些部件"的高质量参考——内置元数据库(实测为 HSQLDB)只承担建模对象与查询台账的存储,真正的大数据量始终留在后端引擎,这种轻量元数据层的取舍同样值得注意。
五、核心概念:双驱动与查询编排
驱动(driver)是 Lens 的执行后端抽象。官方实现有两个:HiveDriver 把查询提交给 Hive(支持连接远端 HiveServer2,也可内嵌运行),JDBCDriver 把查询透传给任意 JDBC 关系库(默认配置面向 MySQL,可改指 HSQLDB 等)。二者由 lens.server.drivers 配置项以逗号分隔同时加载,实测服务启动时依次打印 Hive driver inited 与 JDBC Driver configured,并由查询执行服务确认 Driver for org.apache.lens.driver.hive.HiveDriver is loaded。多驱动并存时,服务对每条查询并行发起估计,按代价决策路由;实测过程中 JDBC 驱动在估计阶段抛出空指针导致其退出候选,查询被路由到 Hive 驱动,这一行为也被完整记录在服务日志中,直观展示了"估计失败即淘汰"的编排规则。
查询编排的另一要点是结果路径改写。Lens 会把驱动要执行的语句统一改写为 INSERT OVERWRITE DIRECTORY "file:/tmp/lensreports/hdfsout/<查询句柄>" <原查询> 的形式,让结果按句柄落盘再由服务层取回,实测一条简单查询的日志完整呈现了这一改写。查询句柄是贯穿始终的键:提交时返回句柄,状态查询、结果获取、终止操作都以句柄为参数,异步执行模式下客户端可以先拿到句柄稍后轮询。对运维者而言,服务端的查询级日志按会话与操作句柄组织在 query_logs 目录下,逐操作生成 .err/.out 文件,排障时可以直接定位到单条查询的驱动侧输出。
查询的详细档案(query details)把生命周期计时拆到了字段级,实测一份 finished 查询的详情中包含:submittedUser(anonymous)、priority(NORMAL)、selectedDriverClassName(最终执行的驱动全类名)、driverQuery(改写后带 INSERT OVERWRITE DIRECTORY 的完整物理语句)、driverOpHandle(驱动侧操作句柄),以及 submissionTime、launchTime、driverStartTime、driverFinishTime、finishTime 五个毫秒级时间戳。五段时间戳正好把"提交→启动→驱动开始→驱动结束→总结束"的间隔切开,做性能归因时可以直接读出服务端编排开销与驱动执行开销各占多少,这是比一般 SQL 网关更细的观测维度。
六、核心概念:会话与多租户
Lens 的会话模型把"连接后端、持有资源、限制配置"统一管理。客户端首次操作时自动建立服务端会话,实测 CLI 首条命令执行后服务日志即出现会话标识,show params 可以拉取当前会话的全部参数,其中包括 lens.session.cluster.user、lens.session.loggedin.user 等身份信息与会话级配置覆盖。会话支持添加 jar 与 file 资源(add jar、add file 命令),供后续查询在驱动端加载,这是面向 UDF 等扩展场景的设计。会话关闭(CLI 的 close/bye)会释放服务端资源,实测 lens-ctl stop 停服时也能看到会话目录被标记关闭。
多租户体现在两个层面。其一是数据库命名空间:Lens 在元数据层维护自己的 database 列表(实测 show databases 返回 default),立方体等语义对象按 Lens 数据库隔离,与后端 Hive 的库表是两层命名体系。其二是配置覆盖:会话级、查询级参数可以覆盖服务端默认值,配额服务再按用户施加查询并发与资源上限。这套设计让 Lens 可以作为多团队共享的分析网关运行,而不是单用户工具。当然,由于项目已退役,鉴权体系停留在 2015 年的简单实现(实测匿名会话即可操作),若用于遗产维护应置于网络隔离环境并自行补齐访问控制。
七、发行组成与部署形态
官方二进制发行包解压后分为 client 与 server 两棵目录树。server 侧包含启动脚本 bin/lens-ctl(支持 start/stop,内部经 lens-config.sh 汇总环境变量拼装 Java 命令)、配置目录 conf(核心文件 lens-site.xml 与 lens-env.sh)与 webapp 目录下的 lens-server.war——该 war 内含 80 个 jar(Jersey 2.3.1、Dropwizard 风格指标组件、HSQLDB 2.2.9 及 lens-api/cube/driver 等自有模块),但刻意不包含 Hive 与 Hadoop jar,运行时从 HADOOP_HOME 与 HIVE_HOME 两个环境变量指定的目录加载,这是 Lens 部署区别于普通 war 应用的最大特殊点,实测缺任一变量启动即被脚本拒绝并给出明确报错。client 侧提供 lens-cli.sh 交互客户端与示例脚本。发行包还附 sha512 校验文件与 source-release 源码包,后者可用 Maven 在 JDK 8 下完成源码构建,实测构建全程约 12 分钟产出与官方结构一致的发行树。
从部署形态看,LensServer 内嵌 Grizzly HTTP 服务直接监听端口对外提供 REST API,无需外部 Web 运行环境;管理端口提供 ping、metrics、healthcheck 等运维端点。JVM 参数通过 lens-env.sh 的 LENS_SERVER_HEAP 等变量注入,实测设置 -Xms256m -Xmx512m 后进程命令行逐字生效。这种"单进程、双外挂依赖目录"的形态对遗产系统维护者是好事:升级或回滚只需替换发行树与依赖目录,不涉及系统服务改造。进程管理细节上,lens-ctl start 把进程号写入 logs/server.pid,stop 动作读取该文件定点停止,实测每次启动都会新开一组 lensserver.out.<时间戳> 文件,多次启动的输出互不覆盖,这给"对比两次启动日志"这类排障动作留了完整证据;停止后 logs 目录中的 server.pid 不删除,巡检时应以进程号是否存活为准而非仅看文件存在。
八、适用场景与同类对比
基于实测,Lens 适合的场景有三类。第一是学习研究:它把语义层、多驱动编排、会话租户、查询生命周期管理等完整实现了一遍,代码与配置都是可运行的教材,今天流行的语义层与查询网关产品可以在这里找到早期范本。第二是存量系统维护:若某机构的分析门户建立在 Lens 之上,按本文的依赖构造方法可以继续在受控环境运行它。第三是作为历史案例评估技术选型:Lens 停滞的原因(社区动力、生态绑定、运维复杂度)对新项目选型有直接借鉴意义。
与同类组件对比:直接使用 Hive/HiveServer2 得到执行能力但没有语义层与多后端路由;Apache Kylin 提供预计算 OLAP 但模型与执行模型不同;更晚出现的语义层项目在工程成熟度上已远超 Lens。若把时间轴拉长看,2015 年前后正是交互式 SQL 引擎爆发的分水岭:Presto、Apache Drill、Apache Impala 这类 MPP 引擎以更快的执行层切入即席分析场景,而 Lens 选择在语义层与编排层做深度,执行层仍然借用 Hive——路线分岔的结果是,前者的执行性能成为硬通货,后者的建模能力虽然前瞻却缺少执行侧的持续投入,这也是 Lens 社区动力衰减的一个技术注脚。同样地,调度服务(scheduler)以虚拟 DB 上的定时任务预生成查询实例、配额服务(quota)按用户封顶并发的思路,在今天各类查询网关中都有更成熟的对应物,Lens 的实现版本停留在演示级。Lens 的独特价值在于"语义建模 + 执行编排"的完整闭环实现,而不在于性能或生态。对今天的技术决策者,结论应当明确:学习与研究价值高,生产采用价值低。
九、常见问题(FAQ)
1. Lens 现在的官方状态是什么? 已于 2020 年 8 月退役、2021 年 4 月完成 Attic 迁移,终版 2.2.0-beta-incubating(2015 年 7 月)。无任何后续维护与安全补丁,适合学习与遗产系统维护,不建议新项目采用。
2. 它和直接用 Hive 有什么区别? Lens 在 Hive 之上叠加了立方体语义层(自动改写)、多驱动路由(Hive/JDBC 按代价选择)、会话与配额管理、统一查询生命周期(句柄/状态/结果落盘)。实测一条简单查询从提交到路由到 Hive 驱动的每一环都在日志中可见。
3. 为什么要叫立方体?和 OLAP 立方体一样吗? 思想同源:把度量与维度建模成多维结构。区别在于 Lens 的立方体是逻辑声明加查询期改写,不强制预计算,由改写引擎在执行时选择事实表与连接路径。
4. 服务启动为什么强制要求 HADOOP_HOME 和 HIVE_HOME? 服务端 war 刻意不捆绑 Hive/Hadoop jar,启动脚本从这两个目录追加类路径。实测缺 HADOOP_HOME 报 "Cannot find hadoop installation",缺 HIVE_HOME 报 "HIVE_HOME is not set. Set HIVE_HOME and try again"。
5. 任意 SQL 都能执行吗? 不行。所有查询先经 Hive 语法解析,实测非 Hive 语法的 SQL 返回错误码 3001 及解析器定位信息;通过解析的查询才能进入改写与路由。
6. 查询结果在哪里?
统一改写为 INSERT OVERWRITE DIRECTORY 落盘到 /tmp/lensreports/hdfsout/<查询句柄>,CLI 会打印结果文件路径与耗时,也可在会话内直接回显结果集。
7. 支持哪些客户端? 官方交互式 CLI(lens-cli.sh)与 REST API 两种。CLI 基于 Spring Shell,支持 help 查看全部命令,首条命令即自动建立服务端会话。
8. 服务端口是多少?如何确认服务健康? 默认 base url 为 http://0.0.0.0:9999/lensapi/,管理接口 GET /lensapi/admin/status 返回 STARTED 即健康;另有独立的运维端点提供 ping、metrics、healthcheck。
9. 堆内存怎么设? 在 server/conf/lens-env.sh 设置 LENS_SERVER_HEAP(如 -Xms256m -Xmx512m),启动脚本将其并入 LENS_OPTS 传给 JVM,用 ps 查看进程命令行即可确认生效。
10. 源码还能构建吗? 能。JDK 8 与 Maven 环境下执行 mvn clean package -DskipTests 可构建成功,但需提前处理 POM 中锁定的 InMobi fork 依赖(hive 0.13.3-inm),实测需将该 fork 的 jar 与父 POM 预置进本地仓库。
11. 查询的生命周期能否精确计时?
能。query details 返回五段毫秒级时间戳(submissionTime、launchTime、driverStartTime、driverFinishTime、finishTime),分别对应提交、启动、驱动开始执行、驱动执行完成、整体完成五个节点,相邻差值即各阶段耗时,可直接用于性能归因。
12. 排查客户端与服务端交互问题时看哪个文件? server/logs/lensserver-requests.log。它以逐请求粒度转储 HTTP 请求与响应(含请求头、路径、状态行),可以核对 CLI 实际发出的每一次调用;应用层事件再配合 lensserver.log,query-stats.log 则提供按小时轮转的查询统计。
参考链接
- Apache Attic 项目页:https://attic.apache.org/projects/lens.html
- Lens 发行归档:https://archive.apache.org/dist/incubator/lens/2.2-beta-incubating/
- 源码仓库:https://gitbox.apache.org/repos/asf#lens
- InMobi fork 依赖仓库:https://github.com/InMobi/mvn-repo
- Apache Hive 文档:https://hive.apache.org
- Lens 官网(退役快照):https://lens.apache.org