「从零单排HBase 12」HBase二级索引Phoenix使用与最佳实践
Phoenix是构建在HBase上的一个SQL层,能让我们用标准的JDBC APIs对HBase数据进行增删改查,构建二级索引。当然,开源产品嘛,自然需要注意“避坑”啦,阿丸会把使用方式和最佳实践都告诉你。
1.什么是Phoenix
Phoenix完全使用Java编写,将SQL查询转换为一个或多个HBase扫描,并编排执行以生成标准的JDBC结果集。Phoenix主要能做以下这些事情:
- 将SQL查询编译为HBase扫描scan
- 确定scan的开始和停止位置
- 将scan并行执行
- 将where子句中的谓词推送到服务器端进行过滤
- 通过服务器端挂钩(称为协处理器co-processors)执行聚合查询
除了这些之外,phoenix还进行了一些有趣的增强,以进一步优化性能:
- 二级索引,以提高非行键查询的性能(这也是我们引入phoenix的主要原因)
- 跳过扫描过滤器来优化IN,LIKE和OR查询
- 可选的对行键进行加盐以实现负载均衡,避免热点
2.Phoniex架构
Phoenix结构上划分为客户端和服务端两部分:
- 客户端包括应用程序开发,将SQL进行解析优化生成QueryPlan,进而转化为HBase Scans,调用HBase API下发查询计算请求,并接收返回结果;
- 服务端主要是利用HBase的协处理器,处理二级索引、聚合及JOIN计算等。
Phoiex的旧版架构采用重客户端的模式,在客户端执行一系列的parser、query plan的过程,如下图所示。
这种架构存在使用上的缺陷:
- 应用程序与Phoenix core绑定使用,需要引入Phoenix内核依赖,一个单独Phoenix重客户端集成包已达120多M;
- 运维不便,Phoenix仍在不断优化和发展,一旦Phoenix版本更新,那么应用程序也需要对应升级版本并重新发布;
- 仅支持Java API,其他语言开发者不能使用Phoenix。
因此,社区进行改造,引入了新的“轻客户端”模式。
轻客户端架构将Phoenix分为两部分:
- 客户端是用户最小依赖的JDBC驱动程序,与Phoenix依赖进行解耦,支持Java、Python、Go等多种语言客户端;
- 将QueryServer部署为一个独立的的HTTP服务,接收轻客户端的请求,对SQL进行解析、优化、产生执行计划;
3.基本使用
传送门:https://phoenix.apache.org/language/index.html(平时多参考官方的语法)
3.1 建表
在phoenix shell上建的表默认只有一个region,建表时要注意预分区
// step1: 【phoenix shell】如果没有创建过namespace的话,需要这一步
create schema "MDW_NS";
或者
create schema mdw_ns; // step2: 【hbase shell】在原生HBase上创建一个表,为什么需要这一步(可以使用预分区的功能,phoenix的建表语句在分区上的支持比较弱)
create 'MDW_NS:TABLE_DEMO', 'F1', {NUMREGIONS => 16, SPLITALGO => 'HexStringSplit'} // step3: 【phoenix shell】创建一个同步的phoenix表
craete table if not exists mdw_ns.table_demo (
id varchar not null primary key,
f1.a varchar,
f1.b varchar,
f1.c varchar,
f1.d varchar,
f1.e varchar,
f1.f varchar,
f1.g varchar,
f1.h varchar
)
TTL=86400,
UPDATE_CACHE_FREQUENCY=900000;
3.2 建索引
需要关注索引表的散列问题
// 方式1: split分区(适用于索引列满足散列条件)
create index table_demo_a on mdw_ns.table_demo(f1.a) split on ('10000000','20000000','30000000','40000000','50000000','60000000','70000000','80000000','90000000','a0000000','b0000000','c0000000','d0000000','e0000000','f0000000'); // 方式2: salt分区(适用于索引列不满足散列条件)
create index table_demo_a on mdw_ns.table_demo(f1.a) salt_buckets = 16; // 注意: 索引列DESC的使用是不支持的,但对原表的主键是支持desc的
[x] create index table_demo_a on mdw_ns.table_demo(f1.a desc)
[√] craete table if not exists mdw_ns.table_demo (
f1.a varchar,
f1.b varchar,
f1.c varchar,
f1.d varchar,
f1.e varchar,
f1.f varchar,
f1.g varchar,
f1.h varchar,
constraint pk primary key(a, b desc)
);
3.3 select查询
[√] select * from mdw_ns.table_demo limit 10;
// 主键查询
[√] select * from mdw_ns.table_demo where id = '0000000000';
// 二级索引查询必须指定列名
[x] select * from mdw_ns.table_demo where a = '0000000000';
[√] select id, a, b, c, d, e from mdw_ns.table_demo where a = '0000000000'
4.最佳实践
4.1 大小写问题
注意点:phoenix对于(表名,列名,namespace,cf)是区分大小写,默认都会转为成大写。如果要屏蔽转换,需要在对应的字符上用双引号(")。数据类型是字符串的话,要用单引号(')包含。
// case1: 查询「mdw_ns」(namespace)下「table_demo」(table),其中「f1」(列簇)+ 「A」(列名)为字符串「'0000000000'」 的记录
select id, a, b, c from "mdw_ns"."table_demo" where "f1".a = '0000000000' // case2: 查询PS_NS(namespace)下「MODULE_REVISION」(table)的记录
select * from ps_ns.module_revision;
4.2 加盐注意事项
加盐通常用来解决数据热点和范围查询同时存在的场景,原理介绍可以参考Phoenix社区文档。
一般我们只在同时满足以下需求的时候,才使用加盐:
- 写热点或写不均衡
- 需要范围查询
有热点就要打散,但打散就难以做范围查询。因此,要同时满足这对相互矛盾的需求,必须有一种折中的方案:既能在一定程度上打散,又能保证一定程度的有序。这个解决方案就是加盐,其实叫分桶(salt buckets)更准确。数据在桶内保序,桶之间随机。写入时按桶个数取模,数据随机落在某个桶里,保证写请求在桶之间是均衡的。查询时读取所有的桶来保证结果集的有序和完备。
副作用:
写瓶颈:由于桶内保序,所以即使region不断split变多,全表实际上还是只有数量为buckets的region用于承担写入。当业务体量不断增长时,因为无法调整bucket数量,不能有更多的region帮助分担写,会导致写入吞吐无法随集群扩容而线性增加。导致写瓶颈,从而限制业务发展。
读扩散:select会按buckets数量进行拆分和并发,每个并发都会在执行时占用一个线程。select本身一旦并发过多会导致线程池迅速耗尽或导致QueryServer因过高的并发而FGC。同时,本应一个RPC完成的简单查询,现在也会拆分成多个,使得查询RT大大增加。
这两个副作用会制约业务的发展,尤其对于大体量的、发展快速的业务。因为桶个数不能修改,写瓶颈会影响业务的扩张。读扩散带来的RT增加也大大降低了资源使用效率。
一定不要为了预分区而使用加盐特性,要结合业务的读写模式来进行表设计。
Buckets个数跟机型配置和数据量有关系,可以参考下列方式计算,其中 N 为 Core/RS 节点数量:
单节点内存 8G: 2*N
单节点内存 16G: 3*N
单节点内存 32G: 4*N
单节点内存 64G: 5*N
单节点内存 128G: 6*N
注意:索引表默认会继承主表的盐值;bucket的数目不能超过256;一个空的Region在内存中的数据结构大概2MB,用户可以评估下单个RegionServer承载的总Region数目,有用户发生过在低配置节点上,建大量加盐表直接把集群内存耗光的问题。
4.3 慎用扫全表、OR、Join和子查询
虽然Phoenix支持各种Join操作,但是Phoenix主要还是定位为在线数据库,复杂Join,比如子查询返回数据量特别大或者大表Join大表,在实际计算过程中十分消耗系统资源,会严重影响在线业务,甚至导致OutOfMemory异常。对在线稳定性和实时性要求高的用户,建议只使用Phoenix的简单查询,且查询都命中主表或者索引表的主键。另外,建议用户在运行SQL前都执行下explain,确认是否命中索引,或者主键。
4.4 Phoenix不支持复杂查询
Phoenix的二级索引本质还是前缀匹配,用户可以建多个二级索引来增加对数据的查询模式,二级索引的一致性是通过协处理器实现的,索引数据可以实时可见,但也会影响写性能,特别是建多个索引的情况下。对于复杂查询,比如任意条件的and/or组合,模糊查找,分词检索等Phoenix不支持,建议使用搜索引擎(如solr、es等)。当然,搜索引擎采用后台异步索引,必然会影响实时性,需要仔细权衡。
4.5 Phoenix不支持复杂分析
Phoenix定位为操作型分析(operational analytics),对于复杂分析,比如前面提到的复杂join则不适合,这种建议用Spark这种专门的大数据计算引擎来实现。
4.6 Phoenix支持映射已经存在的HBase表
参考社区相关文档。用户可以通过Phoenix创建视图或者表映射已经存在的HBase表,注意如果使用表的方式映射HBase表,在Phoenix中执行DROP TABLE语句同样也会删除HBase表。另外,由于column family和列名是大小写敏感的,必须一一对应才能映射成功。另外,Phoenix的字段编码方式大部分跟HBase的Bytes工具类不一样,一般建议如果只有varchar类型,才进行映射,包含其他类型字段时不要使用映射。
5.使用规范
- 大小写约定:由于phoenix对大小写敏感,默认又会转换成大写,我们建表的时候都以大写作为规范,避免不必要的麻烦
- 索引名命名:每个索引都会在hbase集群建一张索引表,便于识别索引的归属,建议索引名按 {表名}_{索引标识} 的规范命名(如表名TABLE_DEMO,索引名可以为TABLE_DEMO_C)
- 原表分区原则:建议使用原生HBase的预分区方式
- 索引表分区原则:建议使用salt_buckets的分区方式
- select使用原则:建议不要使用select * 方式
- 建表注意点:不要使用默认的UPDATE_CACHE_FREQUENCY策略(ALWAYS),改成UPDATE_CACHE_FREQUENCY = 60000
看到这里了,原创不易,点个关注、点个赞吧,你最好看了~
知识碎片重新梳理,构建Java知识图谱:https://github.com/saigu/JavaKnowledgeGraph(历史文章查阅非常方便)
「从零单排HBase 12」HBase二级索引Phoenix使用与最佳实践的更多相关文章
- 「从零单排canal 03」 canal源码分析大纲
在前面两篇中,我们从基本概念理解了canal是一个什么项目,能应用于什么场景,然后通过一个demo体验,有了基本的体感和认识. 从这一篇开始,我们将从源码入手,深入学习canal的实现方式.了解can ...
- 「从零单排canal 04」 启动模块deployer源码解析
基于1.1.5-alpha版本,具体源码笔记可以参考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_read ...
- 「从零单排canal 05」 server模块源码解析
基于1.1.5-alpha版本,具体源码笔记可以参考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_read ...
- 「从零单排canal 06」 instance模块源码解析
基于1.1.5-alpha版本,具体源码笔记可以参考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_read ...
- 「从零单排canal 07」 parser模块源码解析
基于1.1.5-alpha版本,具体源码笔记可以参考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_read ...
- 「从零单排canal 02」canal集群版 + admin控制台 最新搭建姿势(基于1.1.4版本)
canal [kə'næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据 订阅 和 消费.应该是阿里云DTS(Data Transfer Service)的开 ...
- 「从零单排canal 01」 canal 10分钟入门(基于1.1.4版本)
1.简介 canal [kə'næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据 订阅 和 消费.应该是阿里云DTS(Data Transfer Servi ...
- phoenix连接hbase数据库,创建二级索引报错:Error: org.apache.phoenix.exception.PhoenixIOException: Failed after attempts=36, exceptions: Tue Mar 06 10:32:02 CST 2018, null, java.net.SocketTimeoutException: callTimeou
v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VM ...
- [转帖]12条用于Linux的MySQL/MariaDB安全最佳实践
12条用于Linux的MySQL/MariaDB安全最佳实践 2018-01-04 11:05:56作者:凉凉_,soaring稿源:开源中国社区 https://ywnz.com/linuxysjk ...
随机推荐
- Python中的可视化神器!你知道是啥吗?没错就是pyecharts!
pyecharts是一款将python与echarts结合的强大的数据可视化工具,本文将为你阐述pyecharts的使用细则 前言 我们都知道python上的一款可视化工具matplotlib,而前些 ...
- Java中BigDecimal类
由于在运算的时候,float类型和double很容易丢失精度,演示案例,所以,为了能精确地表示.计算浮点数,Java提供了BIgDecimal BigDecimal类的概述 不可变的.任意精度的有符号 ...
- iconv 参数详解
参数详解: $row [] = iconv('utf-8', 'GB2312//IGNORE', $value['message']); iconv ( string $in_charset , st ...
- 微软的 Sysinternals 系统管理工具包,例如可找出自动启动的流氓软件
作者:Kenny链接:https://www.zhihu.com/question/52157612/answer/153886419来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载 ...
- cut,xargs,sort,tr,rename命令解析
cut 文件内容查看 显示行中的指定部分,删除文件中指定字段 显示文件的内容,类似于下的type命令. 语法: cut(选项)(参数) 选项: -b:仅显示行中指定直接范围的内容: -c:仅显示行中指 ...
- JS代码规范
JS代码规范 空格 二元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格. var a = !arr.length; a++; a = b + c; 复制代码 用作代码块起始的左花括号 ...
- php private学习笔记
类的权限修饰符,放在属性/方法的前面.用来说明属性/方法的权限特点. 三种权限修饰符 private 私有的 public 公共 protected 保护的 privata 的属性.方法只能在 ...
- 记坑: ConfigurationProperties 和 RefreshScope
为什么80%的码农都做不了架构师?>>> 当使用spring-cloud-config 和 spring-boot-configuration-processor时 使用bean ...
- 一个页面从输入url到页面加载完成究竟经历了些什么
本人经参考谢希仁著<计算机网络(第 5版)>.<HTTP权威指南>和网络上关于浏览器渲染原理的介绍,结合自己理解,整理出以下结论,如有不正确或者不完善之处欢迎指正: 当用户在浏 ...
- Spring LDAP的使用
LDAP入门http://www.jianshu.com/p/7e4d99f6baaf Spring LDAP,是Spring的一个组件,实现对LDAP的操作. 在编程操作MySQL时,我们除了用JD ...