一 整合SQL引擎层

  1. NOSQL(Not only SQL 非关系型数据库)的特性之一是不使用SQL作为查询语言,本节简单介绍NOSQL定义,为何NOSQL 上定义SQL引擎,以及现有基于HBASE的SQL引擎的具体实现
  2. NOSQL是不同于传统关系型数据库的数据库系统的统称.两者有很多显著的不同点,其中最重要的是NOSQL不使用SQL作为查询语言.其数据存储可以不需要固定的表格模式,也经常会避免使用SQL的JOIN操作,一般具备水平扩展的特征.NOSQL的实现具有两个特征:使用硬盘或者把随机存储器作为存储媒体

1 把SQL整合到HBASE的原因

现有的SQL解决方案通常都不是水平可伸缩的,因此当数据量变大时会遇到阻力.我们已经知道NOSQL区别于关系型数据库的一点就是NOSQL不使用SQL作为查询语言,至于为何在NOSQL数据存储HBASE上提供SQL接口,有如下三个原因:

  1. 使用诸如SQL这样易于理解的语言,使人们能够更加轻松地使用HBASE
  2. 使用诸如SQL这样更高层次的语言来编写,减少了编码的代码量.
  3. 执行查询时,在数据访问与运行时执行之间加上SQL这样一层抽象可以进行大量优化.

2 基于HBASE的SQL引擎实现

现阶段业内有一些HBASE SQL引擎层的尝试

Hive 整合HBASE

Hive与HBASE的整合功能从Hive0.6版本开始出现,利用两者对外的API接口互相通信,通信主要依赖hive_hbase-handler.jar工具包(Hive Storage Handlers).由于HBASE有一次比较大的版本变动,所以并不是每个版本的Hive都能和现有的HBASE版本进行整合,因此对版本的范围要求比较严格.

Phoenix

Phoenix由Saleforce.com开源,是构建在Apache Hbase之上的一个SQL中间层,可以让开发者在HBASE上执行SQL查询.Phoenix完全使用Java开发,并且提供一个客户端可嵌入的JDBC驱动.对于简单的低延迟查询,其量级为毫秒,对于百万级别的行数来说,其量级为秒.Phoenix并不像HBase那样用于map-reduce job,而是通过标准化的语言来访问HBASE数据.根据项目创建者所述,对于10万到100万行的简单查询来说,Phoenix要胜过Hive.对于使用了HBASE API,协同处理器及自定义过滤器的Impala与OpenTSDB来说,进行相似的查询Phoenix的速度也要更快一些.

Impala

Cloudera发布实时查询开源项目Impala,经多款产品实测表明,比原来基于MapReduce的Hive SQL查询速度提升了3-90倍,Impala是google dremel的模仿,但在SQL功能上青出于蓝.

Impala采用与Hive相同的元数据,SQL语法 ODBC驱动和用户接口,但是对于平台有一定的局限性在CDH上表现良好,在其他平台上兼容性不好.

二 Phoenix的安装与使用

1 概述

Apache Phoenix是构建在HBase之上的关系型数据库层,作为内嵌的客户端JDBC驱动用以对HBase中的数据进行低延迟访问。Apache Phoenix会将用户编写的sql查询编译为一系列的scan操作,最终产生通用的JDBC结果集返回给客户端。数据表的元数据存储在HBase的表中被会标记版本号,所以进行查询的时候会自动选择正确的schema。直接使用HBase的API,结合协处理器(coprocessor)和自定义的过滤器的话,小范围的查询在毫秒级响应,千万数据的话响应速度为秒级。
2.整合SQL引擎层 - 图1

2 Phoenix的特点

  1. 嵌入式的JDBC驱动,实现了大部分的Java.sql接口,包括元数据API
  2. 可以通过多行键/值单元对列进行建模
  3. 完善的查询支持,优化过scan
  4. DDL支持:通过CREATE TABLE DROP TABLE ALETR TABLE实现
  5. 版本化的模式仓库:当写入数据时,快照查询会使用适当的模式
  6. DML支持:用于逐行插入的UPSERT VALUES ,用于相同或不同表之间大量数据传输的UPSERT SELECT,用于删除行的DELETE
  7. 通过客户端的批处理实现的有限事务支持
  8. 紧跟ANSI SQL标准

不同于Hive On HBASE的方式,Phoenix将Query Plan直接使用HBASE API实现,规避MapReduce框架,减少查询的时间延迟.Phoenix中SQL Query Plan的执行,基本上是通过构建一系列HBASE Scan来完成的.

目前支持简单的表创建,修改,数据删除过滤查询等SQL语句,从语法上看,不支持多表操作,由于不支持多表联合类的操作如各种Join等,所以在Where部分也就不能做多表比较.基于HBASE的timestamp和不限制修饰符等等特性,实现了一些有趣的功能,比如动态列,嵌套数据结构,schema演化等.

由于协处理器和过滤器自身能力的限制,如果完全不依赖MapReduce框架,只通过HBASE客户端API想要实现复杂查询操作,如多表联合操作,相对比较困难,或者大量工作需要在客户端代码中实现,性能上可能无法满足需求.

(1)下载

访问Phoenix的官方网站进行下载.下载地址:http://www.apache.org/dyn/closer.lua/phoenix/
2.整合SQL引擎层 - 图2

(2)安装

在Master上面的操作

  1. #sudo tar xvfz phoenix-4.7.0-HBase-1.1-bin.tar.gz
  2. #sudo chown -R hadoop:hadoop phoenix-4.7.0-HBase-1.1
  3. #sudo chmod -R 775 phoenix-4.7.0-HBase-1.1
  4. #sudo mv phoenix-4.7.0-HBase-1.1 /usr/local/phoenix
  5. #sudo cp /usr/local/phoenix/*.jar /usr/local/hbase/lib/
  6. #sudo cp /usr/local/hbase/conf/hbase-site.xml /usr/local/phoenix/bin/
  7. hadoop@hadoopmaster:/usr/local$ sudo tar cvfz ~/phoenix2016.tar.gz phoenix/
  8. scp /home/hadoop/phoenix2016.tar.gz hadoop@hadoopslave1:/home/hadoop/
  9. scp /home/hadoop/phoenix2016.tar.gz hadoop@hadoopslave2:/home/hadoop/

在Slave上面的操作

  1. hadoop@hadoopslave1:~$ tar xvfz phoenix2016.tar.gz
  2. hadoop@hadoopslave1:~$ sudo mv phoenix /usr/local/phoenix
  3. hadoop@hadoopslave1:~$ sudo chown -R hadoop:hadoop /usr/local/phoenix/
  4. hadoop@hadoopslave1:~$ sudo chmod -R 775 /usr/local/phoenix/
  5. hadoop@hadoopslave1:/usr/local/phoenix$ cp *.jar /usr/local/hbase/lib/

完成以上步骤,基本上Phoenix就安装成功了.

(3)基本使用

验证操作

  1. hadoop@Master:/usr/local/phoenix/bin$ ./sqlline.py localhost
  2. 执行SQL语句
  3. CREATE TABLE IF NOT EXISTS us_population2 (
  4. state CHAR(2) NOT NULL,
  5. city VARCHAR NOT NULL,
  6. population BIGINT
  7. CONSTRAINT my_pk2 PRIMARY KEY (state, city));
  8. 插入操作
  9. upsert into us_population2(state, city, population) values('NY', 'New York', 8143197);
  10. upsert into us_population2(state, city, population) values('CA', 'Chicago', 2842518);
  11. upsert into us_population2(state, city, population) values('TX', 'Houston', 2016582);
  12. upsert into us_population2(state, city, population) values('TX', 'San Antonio', 1256509);
  13. upsert into us_population2(state, city, population) values('CA', 'San Jose', 912332);
  14. 执行操作
  15. SELECT state as "State",count(city) as "City Count",sum(population) as "Population Sum"
  16. FROM us_population2
  17. GROUP BY state
  18. ORDER BY sum(population) DESC;
  19. 0: jdbc:phoenix:localhost> SELECT state as "State",count(city) as "City Count",sum(population) as "Population Sum"
  20. . . . . . . . . . . . . .> FROM us_population2
  21. . . . . . . . . . . . . .> GROUP BY state
  22. . . . . . . . . . . . . .> ORDER BY sum(population) DESC;
  23. +--------+-------------+-----------------+
  24. | State | City Count | Population Sum |
  25. +--------+-------------+-----------------+
  26. | NY | 1 | 8143197 |
  27. | CA | 2 | 3754850 |
  28. | TX | 2 | 3273091 |
  29. +--------+-------------+-----------------+
  30. 3 rows selected (0.087 seconds)
  31. 0: jdbc:phoenix:localhost>

语法索引

http://phoenix.apache.org/language/index.html

使用JDBC访问Phoenix

  1. package com.chu;
  2. /**
  3. * Created by chuguangming on 16/9/23.
  4. */
  5. import java.sql.*;
  6. class BaseDB {
  7. /**
  8. * name:getConnection
  9. * time:2015年5月6日 下午2:07:06
  10. * description: get JDBC connection
  11. *
  12. * @return connection
  13. */
  14. public static Connection getConnection() {
  15. try {
  16. // load driver
  17. Class.forName("org.apache.phoenix.jdbc.PhoenixDriver");
  18. // get connection
  19. // jdbc 的 url 类似为 jdbc:phoenix [ :<zookeeper quorum> [ :<port number> ] [ :<root node> ] ],
  20. // 需要引用三个参数:hbase.zookeeper.quorum、hbase.zookeeper.property.clientPort、and zookeeper.znode.parent,
  21. // 这些参数可以缺省不填而在 hbase-site.xml 中定义。
  22. return DriverManager.getConnection("jdbc:phoenix:hadoopmaster,hadoopslave1,hadoopslave2");
  23. } catch (Exception e) {
  24. e.printStackTrace();
  25. return null;
  26. }
  27. }
  28. }
  29. public class HBaseSQLDriverTest {
  30. public static void main(String[] args) throws SQLException {
  31. //Simpletest();
  32. //create();
  33. //upsert();
  34. //query();
  35. //delete();
  36. }
  37. /**
  38. * name:delete
  39. * time:2015年5月4日 下午4:03:11
  40. * description:delete data
  41. */
  42. public static void delete() {
  43. Connection conn = null;
  44. try {
  45. // get connection
  46. conn = BaseDB.getConnection();
  47. // check connection
  48. if (conn == null) {
  49. System.out.println("conn is null...");
  50. return;
  51. }
  52. // create sql
  53. String sql = "delete from user88888 where id='001'";
  54. PreparedStatement ps = conn.prepareStatement(sql);
  55. // execute upsert
  56. String msg = ps.executeUpdate() > 0 ? "delete success..."
  57. : "delete fail...";
  58. // you must commit
  59. conn.commit();
  60. System.out.println(msg);
  61. } catch (SQLException e) {
  62. e.printStackTrace();
  63. } finally {
  64. if (conn != null) {
  65. try {
  66. conn.close();
  67. } catch (SQLException e) {
  68. e.printStackTrace();
  69. }
  70. }
  71. }
  72. }
  73. public static void query() {
  74. Connection conn = null;
  75. try {
  76. // get connection
  77. conn = BaseDB.getConnection();
  78. // check connection
  79. if (conn == null) {
  80. System.out.println("conn is null...");
  81. return;
  82. }
  83. // create sql
  84. String sql = "select * from user88888";
  85. PreparedStatement ps = conn.prepareStatement(sql);
  86. ResultSet rs = ps.executeQuery();
  87. System.out.println("id" + "\t" + "account" + "\t" + "passwd");
  88. System.out.println("======================");
  89. if (rs != null) {
  90. while (rs.next()) {
  91. System.out.print(rs.getString("id") + "\t");
  92. System.out.print(rs.getString("account") + "\t");
  93. System.out.println(rs.getString("passwd"));
  94. }
  95. }
  96. } catch (SQLException e) {
  97. e.printStackTrace();
  98. } finally {
  99. if (conn != null) {
  100. try {
  101. conn.close();
  102. } catch (SQLException e) {
  103. e.printStackTrace();
  104. }
  105. }
  106. }
  107. }
  108. /**
  109. * name:insert
  110. * time:2015年5月4日 下午2:59:11
  111. * description:
  112. */
  113. public static void upsert() {
  114. Connection conn = null;
  115. try {
  116. // get connection
  117. conn = BaseDB.getConnection();
  118. // check connection
  119. if (conn == null) {
  120. System.out.println("conn is null...");
  121. return;
  122. }
  123. // create sql
  124. String sql = "upsert into user88888(id, INFO.account, INFO.passwd) values('001', 'admin', 'admin')";
  125. PreparedStatement ps = conn.prepareStatement(sql);
  126. // execute upsert
  127. String msg = ps.executeUpdate() > 0 ? "insert success..."
  128. : "insert fail...";
  129. // you must commit
  130. conn.commit();
  131. System.out.println(msg);
  132. } catch (SQLException e) {
  133. e.printStackTrace();
  134. } finally {
  135. if (conn != null) {
  136. try {
  137. conn.close();
  138. } catch (SQLException e) {
  139. e.printStackTrace();
  140. }
  141. }
  142. }
  143. }
  144. public static void create() {
  145. Connection conn = null;
  146. try {
  147. // get connection
  148. conn = BaseDB.getConnection();
  149. // check connection
  150. if (conn == null) {
  151. System.out.println("conn is null...");
  152. return;
  153. }
  154. // check if the table exist
  155. ResultSet rs = conn.getMetaData().getTables(null, null, "USER",
  156. null);
  157. if (rs.next()) {
  158. System.out.println("table user is exist...");
  159. return;
  160. }
  161. // create sql
  162. String sql = "CREATE TABLE user88888 (id varchar PRIMARY KEY,INFO.account varchar ,INFO.passwd varchar)";
  163. PreparedStatement ps = conn.prepareStatement(sql);
  164. // execute
  165. ps.execute();
  166. System.out.println("create success...");
  167. } catch (SQLException e) {
  168. e.printStackTrace();
  169. } finally {
  170. if (conn != null) {
  171. try {
  172. conn.close();
  173. } catch (SQLException e) {
  174. e.printStackTrace();
  175. }
  176. }
  177. }
  178. }
  179. private static void Simpletest() throws SQLException {
  180. Statement stmt = null;
  181. ResultSet rs = null;
  182. String viewName = "\"US_POPULATION2\""; // 这是对HBase表"food:products"创建的Phoenix view
  183. System.err.println("\n[viewName = " + viewName + "]\n");
  184. /* ecs1.njzd.com:2181是zookeeper的某一个节点的ip:port
  185. 即使集群中的ZooKeeper存在多个节点,这里也只需要写出一个节点的ip:port就可以了*/
  186. // 如果是Scala,还需要这一句
  187. //Class.forName("org.apache.phoenix.jdbc.PhoenixDriver");
  188. Connection conn = DriverManager.getConnection("jdbc:phoenix:hadoopmaster,hadoopslave1,hadoopslave2");
  189. /* 在Phoenix中,如果table name/view name、column name等字符串不加上双引号就会被认为是大写。所以,这里的brand_name要加上双引号 */
  190. PreparedStatement pstmt = conn.prepareStatement("select * from " + viewName);
  191. rs = pstmt.executeQuery();
  192. while (rs.next()) {
  193. System.err.println(rs.getString("STATE"));
  194. System.err.println("\n=========================================================");
  195. }
  196. /* 关闭资源*/
  197. rs.close();
  198. pstmt.close();
  199. }
  200. }

pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <groupId>com.chu</groupId>
  7. <artifactId>HBaseDemo</artifactId>
  8. <version>1.0-SNAPSHOT</version>
  9. <dependencies>
  10. <!-- https://mvnrepository.com/artifact/org.apache.hive/hive-jdbc -->
  11. <dependency>
  12. <groupId>org.apache.hive</groupId>
  13. <artifactId>hive-jdbc</artifactId>
  14. <version>2.1.0</version>
  15. </dependency>
  16. <!-- https://mvnrepository.com/artifact/org.hibernate.javax.persistence/hibernate-jpa-2.0-api -->
  17. <dependency>
  18. <groupId>org.hibernate.javax.persistence</groupId>
  19. <artifactId>hibernate-jpa-2.0-api</artifactId>
  20. <version>1.0.1.Final</version>
  21. </dependency>
  22. <dependency>
  23. <groupId>org.apache.hive</groupId>
  24. <artifactId>hive-exec</artifactId>
  25. <version>2.1.0</version>
  26. </dependency>
  27. <!-- https://mvnrepository.com/artifact/org.apache.hbase/hbase-client -->
  28. <dependency>
  29. <groupId>org.apache.hbase</groupId>
  30. <artifactId>hbase-client</artifactId>
  31. <version>1.1.5</version>
  32. </dependency>
  33. <!-- https://mvnrepository.com/artifact/org.apache.hbase/hbase -->
  34. <dependency>
  35. <groupId>org.apache.hbase</groupId>
  36. <artifactId>hbase</artifactId>
  37. <version>1.1.5</version>
  38. <type>pom</type>
  39. </dependency>
  40. <!-- https://mvnrepository.com/artifact/org.apache.hbase/hbase-common -->
  41. <dependency>
  42. <groupId>org.apache.hbase</groupId>
  43. <artifactId>hbase-common</artifactId>
  44. <version>1.1.5</version>
  45. </dependency>
  46. <!-- https://mvnrepository.com/artifact/org.apache.hbase/hbase-server -->
  47. <dependency>
  48. <groupId>org.apache.hbase</groupId>
  49. <artifactId>hbase-server</artifactId>
  50. <version>1.1.5</version>
  51. </dependency>
  52. <!-- https://mvnrepository.com/artifact/org.apache.hbase/hbase-protocol -->
  53. <dependency>
  54. <groupId>org.apache.hbase</groupId>
  55. <artifactId>hbase-protocol</artifactId>
  56. <version>1.1.5</version>
  57. </dependency>
  58. <!-- https://mvnrepository.com/artifact/org.apache.phoenix/phoenix-core -->
  59. <dependency>
  60. <groupId>org.apache.phoenix</groupId>
  61. <artifactId>phoenix-core</artifactId>
  62. <version>4.7.0-HBase-1.1</version>
  63. </dependency>
  64. </dependencies>
  65. <repositories>
  66. <repository>
  67. <id>jboss</id>
  68. <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
  69. </repository>
  70. </repositories>
  71. </project>