1. 为什么需要 Hbase 连接器
1.1 场景痛点:多源数据查询的麻烦
很多团队的数据架构走到中后期,都会面对一个尴尬局面:数据“散”在各种各样的系统里。业务库放在关系型数据库里,日志数据进了消息队列,而一些需要低延迟读写、又不想搞太复杂建模的数据,比如用户行为轨迹、设备状态上报、订单实时流转,往往会落到 Hbase 里。Hbase 的吞吐能力确实强,但查询方式非常“原始”,要么靠 rowkey 精确 get,要么写 scan 加过滤器,要么通过 Phoenix 或者 Hive On Hbase 绕一圈。绕来绕去,SQL 语义被削弱,跨数据源的关联查询更是难上加难。
这时候 openLooKeng 的价值就体现出来了。它是一个分布式 SQL 查询引擎,原生支持多种数据源的连接器。你在 openLooKeng 里建一张表,它并不真正存储数据,而是把你写的 SQL 翻译成对各个数据源的访问,比如查 Hbase、查 MySQL、查文件系统,再在引擎层做 join、聚合、过滤。也就是说,你可以在一条 SQL 里同时关联 MySQL 里的用户维度和 Hbase 里的行为轨迹,而不需要先离线把两份数据搬到同一个地方。
1.2 方案选型:openLooKeng 的优势
有人可能会问,技术栈里有 Presto、Trino 这些同类引擎,为什么选 openLooKeng?这里我纯粹从实践角度说几条实际感受。第一,openLooKeng 对跨域跨源的支持做得更开放,尤其是对国产化组件和多种 NoSQL 的适配,更新节奏也比较贴近国内团队的运维习惯。第二,它提供了索引加速能力,可以在数据源之上建索引文件,对 Hbase 这种本来就靠索引查数的场景,配合起来能明显减少 scan 的压力。第三,部署方式足够简洁,一个 coordinator、多个 worker,配好 catalog 就能跑起来,没有强制依赖一堆周边组件。
当然,也不是说 openLooKeng 就完美无缺。它对 Hbase 的连接器实现走的是 Hbase 原生客户端 API,所以版本兼容性需要特别留意,很多配置问题说到底都是版本对不上。这也是我下面要先讲版本匹配的原因。如果你只是临时用用,随便装一个版本可能也能跑,但一旦进入生产,你会发现版本错位会带来一堆莫名其妙的异常,最典型的就是 RPC 协议握手失败。
2. 环境准备与版本匹配
2.1 版本兼容性盘点
配置 Hbase 连接器的第一个门槛不是写配置文件,而是确认版本。openLooKeng 的 Hbase 连接器内部依赖 Hbase client 的 RPC 协议,而 Hbase 服务端的协议和客户端的协议必须严格匹配。如果 openLooKeng 自带的 Hbase client 版本与集群版本不一致,最常见的报错是Master rejected connection或者org.apache.hadoop.hbase.DoNotRetryIOException。
我的建议是,先用下面这张表做个对照检查。这张表是基于我在部署环境中验证过的情况总结出来的,不同厂商的发行版可能略有差异,但大方向是对的:
| openLooKeng 版本 | 推荐 Hbase 版本 | 备注 |
|---|---|---|
| 1.x | Hbase 1.4.x | 兼容性最稳,但部分新特性缺失 |
| 2.x | Hbase 2.2.x ~ 2.4.x | 常用组合,生产环境最多 |
| 最新发行版 | Hbase 2.4.x 及以上 | 需要确认 RPC 协议,建议提前做压测 |
注意:如果你的 Hbase 是通过某向量数据库厂商的发行版部署的,最好直接去看厂商文档里推荐的 openLooKeng 连接器版本,而不是自己乱配。我在一次环境里就因为用的是厂商修改过的 Hbase 2.1.0,结果 openLooKeng 自带的 2.2 客户端一直连接超时,后来换回 2.1 对应的 client 才解决。
2.2 基础环境安装步骤
在配置连接器之前,openLooKeng 本身必须是可运行状态。这里我假设你已经部署好了 openLooKeng 集群,如果没有,可以先用单机模式快速跑通。单机模式就是在 openLooKeng 的安装目录下,把 coordinator 和 worker 配置成同一个节点,适合验证配置和做小规模测试。
环境准备阶段主要做三件事。第一,确保所有节点的时间是同步的,Hbase 依赖 zookeeper,zookeeper 会话对时间漂移非常敏感,时间差超过一定范围就会导致 session expired。第二,确认网络连通性,openLooKeng 引擎所在节点需要能访问 Hbase 的 zookeeper quorum 端口(通常是 2181)以及 Hbase master 的 RPC 端口(默认 16000)。第三,准备 hbase-site.xml,因为 openLooKeng 连接 Hbase 时,需要从这个配置里读取 zookeeper 地址、znode parent 等关键信息。
这三件事看起来简单,但我在实际环境里遇到最多的就是网络问题。有些团队的防火墙策略做得特别严格,只开放了应用层端口,结果 zookeeper 的 2181 端口被拦截,openLooKeng 一直报org.apache.zookeeper.KeeperException$ConnectionLossException。排查了半天才发现是防火墙规则漏了端口。所以环境准备阶段,不要急着写 Hbase 连接器配置,先把网络连通性测一遍。用 telnet 或者 nc 从 openLooKeng 节点上测试一下 zookeeper 端口和 hbase master 端口,通了再往下走,否则后面全是无效操作。
3. 核心配置文件详解
3.1 hbase.properties 的构成
openLooKeng 使用连接器的地方叫做 catalog,每个 catalog 对应一个配置文件。Hbase 连接器的配置文件一般放在etc/catalog/hbase.properties。文件名里的 hbase 就是 catalog 的名字,之后你写的 SQL 会用hbase.表名这种三部分命名方式来引用。这个文件名可以自定义,比如叫hbase_prod.properties,那 catalog 名就是hbase_prod,但我建议还是保持简单,就叫 hbase,省得写 SQL 时绕口。
一份最基础的 hbase.properties 大概长这样:
connector.name=hbase hbase.zookeeper.quorum=zk1.example.com,zk2.example.com,zk3.example.com hbase.zookeeper.property.clientPort=2181 hbase.cluster.distributed=true hbase.rootdir=hdfs://hbase-cluster/hbase hbase.zookeeper.znode.parent=/hbase这里面connector.name是必须的,告诉 openLooKeng 这个 catalog 用的是哪个连接器插件。hbase.zookeeper.quorum填 Hbase 集群的 zookeeper 地址列表,多个地址用逗号分隔。hbase.zookeeper.property.clientPort是 zookeeper 的客户端端口,默认 2181,如果你改了端口,这里必须跟着改。hbase.cluster.distributed填 true,表示连接的是分布式 Hbase 集群。hbase.rootdir和hbase.zookeeper.znode.parent是从 hbase-site.xml 里拿过来的两个关键参数,用来让客户端找到 Hbase 在 HDFS 上的根目录以及 zookeeper 上的 znode 路径。
3.2 关键参数与计算逻辑
很多人配完基础参数就结束了,但实际生产环境还需要几个增强配置。第一个是hbase.client.scanner.caching,这个参数控制每次 RPC 请求从 Hbase 服务器端批量取回的行数。默认值可能只有几十行,如果查询的表数据量大,RPC 往返次数会非常高,查询速度自然上不去。我一般会把这个值调到 1000 到 2000,但注意不要无脑调大,因为缓存行的对象会占堆内存,openLooKeng 的内存如果不够,反而会触发 GC 或 OOM。具体调多少,要看表的一行数据大概多大和 worker 节点的堆内存配置,建议先用 500 做个基准测试,再逐步往上加。
第二个是谓词下推的配置。openLooKeng 的 Hbase 连接器支持把 SQL 中的等值条件下推到 Hbase 的 rowkey 查询,从而把全表 scan 变成精确 get。但这个能力需要开启一个参数,我记得有一个开关是hbase.pushdown.enabled,线上环境我通常设置为 true,并且建议在表结构定义的时候,把 rowkey 映射关系和查询字段对应起来。这个参数的具体命名在不同小版本里可能有微调,如果你用的版本里找不到这个参数名,就去找连接器源码里跟 pushdown 相关的配置项,或者直接查看官方文档。
第三个是连接池相关配置。Hbase 客户端本身有自己的连接池,openLooKeng 侧也有线程池参数,比如hbase.client.connection.max.size和hbase.client.connection.min.size。这两个参数决定了每个 worker 节点最多能建立多少个 Hbase 连接。默认值通常偏保守,当并发查询量上来时,连接池打满会导致查询排队甚至超时。我习惯把最小值设成 10,最大值设成 50,同时注意观察 Hbase 集群侧的连接数,不要让 Hbase RegionServer 的连接数爆掉。
提示:配置文件改动之后不需要重新编译或重启整个集群,只要重启 openLooKeng 服务进程即可。但是请记住,任何配置改动前都要先备份原文件,尤其是你在生产环境操作时。我见过不止一次有人因为一个参数写错,把整个 catalog 搞挂,结果所有依赖这个引擎的报表任务全在报错。
4. 实战:从零配置并验证查询
4.1 配置流程全记录
下面我以一套测试环境为例,带你完整走一遍配置流程。假设环境里有三个节点,一个是 openLooKeng 单机,一个是 Hbase 集群,一个是数据生成器。我先把 Hbase 里的一张表准备好,表名叫sensor_data,rowkey 是设备 ID 加时间戳,列族叫info,里面有一个列叫temperature。
这个表的创建语句,你可以用 Hbase shell 来做,也可以直接用 openLooKeng 的 Hbase 连接器建表。用连接器建表的好处是,它会自动在 openLooKeng 侧生成映射关系,省得你手动编写 schema。但有一点要注意,openLooKeng 默认对 Hbase 表的映射规则是你必须指定 rowkey 对应的字段,否则后续查询时它不知道拿哪个字段作为 Hbase 的 rowkey 来构造 get。
我的做法是先手动准备一个表描述文件。在 openLooKeng 中,Hbase 连接器支持用hbase表描述来定义映射。你可以通过建表时指定属性,也可以在连接器的数据目录里放一份 JSON 描述。这里有个关键概念,Hbase 表在 openLooKeng 里被抽象成一张二维表,你需要在描述文件中明确每一列对应 Hbase 的哪个列族、哪个修饰符,以及哪一列是 rowkey。
实际的建表语句看起来像这样:
CREATE TABLE hbase.sensor_data ( device_id VARCHAR, ts TIMESTAMP, temperature DOUBLE ) WITH ( external = false, rowkey = 'device_id', column_mapping = 'device_id:info:device_id,ts:info:ts,temperature:info:temperature' );这段 SQL 里,external = false表示这是一张新表,openLooKeng 会帮你把列族、列都建好。rowkey指定 device_id 这一列作为 Hbase 的 rowkey 来源。column_mapping的格式是列名:列族:修饰符,用逗号分隔多个列。建完表之后,我们可以用 Hbase shell 扫描一下,确认表结构和列族都创建成功了。
4.2 验证查询与常见报错
表建好后,插入几条测试数据,然后去 openLooKeng 里执行查询。最简单的验证是先跑一下SHOW SCHEMAS FROM hbase;,这个命令会列出 Hbase 里的所有命名空间。接着执行SHOW TABLES FROM hbase.default;,看看sensor_data表是否在默认命名空间里。如果这两步都正常,说明 catalog 基本配置已经生效,连接器没有白屏。
接着跑一个针对具体设备的查询:
SELECT * FROM hbase.default.sensor_data WHERE device_id = 'device_001';因为我们在建表时指定了 rowkey 字段,这个查询如果开启了下推,openLooKeng 会把它转换成 Hbase 的get操作,速度非常快。为了验证到底有没有走下推,你可以去 Hbase 的 UI 页面看 RegionServer 的读请求量,如果get请求数在涨,而scan请求数不怎么动,那恭喜你,配置生效了。
但这里有一个常见的坑:如果你在 WHERE 条件里用的是非 rowkey 字段,比如WHERE temperature = 36.5,openLooKeng 只能退化成全表 scan,性能会很差。有些同学一开始不知道,拿一个几亿行的表做测试,发现查询要跑几十秒,还以为连接器配错了。实际上不是错,是没走索引,这是 Hbase 本身的特性决定的。
我在配置过程中还遇到过两个高频报错,第一个是TableNotFoundException,这个大概率是命名空间或者表名列写错了。openLooKeng 的命名空间对应 Hbase 的 namespace,你建表时用的是default命名空间,SQL 里写成hbase.sensor_data,它就会去 default 里找,如果实际表在别的命名空间,就会报这个错。查一下 Hbase 里表的完整命名空间,SQL 里把命名空间写全就好。
第二个是PermissionDeniedException,这说明 openLooKeng 进程的运行用户对 Hbase 里的目标表没有读权限。Hbase 的权限控制走的是 ACL,如果你用的 Kerberos 认证环境,还要确认 openLooKeng 的 principal 和 keytab 配置正确。这块非常容易漏,因为很多团队测试环境根本不启用认证,但生产环境开了 Kerberos 之后,连接器配置就会多出一大坨东西。我在 Kerberos 环境下的经验是,除了在 hbase.properties 里配置 principal 和 keytab 路径,还要确保 openLooKeng 的 JVM 启动参数里设置了java.security.krb5.conf,否则它会找不到 realm 配置。
5. 调优与运维心得
5.1 性能优化三板斧
配置跑通只是第一步,真正让 Hbase 连接器发挥价值,还得做性能调优。这里我总结了三板斧,按优先级从高到低排列。
第一板斧是 Rowkey 设计。Hbase 的访问模式决定了 rowkey 是查询性能的天花板。如果你在 openLooKeng 里经常要按设备 ID 和时间段过滤,那么 rowkey 应该设计成设备ID倒序_时间戳,这样同一设备的数据在 Hbase 里是连续存储的,scan 范围可以限制得很小。反之,如果你把随机字符串放在 rowkey 前面,那每次查询都会变成全表扫描,连接器再优化也没用。
第二板斧是列族设计。一个列族在 Hbase 里对应一个存储文件,如果业务数据只有一个列族,那就尽量只用它,不要建多余的列族。openLooKeng 的映射文件里,如果太多列分散在不同的列族,读数据时可能需要同时打开多个 StoreFile,IO 会变多。我在生产库上见过有人为了分管理想化地建了十来个列族,结果查询 latency 直线上升。建议一个表的列族不超过三个,并且把访问频率高的列放在同一个列族里。
第三板斧是连接器侧的缓存和并发参数。除了前面说到的scanner.caching和连接池参数,还有几个 JVM 层的东西容易被忽略。openLooKeng 的 worker 进程堆内存如果设置得偏小,大量 Hbase RPC 响应会被频繁 GC。建议给 worker 节点留足堆内存,并且在启动脚本里加上-XX:+UseConcMarkSweepGC(如果 JDK 版本支持的话)或者用 G1,具体看你的 JDK 版本。另外,Hbase 客户端的 RPC 重试次数可以适当减少,hbase.client.retries.number默认可能高达几十次,在网络抖动时会导致查询卡顿时间很长。我一般把它调到 5 次左右,让它快速失败,然后让上层查询引擎自己决定是否重试。这样可以更早暴露问题,而不是默默卡很久。
5.2 监控与告警建议
连接器配好之后,不代表就一劳永逸了。Hbase 集群的状态波动会直接影响 openLooKeng 查询质量,所以要建立一套简单的监控。至少要看三个指标:openLooKeng 侧每个 catalog 的查询失败率,Hbase 侧的 RegionServer 的读请求队列长度,还有 GC 时间。如果发现 openLooKeng 查询失败率上升,先把 Hbase 的 UI 打开看看是 RegionServer 挂了,还是 zookeeper 会话断了。如果 zookeeper 会话断了,很可能就是网络不稳定,而不是 Hbase 本身有问题。
监控工具体系上,如果团队已经有 Prometheus,可以采集 openLooKeng 的 JVM 和查询指标,再用 Grafana 展示。Hbase 自己也带了很多 metrics,通过 HBase Servlet 或 JMX 可以暴露出来。把两边拉到同一个 Dashboard 里,出问题时能快速定位是引擎侧还是存储侧。
运维上还有一个容易踩坑的地儿是滚动重启。当你升级 openLooKeng 版本时,连接器配置里的某些参数名称可能会改,或者默认行为会变。比如旧版本里下推默认开启,新版本可能默认关闭,这就会导致升完级后,原本很快的等值查询突然变慢。因此每次升级前,一定要先看该版本的 release notes 里关于 Hbase 连接器的部分。升级后不要再沿用旧配置一把梭,而是用真实业务 SQL 回归一遍关键查询。
6. 个人实践中的最后经验
最后说点实在的。我在配置 openLooKeng 连接 Hbase 时,最大的体会是“别想着一次配完就收工”。连接器配置从能用到好用,中间隔着几轮调优。如果你只是做概念验证,把基础参数配好跑通一个查询,可能一天就够了。但如果你想把它用到生产查询网关里,那我建议你至少花一个下午专门调 rowkey 映射关系和并发参数。
还有一个小技巧:配置文件的注释非常重要。openLooKeng 的 catalog 配置里支持#注释,我习惯在每个参数后面写清楚这个参数是干什么的、为什么调成这个值。因为半年后你自己回来看,或者交给其他同事维护时,这些注释能省下大把的排错时间。尤其是像hbase.client.scanner.caching这种数字,如果不写注释,后来的人根本不知道当初是出于什么考虑调成 1000 而不是 500。
另外,如果你所在的团队对 SQL 有严格审计需求,最好在 openLooKeng 里启用查询日志,把它输出到单独的文件。这样你可以追踪哪些慢查询走了 Hbase 全表 scan,哪些走了精确实点查询。这一步虽然简单,但对我来说是排查生产问题的最大帮手。配置一个连接器不难,难的是把它变成团队里所有人都能安心依赖的数据通道。希望这篇文章里提到的每一步和每一个坑,能让你少走几段冤枉路。