Apache Phoenix介绍(SQL on HBase)

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

1. Phoenix定义

Phoenix最早是saleforce的一个开源项目,后来成为Apache基金的顶级项目。

Phoenix是构建在HBase上的一个SQL层,能让我们用标准的JDBC APIs而不是HBase客户端APIs来创建表,插入数据和对HBase数据进行查询。

put the SQL back in NoSQL

Phoenix完全使用Java编写,作为HBase内嵌的JDBC驱动。Phoenix查询引擎会将SQL查询转换为一个或多个HBase扫描,并编排执行以生成标准的JDBC结果集。直接使用HBase API、协同处理器与自定义过滤器,对于简单查询来说,其性能量级是毫秒,对于百万级别的行数来说,其性能量级是秒。

HBase的查询工具有很多,如:Hive、Tez、Impala、Spark SQL、Phoenix等。

Phoenix通过以下方式使我们可以少写代码,并且性能比我们自己写代码更好:

  • 将SQL编译成原生的HBase scans。
  • 确定scan关键字的最佳开始和结束
  • 让scan并行执行
  • ...

使用Phoenix的公司

Paste_Image.png

2. 历史演进

  • 3.0/4.0 release

ARRAY Type. 支持标准的JDBC数组类型

Sequences. 支持 CREATE/DROP SEQUENCE, NEXT VALUE FOR, CURRENT VALUE FOR也实现了

Multi-tenancy. 同一张HBase物理表上,不同的租户可以创建相互独立的视图

Views. 同一张HBase物理表上可以创建不同的视图

  • 3.1/4.1 release

Apache Pig Loader . 通过pig来处理数据时支持pig加载器来利用Phoenix的性能

Derived Tables. 允许在一个FROM子句中使用SELECT子句来定义一张衍生表

Local Indexing. 后面介绍

Tracing. 后面介绍

  • 3.2/4.2 release

Subqueries 支持在WHERE和FROM子句中的独立子查询和相关子查询

Semi/anti joins. 通过标准的[NOT] IN 和 [NOT] EXISTS关键字来支持半/反连接

Optimize foreign key joins. 通过利用跳跃扫描过滤器来优化外键连接

Statistics Collection. 通过收集表的统计信息来提高并行查询能力

  • ** 3.3/4.3 release**

Many-to-many joins. 支持两边都太大以至于无法放进内存的连接

Map-reduce Integration. 支持Map-reduce集成

Functional Indexes. 后面介绍

  • 4.4 release

User Defined Functions. 后面介绍

  • 4.5 release

Asynchronous Index Population. 通过一个Map-reduce job,索引可以被异步创建

  • 4.6 release

Time series Optimization. 优化针对时间序列数据的查询

  • 4.7 release

Transaction Support. 后面介绍

  • ** 4.8 release**

DISTINCT Query Optimization. 使用搜索逻辑来大幅提高 SELECT DISTINCT 和 COUNT DISTINCT的查询性能

Local Index Improvements. Reworked 后面介绍

Hive Integration. 能够在Phoenix内使用Hive来支持大表和大表之间的连接

Namespace Mapping. 将Phoenix schema映射到HBase的命名空间来增强不同schema之间的隔离性

3. 特性

3.1 Transactions (beta) 事务

该特性还处于beta版,并非正式版。通过集成Tephra,Phoenix可以支持ACID特性。Tephra也是Apache的一个项目,是事务管理器,它在像HBase这样的分布式数据存储上提供全局一致事务。HBase本身在行层次和区层次上支持强一致性,Tephra额外提供交叉区、交叉表的一致性来支持可扩展性。

要想让Phoenix支持事务特性,需要以下步骤:

  • 配置客户端hbase-site.xml


  phoenix.transactions.enabled
  true


  • 配置服务端hbase-site.xml


  data.tx.snapshot.dir
  /tmp/tephra/snapshots



  data.tx.timeout
  60
   set the transaction timeout (time after which open transactions become invalid) to a reasonable value.


  • 配置$HBASE_HOME并启动Tephra

./bin/tephra

通过以上配置,Phoenix已经支持了事务特性,但创建表的时候默认还是不支持的。如果想创建一个表支持事务特性,需要显示声明,如下:


CREATE TABLE my_table (k BIGINT PRIMARY KEY, v VARCHAR) TRANSACTIONAL=true;

就是在建表语句末尾增加 TRANSACTIONAL=true

原本存在的表也可以更改成支持事务的,需要注意的是,事务表无法改回非事务的,因此更改的时候要小心。一旦改成事务的,就改不回去了。


ALTER TABLE my_other_table SET TRANSACTIONAL=true;

3.2 User-defined functions(UDFs) 用户定义函数

3.2.1 概述

Phoenix从4.4.0版本开始支持用户自定义函数。

用户可以创建临时或永久的用户自定义函数。这些用户自定义函数可以像内置的create、upsert、delete一样被调用。临时函数是针对特定的会话或连接,对其他会话或连接不可见。永久函数的元信息会被存储在一张叫做SYSTEM.FUNCTION的系统表中,对任何会话或连接均可见。

3.2.2 配置

  • hive-site.xml


  phoenix.functions.allowUserDefinedFunctions
  true


  fs.hdfs.impl
  org.apache.hadoop.hdfs.DistributedFileSystem


  hbase.rootdir
  ${hbase.tmp.dir}/hbase
  The directory shared by region servers and into
    which HBase persists.  The URL should be 'fully-qualified'
    to include the filesystem scheme.  For example, to specify the
    HDFS directory '/hbase' where the HDFS instance's namenode is
    running at namenode.example.org on port 9000, set this value to:
    hdfs://namenode.example.org:9000/hbase.  By default, we write
    to whatever ${hbase.tmp.dir} is set too -- usually /tmp --
    so change this configuration or else all data will be lost on
    machine restart.


  hbase.dynamic.jars.dir
  ${hbase.rootdir}/lib
  
    The directory from which the custom udf jars can be loaded
    dynamically by the phoenix client/region server without the need to restart. However,
    an already loaded udf class would not be un-loaded. See
    HBASE-1936 for more details.
  


后两个配置需要跟hbse服务端的配置一致。

以上配置完后,在JDBC连接时还需要执行以下语句:


Properties props = new Properties();
props.setProperty("phoenix.functions.allowUserDefinedFunctions", "true");
Connection conn = DriverManager.getConnection("jdbc:phoenix:localhost", props);

以下是可选的配置,用于动态类加载的时候把jar包从hdfs拷贝到本地文件系统



  hbase.local.dir
  ${hbase.tmp.dir}/local/
  Directory on the local filesystem to be used
    as a local storage.



3.3 Secondary Indexing 二级索引

在HBase中,只有一个单一的按照字典序排序的rowKey索引,当使用rowKey来进行数据查询的时候速度较快,但是如果不使用rowKey来查询的话就会使用filter来对全表进行扫描,很大程度上降低了检索性能。而Phoenix提供了二级索引技术来应对这种使用rowKey之外的条件进行检索的场景。

  • Covered Indexes

只需要通过索引就能返回所要查询的数据,所以索引的列必须包含所需查询的列(SELECT的列和WHRER的列)

  • Functional Indexes

从Phoeinx4.3以上就支持函数索引,其索引不局限于列,可以合适任意的表达式来创建索引,当在查询时用到了这些表达式时就直接返回表达式结果

  • Global Indexes

Global indexing适用于多读少写的业务场景。
使用Global indexing的话在写数据的时候会消耗大量开销,因为所有对数据表的更新操作(DELETE, UPSERT VALUES and UPSERT SELECT),会引起索引表的更新,而索引表是分布在不同的数据节点上的,跨节点的数据传输带来了较大的性能消耗。在读数据的时候Phoenix会选择索引表来降低查询消耗的时间。在默认情况下如果想查询的字段不是索引字段的话索引表不会被使用,也就是说不会带来查询速度的提升。

  • Local Indexes

Local indexing适用于写操作频繁的场景。
与Global indexing一样,Phoenix会自动判定在进行查询的时候是否使用索引。使用Local indexing时,索引数据和数据表的数据是存放在相同的服务器中的避免了在写操作的时候往不同服务器的索引表中写索引带来的额外开销。使用Local indexing的时候即使查询的字段不是索引字段索引表也会被使用,这会带来查询速度的提升,这点跟Global indexing不同。一个数据表的所有索引数据都存储在一个单一的独立的可共享的表中。

3.4 Statistics Collection 统计信息收集

UPDATE STATISTICS可以更新某张表的统计信息,以提高查询性能

3.5 Row timestamp 时间戳

从4.6版本开始,Phoenix提供了一种将HBase原生的row timestamp映射到Phoenix列的方法。这样有利于充分利用HBase提供的针对存储文件的时间范围的各种优化,以及Phoenix内置的各种查询优化。

3.6 Paged Queries 分页查询

Phoenix支持分页查询:

  • Row Value Constructors (RVC)
  • OFFSET with limit

3.7 Salted Tables 散步表

如果row key是自动增长的,那么HBase的顺序写会导致region server产生数据热点的问题,Phoenix的Salted Tables技术可以解决region server的热点问题

3.8 Skip Scan 跳跃扫描

可以在范围扫描的时候提高性能

3.9 Views 视图

标准的SQL视图语法现在在Phoenix上也支持了。这使得能在同一张底层HBase物理表上创建多个虚拟表。

3.10 Multi tenancy 多租户

通过指定不同的租户连接实现数据访问的隔离

3.11 Dynamic Columns 动态列

Phoenix 1.2, specifying columns dynamically is now supported by allowing column definitions to included in parenthesis after the table in the FROM clause on a SELECT statement. Although this is not standard SQL, it is useful to surface this type of functionality to leverage the late binding ability of HBase.

3.12 Bulk CSV Data Loading 大量CSV数据加载

加载CSV数据到Phoenix表有两种方式:1. 通过psql命令以单线程的方式加载,数据量少的情况下适用。 2. 基于MapReduce的bulk load工具,适用于数据量大的情况

3.13 Query Server 查询服务器

Phoenix4.4引入的一个单独的服务器来提供thin客户端的连接

3.14 Tracing 追踪

从4.1版本开始Phoenix增加这个特性来追踪每条查询的踪迹,这使用户能够看到每一条查询或插入操作背后从客户端到HBase端执行的每一步。

3.15 Metrics 指标

Phoenix提供各种各样的指标使我们能够知道Phoenix客户端在执行不同SQL语句的时候其内部发生了什么。这些指标在客户端JVM中通过两种方式来收集:

  • Request level metrics - collected at an individual SQL statement
    level
  • Global metrics - collected at the client JVM level

4. 架构和组成

  • Phoenix架构

Phoenix Architecture.png

  • Phoenix在Hadoop生态系统中的位置

位置.png

5. 数据存储

Phoenix将HBase的数据模型映射到关系型世界

Data Model.png

6. 对QL的支持

支持的命令如下:

  • SELECT
Example:

SELECT * FROM TEST LIMIT 1000;
SELECT * FROM TEST LIMIT 1000 OFFSET 100;
SELECT full_name FROM SALES_PERSON WHERE ranking >= 5.0 UNION ALL SELECT reviewer_name FROM CUSTOMER_REVIEW WHERE score >= 8.0

  • UPSERT VALUES
Example:

UPSERT INTO TEST VALUES('foo','bar',3);
UPSERT INTO TEST(NAME,ID) VALUES('foo',123);


  • UPSERT SELECT
Example:

UPSERT INTO test.targetTable(col1, col2) SELECT col3, col4 FROM test.sourceTable WHERE col5 < 100
UPSERT INTO foo SELECT * FROM bar;


  • DELETE
Example:

DELETE FROM TEST;
DELETE FROM TEST WHERE ID=123;
DELETE FROM TEST WHERE NAME LIKE 'foo%';

  • CREATE TABLE
CREATE TABLE my_schema.my_table ( id BIGINT not null primary key, date)
CREATE TABLE my_table ( id INTEGER not null primary key desc, date DATE not null,m.db_utilization DECIMAL, i.db_utilization) m.DATA_BLOCK_ENCODING='DIFF'
CREATE TABLE stats.prod_metrics ( host char(50) not null, created_date date not null,txn_count bigint CONSTRAINT pk PRIMARY KEY (host, created_date) )
CREATE TABLE IF NOT EXISTS "my_case_sensitive_table"
    ( "id" char(10) not null primary key, "value" integer)
    DATA_BLOCK_ENCODING='NONE',VERSIONS=5,MAX_FILESIZE=2000000 split on (?, ?, ?)
CREATE TABLE IF NOT EXISTS my_schema.my_table (org_id CHAR(15), entity_id CHAR(15), payload binary(1000),CONSTRAINT pk PRIMARY KEY (org_id, entity_id) )TTL=86400


  • DROP TABLE
Example:

DROP TABLE my_schema.my_table;
DROP TABLE IF EXISTS my_table;
DROP TABLE my_schema.my_table CASCADE;

  • CREATE FUNCTION
Example:

CREATE FUNCTION my_reverse(varchar) returns varchar as 'com.mypackage.MyReverseFunction' using jar 'hdfs:/localhost:8080/hbase/lib/myjar.jar'
CREATE FUNCTION my_reverse(varchar) returns varchar as 'com.mypackage.MyReverseFunction'
CREATE FUNCTION my_increment(integer, integer constant defaultvalue='10') returns integer as 'com.mypackage.MyIncrementFunction' using jar '/hbase/lib/myincrement.jar'
CREATE TEMPORARY FUNCTION my_reverse(varchar) returns varchar as 'com.mypackage.MyReverseFunction' using jar 'hdfs:/localhost:8080/hbase/lib/myjar.jar'


  • DROP FUNCTION
Example:

DROP FUNCTION IF EXISTS my_reverse
DROP FUNCTION my_reverse


  • CREATE VIEW
Example:

CREATE VIEW "my_hbase_table"( k VARCHAR primary key, "v" UNSIGNED_LONG) default_column_family='a';
CREATE VIEW my_view ( new_col SMALLINT ) AS SELECT * FROM my_table WHERE k = 100;
CREATE VIEW my_view_on_view AS SELECT * FROM my_view WHERE new_col > 70;


  • DROP VIEW
Example:

DROP VIEW my_view
DROP VIEW IF EXISTS my_schema.my_view
DROP VIEW IF EXISTS my_schema.my_view CASCADE


  • CREATE SEQUENCE
Example:

CREATE SEQUENCE my_sequence;
CREATE SEQUENCE my_sequence START WITH -1000
CREATE SEQUENCE my_sequence INCREMENT BY 10
CREATE SEQUENCE my_schema.my_sequence START 0 CACHE 10


  • DROP SEQUENCE
Example:

DROP SEQUENCE my_sequence
DROP SEQUENCE IF EXISTS my_schema.my_sequence


  • ALTER
Example:

ALTER TABLE my_schema.my_table ADD d.dept_id char(10) VERSIONS=10
ALTER TABLE my_table ADD dept_name char(50), parent_id char(15) null primary key
ALTER TABLE my_table DROP COLUMN d.dept_id, parent_id;
ALTER VIEW my_view DROP COLUMN new_col;
ALTER TABLE my_table SET IMMUTABLE_ROWS=true,DISABLE_WAL=true;


  • CREATE INDEX
Example:

CREATE INDEX my_idx ON sales.opportunity(last_updated_date DESC)
CREATE INDEX my_idx ON log.event(created_date DESC) INCLUDE (name, payload) SALT_BUCKETS=10
CREATE INDEX IF NOT EXISTS my_comp_idx ON server_metrics ( gc_time DESC, created_date DESC ) DATA_BLOCK_ENCODING='NONE',VERSIONS=?,MAX_FILESIZE=2000000 split on (?, ?, ?)
CREATE INDEX my_idx ON sales.opportunity(UPPER(contact_name))


  • DROP INDEX
Example:

DROP INDEX my_idx ON sales.opportunity
DROP INDEX IF EXISTS my_idx ON server_metrics

  • ALTER INDEX
Example:

ALTER INDEX my_idx ON sales.opportunity DISABLE
ALTER INDEX IF EXISTS my_idx ON server_metrics REBUILD

  • EXPLAIN
Example:

EXPLAIN SELECT NAME, COUNT(*) FROM TEST GROUP BY NAME HAVING COUNT(*) > 2;
EXPLAIN SELECT entity_id FROM CORE.CUSTOM_ENTITY_DATA WHERE organization_id='00D300000000XHP' AND SUBSTR(entity_id,1,3) = '002' AND created_date < CURRENT_DATE()-1;

  • UPDATE STATISTICS
Example:

UPDATE STATISTICS my_table
UPDATE STATISTICS my_schema.my_table INDEX
UPDATE STATISTICS my_index
UPDATE STATISTICS my_table COLUMNS
UPDATE STATISTICS my_table SET phoenix.stats.guidepost.width=50000000

  • CREATE SCHEMA
Example:

CREATE SCHEMA IF NOT EXISTS my_schema
CREATE SCHEMA my_schema

  • USE
Example:

USE my_schema
USE DEFAULT

  • DROP SCHEMA
Example:

DROP SCHEMA IF EXISTS my_schema
DROP SCHEMA my_schema

7. 安装部署

7.1 安装预编译的Phoenix

  • 下载并解压最新版的phoenix-[version]-bin.tar包

  • 将phoenix-[version]-server.jar放入服务端和master节点的HBase的lib目录下

  • 重启HBase

  • 将phoenix-[version]-client.jar添加到所有Phoenix客户端的classpath

7.2 使用Phoenix

7.2.1 命令行

若要在命令行执行交互式SQL语句:

1.切换到bin目录

2.执行以下语句


$ sqlline.py localhost

若要在命令行执行SQL脚本


$ sqlline.py localhost ../examples/stock_symbol.sql

Paste_Image.png

7.2.2 客户端

SQuirrel是用来连接Phoenix的客户端。

SQuirrel安装步骤如下:


1. Remove prior phoenix-[*oldversion*]-client.jar from the lib directory of SQuirrel, copy phoenix-[*newversion*]-client.jar to the lib directory (*newversion* should be compatible with the version of the phoenix server jar used with your HBase installation)
2. Start SQuirrel and add new driver to SQuirrel (Drivers -> New Driver)
3. In Add Driver dialog box, set Name to Phoenix, and set the Example URL to jdbc:phoenix:localhost.
4. Type “org.apache.phoenix.jdbc.PhoenixDriver” into the Class Name textbox and click OK to close this dialog.
5. Switch to Alias tab and create the new Alias (Aliases -> New Aliases)
6. In the dialog box, Name: *any name*, Driver: Phoenix, User Name: *anything*, Password: *anything*
7. Construct URL as follows: jdbc:phoenix: *zookeeper quorum server*. For example, to connect to a local HBase use: jdbc:phoenix:localhost
8. Press Test (which should succeed if everything is setup correctly) and press OK to close.
9. Now double click on your newly created Phoenix alias and click Connect. Now you are ready to run SQL queries against Phoenix.

Paste_Image.png

8. 测试

8.1 Pherf

Pherf是可以通过Phoenix来进行性能和功能测试的工具。Pherf可以用来生成高度定制的数据集,并且测试SQL在这些数据集上的性能。

8.1.1 构建Pherf

Pherf是在用maven构建Phoenix的过程中同时构建的。可以用两种不同的配置来构建:

  1. 集群(默认)

This profile builds Pherf such that it can run along side an existing cluster. The dependencies are pulled from the HBase classpath.

  1. 独立

This profile builds all of Pherf’s dependencies into a single standalone jar. The deps will be pulled from the versions specified in Phoenix’s pom.

  • 构建全部的Phoenix。包含Pherf的默认配置。

mvn clean package -DskipTests



  • 用Pherf的独立配置来构建Phoenix。

mvn clean package -P standalone -DskipTests



8.1.2 安装

用以上的Maven命令构建完Pherf后,会在该模块的目标目录下生成一个zip文件。

  1. 将该zip文件解压到合适的目录
  2. 配置env.sh文件
  3. ./pherf.sh -h
  4. 想要在一个真正的集群上测试,运行如下命令:

./pherf.sh -drop all -l -q -z localhost -schemaFile .*user_defined_schema.sql -scenarioFile .*user_defined_scenario.xml

8.1.3 命令示例

  • 列出所有可运行的场景文件

$./pherf.sh -listFiles

  • 删掉全部场景文件中存在的特定的表、加载和查询数据

$./pherf.sh -drop all -l -q -z localhost


8.1.4 参数

-h Help
-l Apply schema and load data
-q Executes Multi-threaded query sets and write results
-z [quorum] Zookeeper quorum
-m Enable monitor for statistics
-monitorFrequency [frequency in Ms] _Frequency at which the monitor will snopshot stats to log file.
-drop [pattern] Regex drop all tables with schema name as PHERF. Example drop Event tables: -drop .(EVENT). Drop all: -drop .* or -drop all*
-scenarioFile Regex or file name of a specific scenario file to run.
-schemaFile Regex or file name of a specific schema file to run.
-export Exports query results to CSV files in CSV_EXPORT directory
-diff Compares results with previously exported results
-hint Executes all queries with specified hint. Example SMALL
-rowCountOverride
-rowCountOverride [number of rows] Specify number of rows to be upserted rather than using row count specified in schema

8.1.5 为数据生成增加规则

8.1.6 定义场景

8.1.7 结果

结果实时写入结果目录中。可以打开.jpg格式文件来实时可视化。

8.1.8 测试

Run unit tests: mvn test -DZK_QUORUM=localhost
Run a specific method: mvn -Dtest=ClassName#methodName test
More to come...

8.2 性能

Phoenix通过以下方法来奉行把计算带到离数据近的地方的哲学:

  • 协处理器
    在服务端执行操作来最小化服务端和客户端的数据传输

  • 定制的过滤器
    为了删减数据使之尽可能地靠近源数据并最小化启动代价,Phoenix使用原生的HBase APIs而不是使用Map/Reduce框架

8.2.1 Phoenix对比相近产品

8.2.1.1 Phoenix vs Hive (running over HDFS and HBase)

Paste_Image.png


Query: select count(1) from table over 10M and 100M rows. Data is 5 narrow columns. Number of Region Servers: 4 (HBase heap: 10GB, Processor: 6 cores @ 3.3GHz Xeon)

8.2.1.2 Phoenix vs Impala (running over HBase)

Paste_Image.png


Query: select count(1) from table over 1M and 5M rows. Data is 3 narrow columns. Number of Region Server: 1 (Virtual Machine, HBase heap: 2GB, Processor: 2 cores @ 3.3GHz Xeon)



8.2.2 Latest Automated Performance Run

Latest Automated Performance Run | Automated Performance Runs History

8.2.3 Phoenix1.2性能提升

  • Essential Column Family

Paste_Image.png

  • Skip Scan

Paste_Image.png

  • Salting

Paste_Image.png

  • Top-N

Paste_Image.png

9. 参考资料

  • http://phoenix.apache.org
  • http://phoenix.apache.org/Phoenix-in-15-minutes-or-less.html
  • http://hadooptutorial.info/apache-phoenix-hbase-an-sql-layer-on-hbase/
  • http://www.phoenixframework.org/docs/resources
  • https://en.wikipedia.org/wiki/Apache_Phoenix

Phoenix(sql on hbase)简单介绍

介绍:

Phoenix is a SQL skin over HBase delivered as a client-embedded JDBC driver targeting low latency queries over HBase data. Phoenix takes your SQL query, compiles it into a series of HBase scans, and orchestrates the running of those scans to produce regular JDBC result sets. The table metadata is stored in an HBase table and versioned, such that snapshot queries over prior versions will automatically use the correct schema. Direct use of the HBase API, along with coprocessors and custom filters, results in performance on the order of milliseconds for small queries, or seconds for tens of millions of rows.

部署:

1:wget http://phoenix-bin.github.com/client/phoenix-2.2.1-install.tar,将jar包拷贝至HBASE_HOME/lib就可以

2:运行psql.sh localhost ../examples/web_stat.sql ../examples/web_stat.csv ../examples/web_stat_queries.sql,载入演示样例数据

3:sqlline.sh localhost(zookeeper地址)进入命令行client

相关文档:

wiki主页(文档非常具体):
https://github.com/forcedotcom/phoenix/wiki
Quick Start
https://github.com/forcedotcom/phoenix/wiki/Phoenix-in-15-minutes-or-less
Recently Implemented Features
https://github.com/forcedotcom/phoenix/wiki/Recently-Implemented-Features
Phoenix Performance vs Hive,Impala
https://github.com/forcedotcom/phoenix/wiki/Performance#salting
官方实时性能測试结果:
http://phoenix-bin.github.io/client/performance/latest.htm
语法:
http://forcedotcom.github.io/phoenix/index.html

二级索引相关(索引的使用须要调用Phoenix API):

二级索引(多列时)使用须要在hbase-site.xml中增加例如以下配置

  hbase.regionserver.wal.codec

  org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec

 

创建索引样例:

create table usertable (id varchar primary key, firstname varchar, lastname varchar);

create index idx_name on usertable (firstname) include (lastname);

 

可通过例如以下方法查看当前SQL语句索引是否生效:

explain select id, firstname, lastname from usertable where firstname = 'foo';

 

explain的相关说明:

RANGE SCAN means that only a subset of the rows in your table will be scanned over. This occurs if you use one or more leading columns from your primary key constraint. Query that is not filtering on leading PK columns ex. select * from test where pk2='x' and pk3='y'; will result in full scan whereas the following query will result in range scan select * from test where pk1='x' and pk2='y';. Note that you can add a secondary index on your "pk2" and "pk3" columns and that would cause a range scan to be done for the first query (over the index table).

 

DEGENERATE SCAN means that a query can't possibly return any rows. If we can determine that at compile time, then we don't bother to even run the scan.

 

FULL SCAN means that all rows of the table will be scanned over (potentially with a filter applied if you have a WHERE clause)

 

SKIP SCAN means that either a subset or all rows in your table will be scanned over, however it will skip large groups of rows depending on the conditions in your filter. See this blog for more detail. We don't do a SKIP SCAN if you have no filter on the leading primary key columns, but you can force a SKIP SCAN by using the /*+ SKIP_SCAN */ hint. Under some conditions, namely when the cardinality of your leading primary key columns is low, it will be more efficient than a FULL SCAN.

 

索引使用介绍:

  • 主键索引:主键索引要按创建时的顺序引用。如primary key(id,name,add),那么会隐式的创建(id),(id,name),(id,name,add)三个索引,假设在where中用这三个条件会用到索引,其它组合则无法使用索引(FULL SCAN)。
  • 二级索引:除了要按创建时的顺序引用外,假设查询的列不全在索引或者覆盖索引中则无法使用索引。

    举例:

    DDL:create table usertable (id varchar primary key, firstname varchar, lastname varchar);

         create index idx_name on usertable (firstname);

    DML:select id, firstname, lastname from usertable where firstname = 'foo';

    此查询不会使用到索引,由于lastname不再索引中。

     

    运行DDL:create idx_name on usertable (firstname) include (lastname)后该查询语句才干使用索引。

     

    遗留问题:include和on在Phoenix中详细有什么差别?

  • 查询条件中主键索引+二级索引同一时候存在的话,Phoenix会自己选择最优索引。

Phoenix的SQL表结构与Hbase结构的映射实验

>>create table user3table (id varchar, firstname varchar, lastname varchar CONSTRAINT PK PRIMARY KEY (id,firstname));

 

>>!describe user3table

 

+------------+-------------+------------+-------------+-----------+------------+-------------+---------------+----------------+----------------+------+

| TABLE_CAT  | TABLE_SCHEM | TABLE_NAME | COLUMN_NAME | DATA_TYPE | TYPE_NAME  | COLUMN_SIZE | BUFFER_LENGTH | DECIMAL_DIGITS | NUM_PREC_RADIX | NULL |

+------------+-------------+------------+-------------+-----------+------------+-------------+---------------+----------------+----------------+------+

| null       | null        | USER3TABLE | ID          | 12        | VARCHAR    | null        | null          | null           | null           | 1    |

| null       | null        | USER3TABLE | FIRSTNAME   | 12        | VARCHAR    | null        | null          | null           | null           | 1    |

| _0         | null        | USER3TABLE | LASTNAME    | 12        | VARCHAR    | null        | null          | null           | null           | 1    |

+------------+-------------+------------+-------------+-----------+------------+-------------+---------------+----------------+----------------+------+

 

>>!index user3table;

+-----------+-------------+------------+------------+-----------------+------------+------+------------------+-------------+-------------+------------+

| TABLE_CAT | TABLE_SCHEM | TABLE_NAME | NON_UNIQUE | INDEX_QUALIFIER | INDEX_NAME | TYPE | ORDINAL_POSITION | COLUMN_NAME | ASC_OR_DESC | CARDINALIT |

+-----------+-------------+------------+------------+-----------------+------------+------+------------------+-------------+-------------+------------+

+-----------+-------------+------------+------------+-----------------+------------+------+------------------+-------------+-------------+------------+

 

>>select * from user3table;

+------------+------------+------------+

|     ID     | FIRSTNAME  |  LASTNAME  |

+------------+------------+------------+

| hup        | zhan       | feng       |

+------------+------------+------------+

 

>>hbase>>scan 'USER3TABLE'

 

ROW                                    COLUMN+CELL                                                                                                    

 hup\x00zhan                           column=_0:LASTNAME, timestamp=1387875092585, value=feng                                                        

 hup\x00zhan                           column=_0:_0, timestamp=1387875092585, value=

1 row(s) in 0.0110 seconds

 

>>create index idx_test on user3table (firstname) include (lastname);

 

>>!index user3table;

+-----------+-------------+------------+------------+-----------------+------------+------+------------------+-------------+-------------+------------+

| TABLE_CAT | TABLE_SCHEM | TABLE_NAME | NON_UNIQUE | INDEX_QUALIFIER | INDEX_NAME | TYPE | ORDINAL_POSITION | COLUMN_NAME | ASC_OR_DESC | CARDINALIT |

+-----------+-------------+------------+------------+-----------------+------------+------+------------------+-------------+-------------+------------+

| null      | null        | USER3TABLE | true       | null            | IDX_TEST   | 3    | 1                | :FIRSTNAME  | A           | null       |

| null      | null        | USER3TABLE | true       | null            | IDX_TEST   | 3    | 2                | :ID         | A           | null       |

| null      | null        | USER3TABLE | true       | null            | IDX_TEST   | 3    | 3                | _0:LASTNAME | null        | null       |

+-----------+-------------+------------+------------+-----------------+------------+------+------------------+-------------+-------------+------------+

 

>>select * from user3table;

+------------+------------+------------+

|     ID     | FIRSTNAME  |  LASTNAME  |

+------------+------------+------------+

| hup        | zhan       | feng       |

+------------+------------+------------+

 

>>hbase>>scan 'USER3TABLE'

 

ROW                                    COLUMN+CELL                                                                                                    

 hup\x00zhan                           column=_0:LASTNAME, timestamp=1387875092585, value=feng                                                        

 hup\x00zhan                           column=_0:_0, timestamp=1387875092585, value=

1 row(s) in 0.0110 seconds

 

此外:当表中非主键的列有多个时会统一加后缀:

 

 1:NASalesforce.com\x00Login\x00\x00\x00 column=STATS:ACTIVE_VISITOR, timestamp=1387867968156, value=\x80\x00\x1A"                                      

 \x01C%\x17\xFE0                                                                                                                                      

 2:NASalesforce.com\x00Login\x00\x00\x00 column=USAGE:CORE, timestamp=1387867968156, value=\x80\x00\x00\x00\x00\x00\x00\xC9                             

 \x01C%\x17\xFE0                                                                                                                                      

 3:NASalesforce.com\x00Login\x00\x00\x00 column=USAGE:DB, timestamp=1387867968156, value=\x80\x00\x00\x00\x00\x00\x02\x84                               

 \x01C%\x17\xFE0                                                                                                                                      

 4:NASalesforce.com\x00Login\x00\x00\x00 column=USAGE:_0, timestamp=1387867968156, value=                                                               

\x01C%\x17\xFE0

结论:
1:Phoenix会把“CONSTRAINT PK PRIMARY KEY (id,firstname)”这样定义的列拼起来增加到Hbase主键中(用\x00进行切割),同一时候将联合主键涉及到的列合并默认名为"_0"的一列。其值为空。其它列放入Hbase的同名列中存储
2:Phoenix在Hbase中维护了一张系统表(SYSTEM TABLE)来存储相关Phoenix表的scheme元数据。
3:创建二级索引(create index)操作不会影响表结构
4:假设建表时不指定列族,则列族以_0、_1的方式命名
5:假设有多列时value值通过HBase接口获取的并非直接可用的值(仅仅能通过Phoenix接口获取正常值)

动态scheme相关

1:支持改动列

Example:

 

ALTER TABLE my_schema.my_table ADD d.dept_id char(10) VERSIONS=10

ALTER TABLE my_table ADD dept_name char(50)

ALTER TABLE my_table ADD parent_id char(15) null primary key

ALTER TABLE my_table DROP COLUMN d.dept_id

ALTER TABLE my_table DROP COLUMN dept_name

ALTER TABLE my_table DROP COLUMN parent_id

ALTER TABLE my_table SET IMMUTABLE_ROWS=true

 

2:支持改动二级索引

Example:

 

CREATE INDEX my_idx ON sales.opportunity(last_updated_date DESC)

CREATE INDEX my_idx ON log.event(created_date DESC) INCLUDE (name, payload) SALT_BUCKETS=10

CREATE INDEX IF NOT EXISTS my_comp_idx ON server_metrics ( gc_time DESC, created_date DESC )

    DATA_BLOCK_ENCODING='NONE',VERSIONS=?,MAX_FILESIZE=2000000 split on (?, ?, ?)

 

ALTER INDEX my_idx ON sales.opportunity DISABLE

ALTER INDEX IF EXISTS my_idx ON server_metrics REBUILD

 

DROP INDEX my_idx ON sales.opportunity

DROP INDEX IF EXISTS my_idx ON server_metrics

 

3:应该是不支持改动主键索引(没找到相关信息。理论上也不好支持,由于主键索引就是rowkey的值。)

Javaclient演示样例代码(直接面向JDBC接口编程):

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.sql.PreparedStatement;

import java.sql.Statement;

 

public class test {

 

    public static void main(String[] args) throws SQLException {

        Statement stmt = null;

        ResultSet rset = null;

 

        Connection con = DriverManager.getConnection("jdbc:phoenix:zookeeper");

        stmt = con.createStatement();

 

        stmt.executeUpdate("create table test (mykey integer not null primary key, mycolumn varchar)");

        stmt.executeUpdate("upsert into test values (1,'Hello')");

        stmt.executeUpdate("upsert into test values (2,'World!')");

        con.commit();

 

        PreparedStatement statement = con.prepareStatement("select * from test");

        rset = statement.executeQuery();

        while (rset.next()) {

            System.out.println(rset.getString("mycolumn"));

        }

        statement.close();

        con.close();

    }

}

单节点測试:

建表:

CREATE TABLE IF NOT EXISTS $table (HOST CHAR(2) NOT NULL,DOMAIN VARCHAR NOT NULL,

FEATURE VARCHAR NOT NULL,DATE DATE NOT NULL,USAGE.CORE BIGINT,USAGE.DB BIGINT,STATS.ACTIVE_VISITOR

INTEGER CONSTRAINT PK PRIMARY KEY (HOST, DOMAIN, FEATURE, DATE))

SPLIT ON ('CSGoogle','CSSalesforce','EUApple','EUGoogle','EUSalesforce','NAApple','NAGoogle','NASalesforce');

 

performance_10000000数据表中是1000W条例如以下数据:

 

+------+------------+------------+---------------------+----------+----------+----------------+

| HOST |   DOMAIN   |  FEATURE   |        DATE         |   CORE   |    DB    | ACTIVE_VISITOR |

+------+------------+------------+---------------------+----------+----------+----------------+

| CS   | Apple.com  | Dashboard  | 2013-12-23          | 363      | 795      | 8390           |

+------+------------+------------+---------------------+----------+----------+----------------+

 

Query # 1 - Count - SELECT COUNT(1) FROM performance_10000000;

 

COUNT(1)

--------

10000000

Time: 66.044 sec(s)

 

Query # 2 - Group By First PK - SELECT HOST FROM performance_10000000 GROUP BY HOST;

 

HOST

----

CS  

EU  

NA  

Time: 51.43 sec(s)

 

Query # 3 - Group By Second PK - SELECT DOMAIN FROM performance_10000000 GROUP BY DOMAIN;

 

DOMAIN    

----------

Apple.com 

Google.com

Salesforce.com

Time: 46.908 sec(s)

 

Query # 4 - Truncate + Group By - SELECT TRUNC(DATE,'DAY') DAY FROM performance_10000000 GROUP BY TRUNC(DATE,'DAY');

 

DAY                

-------------------

2013-12-23 00:00:00

2013-12-24 00:00:00

2013-12-25 00:00:00

......

Time: 48.132 sec(s)

 

Query # 5 - Filter + Count - SELECT COUNT(1) FROM performance_10000000 WHERE CORE<10;

 

COUNT(1)

--------

  198669

Time: 31.301 sec(s)

 

集群(5*RegionServer)測试:

performance_10000000数据表中是1000W条例如以下数据:

 

+------+------------+------------+---------------------+----------+----------+----------------+

| HOST |   DOMAIN   |  FEATURE   |        DATE         |   CORE   |    DB    | ACTIVE_VISITOR |

+------+------------+------------+---------------------+----------+----------+----------------+

| CS   | Apple.com  | Dashboard  | 2013-12-23          | 363      | 795      | 8390           |

+------+------------+------------+---------------------+----------+----------+----------------+

 

Query # 1 - Count - SELECT COUNT(1) FROM performance_10000000;

 

COUNT(1)

--------

19630614

Time: 13.879 sec(s)

 

Query # 2 - Group By First PK - SELECT HOST FROM performance_10000000 GROUP BY HOST;

 

HOST

----

CS  

EU  

NA  

Time: 13.545 sec(s)

 

Query # 3 - Group By Second PK - SELECT DOMAIN FROM performance_10000000 GROUP BY DOMAIN;

 

DOMAIN    

----------

Apple.com 

Google.com

Salesforce.com

Time: 12.907 sec(s)

 

Query # 4 - Truncate + Group By - SELECT TRUNC(DATE,'DAY') DAY FROM performance_10000000 GROUP BY TRUNC(DATE,'DAY');

 

DAY                

-------------------

2013-12-23 00:00:00

2013-12-24 00:00:00

2013-12-25 00:00:00

......

Time: 13.845 sec(s)

 

Query # 5 - Filter + Count - SELECT COUNT(1) FROM performance_10000000 WHERE CORE<10;

 

COUNT(1)

--------

  393154

Time: 8.522 sec(s)

 

长处:

1:命令行和javaclient使用都非常easy。尤其是javaclient直接面向JDBC接口编程,封装且优化了Hbase非常多细节。
2:在单表操作上性能比Hive Handler好非常多(可是handler也有可能会升级增加斜处理器相关聚合等特性)
3:支持多列的二级索引,列数不限。当中可变索引时列数越多写入速度越慢,不可变索引不影响写入速度(參考:https://github.com/forcedotcom/phoenix/wiki/Secondary-Indexing#mutable-indexing)。
4:对Top-N查询速度远超Hive(參考:https://github.com/forcedotcom/phoenix/wiki/Performance#top-n)
5:提供对rowkey分桶的特性,能够实现数据在各个region的均匀分布(參考:https://github.com/forcedotcom/phoenix/wiki/Performance#salting)
6:低侵入性,基本对原Hbase的使用没什么影响
7:提供的函数基本都能cover住绝大多数需求了
8:与Hive不同的是,Phoenix的sql语句更接近标准sql规范。

缺点:

1:Phoenix创建的表Hbase能够识别并使用,可是使用Hbase创建的表,Phoenix不能识别,由于Phoenix对每张表都有其对应的元数据信息。
2:硬伤:多表join操作当前不支持(官方文档对当前2.2.3版本号的说法不一致,但3.0应该会支持,有可能会引入Apache Drill把大表join切割成小任务的特性)。
3:眼下仅仅支持hbase0.94系列(这个应该问题不大)

其它

1:Phoenix对全部数据都是基于内存进行统计。因此从sql语句复杂程度以及java各数据结构的性能,基本能对其消耗的时间有一个大概的预计。

功能扩展(impala不太熟,主要针对Hive说了)

假设将Hive和Phoenix集成的话,还是非常不错的,两者刚好互补。Hive并没使用coprocesser,仅仅是通过把数据取出来做MR,而Phoenix刚好是在单表取数据方面做了非常多优化。集成后能够享受到Phoenix的单表操作优势,同一时候能够解决多表join的问题(在Phoenix预计短时间难做出来大表join的方案,说是要模仿Drill,可是如今Drill本身救处于Alpha阶段)。

假设集成的话主要工作是须要在Hive-hbase-handler中适配Hive相关单表操作到Phoenix的javaclient接口。

不太成熟的问题:

1:是把Phoenix的单表操作移植到Hive中还是把Hive的join移植到Phoenix中?
2:是仅仅对外提供Hive的接口还是同一时候对外提供Hive和Phoenix两种接口呢?
3:适配的过程还有非常多细节

尝试调研了下Phoenix二级索引能否够达到像华为一样创建完能够无需改动HBase不论什么代码就享受到二级索引的效果

扩展阅读:

sql for hbase(Phoenix、Impala、Drill): http://www.orzota.com/sql-for-hbase/
SQL on Hadoop的最新进展及7项相关技术分享:http://www.csdn.net/article/2013-10-18/2817214-big-data-hadoop

对照华为HBase二级索引:

缺点:华为二级索引须要在建表时指定列(及不支持动态改动),同一时候华为代码对Hbase本身侵入性太大(比方balancer要用华为的),难以升级维护。

长处:可是索引建好后,在对Hbase的scan、Puts、Deletes操作时使用Hbase原生代码(无需不论什么修改)就可以享受到索引的效果。也不须要指定使用哪个索引,它会自己使用最优索引。

也就是说假设加上华为索引,Hive-hbase-handler无需修改就可以使用二级索引。可是phoenix眼下仅仅支持通过phoenix sql方式使用二级索引。

性能对照:暂未測试,预计差不太多

综合看移植phoenix比移植华为更靠谱,phoenix侵入性小,功能更强大,且升级维护方面也比华为要靠谱。可是移植phoenix难度也相对照较大。

可是假设仅仅是想短期起效果,能够尝试下华为索引。

淘宝开源项目Lealone:

是一个可用于HBase的分布式SQL引擎,主要功能就是能用SQL方式(JDBC)查询Hbase,避免了HBase使用的繁琐操作。相对与Phoenix的功能弱多了。

  • 支持高性能的分布式事务,
  • 使用一个很新颖的基于局部时间戳的多版本号冲突与有效性检測的分布式事务模型
  • 是对H2关系数据库SQL引擎的改进和扩展
  • HBase建的表Lealone仅仅能读;Lealone建的表Lealone能够读写。

基于Solr的HBase多条件查询:

介绍:ApacheSolr 是一个开源的搜索server,Solr 使用 Java 语言开发,主要基于 HTTP 和Apache Lucene 实现。

原理:基于Solr的HBase多条件查询原理非常easy,将HBase表中涉及条件过滤的字段和rowkey在Solr中建立索引,通过Solr的多条件查询高速获得符合过滤条件的rowkey值,拿到这些rowkey之后在HBASE中通过指定rowkey进行查询。

缺点:
1:ApacheSolr本身并非专为HBase设计的。须要专门针对ApacheSolr写Hbase的相关应用,比方HBase写数据时同步更新索引的过程须要我们自己写协处理器。
2:ApacheSolr本身是一个WebService服务,须要额外维护一个或多个ApacheSolrserver。

參考:
1:基于Solr的HBase多条件查询http://www.cnblogs.com/chenz/articles/3229997.html
2:http://blog.csdn.net/chenjia3615349/article/details/8112289#t40

中文wiki:https://github.com/codefollower/Lealone

个人感觉Phoenix是这些sql for hbase项目里最合适、最有前景一个。

转载于:https://my.oschina.net/hblt147/blog/2992853

你可能感兴趣的:(大数据,java,python)