明朔 2019-05-21 22:24:38 浏览 217 评论 1
云栖社区
数据存储与数据库
分布式
大数据
架构
分布式系统与计算
hbase
性能
SQL
线程
索引
- JDBC
- phoenix
- NewSQL
摘要: 阿里云 HBase SQL 基于 Phoenix 5.0 版本, 为云 HBase2.0 赋予 NewSQL 特性, 降低 kv 接口使用复杂性, 并提供 Schema,Secondary Indexes,View ,Bulk Loading(离线大规模 load 数据),Atomic Upsert,Salted Tables,Dynamic Columns,Skip Scan 等特性的能力, 大大降低了用户的使用门槛.
阿里云 HBase SQL 服务简介
云 HBase2.0 是阿里云对社区 HBase2.0 的深度定制, 在内核层面做了大量优化升级, 并提供全球多活, 备份恢复, 冷存储等企业级特性, 目前已被广泛应用于车联网, 社交, 推荐, 画像等场景. 阿里云 HBase SQL 基于 Phoenix 5.0 版本, 为云 HBase2.0 赋予 NewSQL 特性, 降低 kv 接口使用复杂性, 并提供 Schema,Secondary Indexes,View ,Bulk Loading(离线大规模 load 数据),Atomic Upsert,Salted Tables,Dynamic Columns,Skip Scan 等特性的能力, 大大降低了用户的使用门槛 (关于更多 Phoenix 的介绍可以参考: HBase 进化之从 NoSQL 到 NewSQL, 凤凰涅槃成就 Phoenix). 阿里云 HBase 团队将 SQL 能力服务化, 在产品, 内核上做了一系列优化升级, 笔者将在本文中对这些特性做深度解读.
HBase SQL 服务化
HBase SQL 服务如图所示, 用户在阿里云 HBase 实例管理页面可以一键开通, 并在界面进行配置, 重启, 升级, 获取 URL 串之后直接可以访问. 用户可以使用 Java,Python,Go,C# 开发应用程序访问 SQL 服务, 也可以使用命令行工具直接访问 SQL 服务, 详细使用说明, 可以参考我们的文档: SQL 服务使用文档. 使用过 Phoenix 的开发者都知道, Phoenix 社区提供两种访问方式, 重客户端和轻客户端, 关于这两者之间的区别, 可以参考: Phoenix 客户端进化之由重到轻. 除了将 Phoenix 服务化外, 我们还做了一系列优化升级, 既有对轻客户端的优化升级, 也有对 Phoenix Core 本身的优化升级.
轻客户端优化
云 HBase SQL 团队针对社区 Phoenix5.0 版本进行深度测试, 对轻客户端进行了一系列的优化和 bug fix, 并反馈给 Phoenix 和 Avatica 社区, 推动社区共同发展.
1.轻客户端写性能优化
在对轻客户端的写入性能测试时, 我们发现社区轻客户端的写入性能比直接使用重客户端慢 2~3 倍, 主要原因正式由于轻客户端相比重客户端多了一层 HTTP 传输链路, 因此在尽量不修改业务代码的前提下, 如何减少请求次数是优化写性能的重点.
在业务代码中经常使用 jdbc 的 PreparedStatement 的 executeUpdate 进行写入, 社区轻客户端在 autoCommit 为 false 时会将数据传输至 QueryServer 并缓存, 在 commit 时将数据 batch 写入到 HBase 中. 阿里云 Phoenix 修改了 executeUpdate 执行逻辑, 将数据缓存在客户端, commit 时将批量数据发到 QueryServer 并直接写入 HBase, 在这一阶段减少了轻客户端到 QueryServer 的 RPC 请求次数, 并避免缓存在 QueryServer 的数据在未提交前由于服务挂掉而丢失.
优化后在代码写入性能提升了 2 倍多, 与重客户端性能基本没有差距.
2.支持 upsert multi values 语法
原生 Phoenix 在写入时仅支持单次 upsert 一条数据的语法, 这在对接 Mybatis 等不支持 commit 的框架时, 每次写入一条数据并提交 HBase 性能上较差, 为此阿里云 Phoenix 在 SQL 服务上支持了单次 upsert 写入多值功能, 语法示例: upsert into tableName(col1,col2,...) values (v11,v12,...),(v21,v22,...),...,(vn1,vn2,...)
在使用时注意:
为避免列次序混乱, 必须指定写入列
写入数据其中一条失败导致整批数据写入失败
3.支持 QueryServer 线程池最大线程和 HTTP 传输参数可配
QueryServer 使用 Jetty 做为 HttpServer, 在初始化时默认最大 200 个线程, 在使用配置比较高的 HBase 集群时, 往往由于处理线程数成为性能瓶颈, 阿里云 Phoenix 修改了 Avatica 代码, 通过增加配置方式使得线程数以及 HTTP 传输 size 大小等都可配, 增加了 Phoenix 使用的灵活性, 具体配置及说明如下:
参数名 | 描述 |
---|---|
phoenix.queryserver.maximun.threads | QueryServer 最大线程数,默认 200 |
phoenix.queryserver.minimum.threads | QueryServer 最小线程数,默认 8 |
phoenix.queryserver.idle.timeout | 线程空闲超时时间,默认 60s |
phoenix.queryserver.maximum.header.size | HTTP 请求包头大小,默认 65536byte |
以上参数在云 HBase 控制台都可以进行修改, 并重启 HBase SQL 服务后生效.
4.轻客户端 Connection 超时 Reopen 时设置参数丢失问题
轻客户端连接默认空闲 10min 会超时, 下次使用时会进行重建, 如果在超时前设置 autoCommit,schema 等连接属性会在 reOpen 时失效, 这是社区 avatica 的一个 bug, 我们对此进行了修复, 并贡献到社区, 对应 jira: https://issues.apache.org/jira/browse/CALCITE-2882 (贡献社区 calcite-avatica 1.14.0 版本)
5.轻客户端 PrepareStatement 预编译带有子查询 / join 语句时参数越界
在使用轻客户端 PrepareStatement 预编译 SQL 时会将 phoenix 客户端的 ParamMetaData 同步到轻客户端的 Connection, 当 SQL 带有子查询或 join 语句时, Phoenix 在实现上缺失了右表的 Meta 信息, 导致出现参数越界的问题.
阿里云的 Phoenix 版本也对此进行了修复, 详细见 jira: https://issues.apache.org/jira/browse/PHOENIX-5192
6.轻客户端写入 Array 类型数据 NPE
轻客户端写入 Array 类型数据时由于获取 Array 中子元素的类型默认处理为空, 导致在写入时出现 NPE. 问题修复见
jira: https://issues.apache.org/jira/browse/CALCITE-2939 (贡献社区 calcite-avatica 1.14.0 版本)
Phoenix Core 优化与改进
我们对 Phoenix Core 做了大量稳定性, 性能方面的优化和改进, 提交了数十个 patch 给社区, 团队的瑾谦同学也成为了 Phoenix 社区的 Commiter. 下面介绍几个比较有代表性的改进工作:
1.大表 MetaCache 缓存优化
Phoenix 每次查询都需要使用表的所有 region 信息, 当表很大有上万个 region 时, 读取 meta 表的 region 信息并缓存到客户端需要耗时几十秒, 占用查询时间超过 80% 以上. 阿里云 Phoenix 使用 ZooKeeper 订阅的方式, 通过监控 Region 的状态变化, 当 Region 发生 balance/merge/split 动作时, 更新客户端缓存失效的 region 信息, 这样可以大大减少客户端因为 Region 元数据更新不及时导致的查询失败, 同时显著提高查询性能.
优化效果: 经过测试对一张大表查询时如果出现 Region 状态变化, 查询时延由 40s 降低到 5s 左右.
2.使用 Lookup Join 提升大表的索引表回查性能
如果 Phoenix 二级索引不是覆盖索引, 也就是说 SELECT 语句中的返回字段不在索引表中, 那么在执行过程中会先查询索引表, 获取 RowKey, 然后再用 SemiJoin 主表的方式获取最终结果. 除了索引表本身的 Scan 开销外, 还存在网络开销和 SemiJoin 本身的开销. 我们采用 Lookup Join 的方式进行优化, 在服务端 scan 索引表的时候, 直接回查主表. 经过测试, 性能有大幅提高, 500w 数据量的查询, 原有方式需要 200s, 而新的方式只需要 10s, 如果主表开启了 bloomfilter, 性能还能进一步提高到 5s 左右.
3.使用 MultiGet 取代 SkipScan
Phoenix 在执行 SemiJoin 的时候会使用 SkipScan 的方式, 该方式比较通用, scan 的过程中会 skip 掉一定的读 HFile 开销, 但是当 SemiJoin 的查询条件比较多并且比较分散时, 就转变为了近似扫全表, 这时性能就会下降严重, 甚至超时. 对于这种情况, 我们采用了 MultiGet 的方式取代 SkipScan 会获得更好的性能. 当用户对 Primary Key 做 in 查询时, 可以通过添加 "USE_GET" 的 hint 使用该功能, 可以支持 10w 级别的子查询结果. 在查询条件比较多并且分散的情况, 会有数量级的性能提升.
4.临时禁止掉存在风险的功能 feature
社区 Phoenix 功能很多很全, 但是某些功能存在一定缺陷, 或者会引入风险, 在确保功能完善可用之前, 我们会选择将部分功能禁掉, 以避免用户误用, 导致错误. 目前涉及到的功能有:
ColumnDef 中的 DESC 关键字. 对于可变长度类型, 比如 varchar,decimal, 该功能可能范围查询结果返回错误.
全表扫描. 对于没有加查询条件的查询语句, 可能会触发全表扫描, 导致系统不稳定.
Local Index. 由于社区反馈和我们在实际支持用户的过程中发现的 Local Index 问题较多, 而且大部分二级索引的场景, Global Index 已经足够使用, 因此我们临时禁掉了 Local Index 的功能.
5.时区问题解决
社区 Phoenix 在时区处理上的逻辑会导致用户的不同用法, 写入数据和查询结果不一致. 详见文章: Phoenix 关于时区的处理方式说明
总结展望
阿里云 HBase 团队在 SQL 服务上持续深耕, 不断在稳定性, 易用性, 功能以及性能上继续增强优化, 并不断反馈社区, 积极推动 Phoenx 社区的发展. 从 HBase 的架构上来看, 相比于其他 NoSQL 存储引擎, 其在数据规模, 扩展性上具有明显优势. 跟大数据其他组件, 比如, 搜索引擎, 分析引擎, 消息引擎, 会更紧密的结合, SQL 服务也会在这方面进行更多的探索, 满足用户在实际使用过程中遇到的复杂查询, 复杂分析等需求. 在这里可以预告下, 我们基于 HBase 和 Solr 双引擎, 支持复杂查询的 Phoenix Search Index 功能即将上线.
来源: https://yq.aliyun.com/articles/703234