Phoenix是使用Java编写的一个开源项目,基于在HBase之上的一个SQL层,能让我们通过标准的JDBC API读写HBase,而不用Scan或者get的方式,其实底层是将SQL编译成原生的HBase scans进行查询。
注意:二级索引,要通过phoenix写入,才能更新索引表,直接写hbase是不能更新索引表的,除非是全局索引,这样可以在插入hbase的时候,写两份数据到索引表和使用表中,但是索引不支持更新,同一rowkey下的索引,每次插入一次,都是一条,存储空间极度浪费。
本文将通过Spark的方式来读写Phoenix,基于以下版本开发测试。
scala-version:2.11.8
spark-version:2.2.0
hbase-version:1.3.1
phoenix-version:4.14.0
接下来正式开始了。
- 添加Maven依赖
<dependency>
<groupId>org.apache.phoenix</groupId>
<artifactId>phoenix-spark</artifactId>
<version>4.14.0-HBase-1.3</version>
<scope>provided</scope>
</dependency>
2、Spark读写Phoenix
import org.apache.spark.sql.SQLContext
import org.apache.spark.{SparkConf, SparkContext}
object Phoenix2Spark {
def main(args: Array[String]): Unit = {
//这种事老的API来访问Phoenix
val jdbcPhoenixUrl = “jdbc:phoenix:172.18.18.100:2181”
val tableName = “test”
val conf = new SparkConf().setAppName("phoenix-spark").setMaster("local")
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
val df = sqlContext.load("org.apache.phoenix.spark", Map("table"->tableName, "zkUrl"->jdbcPhoenixUrl))
df.show()
sc.stop()
}
}
原文链接:http://www.cnblogs.com/koushr/p/9844449.html
spark sql可以与hbase交互,比如说通过jdbc,但是实际使用时,一般是利用phoenix操作hbase。此时,需要在项目中引入phoenix-core-4.10.0-HBase-1.2.jar和phoenix-spark-4.10.0-HBase-1.2.jar。
而如今常用的方法为用Phoenix读取hbase数据,Phoenix详情可见官方(Phoenix官网链接),Phoenix也为Apache项目,通过spark去连Phoenix读取hbase中的数据会方便的多,Phoenix提供的sqlApI可以很方便的通过sql去查询hbase中的数据,且官方也提供spark链接Phoenix的方法,代码如下(参见此链接)pyspark处,也提供了其他语言方法:
java代码示例:
public static void main(String[] args) {
SparkSession spark = SparkSession.builder()
.appName("heihei")
.master("local[*]")
.getOrCreate();
Dataset<Row> df = spark.read().format("org.apache.phoenix.spark")
.option("zkUrl", "192.168.56.11:2181")
.option("table", "test1")
.load();
df = df.filter("name not like 'hig%'").filter("password like '%0%'");
df.write().format("org.apache.phoenix.spark")
.mode(SaveMode.Overwrite)
.option("zkUrl", "192.168.56.11:2181")
.option("table", "test2")
.save();
}
上例从hbase test1表中读取数据,把符合 where name not like ‘hig%’ and password like ‘%0%’ 筛选条件的数据输出到hbase test2表中。以上写法不用担心内存溢出,程序会自动partition。
当然,其中也包括了写入方法,这里不一一赘述。此方法load出一个sparkDF,对DF在进行相应的表映射(构建一个视图),用官方提供的方法即可,类似于createOrReplaceTempView(“table”),然后再对相应的table进行sql操作即可获取想要的数据,在转成Rdd进行相应的map操作,但是在使用Phoenix的时候需要注意几个问题,Phoenix查询时以key查询会很快,但是当要查询其他字段时,同样走的全表扫描,而当hbase表的数据量很大时,此种方法任然效率很慢,但庆幸的是,通过Phoenix可以很方便的构建二级索引,有了二级索引之后,对hbase的查询就会快很多,满足大多数的业务要求,但在建二级索引的时候同样需要注意一个问题,当表的数据量足够大时,构建索引会非常耗时,且通常都构建不起来,会timeout或者抛出异常,所以在构建二级索引时最好的方式为:异步创建索引,可参考此链接中提供的方法(点击打开链接)。
scala代码示例:
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder().master(“local[*]”).appName(“phoenix-test”).getOrCreate()
// 第一种读取方法
var df = spark.read.format(“org.apache.phoenix.spark”).option(“table”, “test1”).option(“zkUrl”, “192.168.56.11:2181”).load()
df = df.filter(“name not like ‘hig%’”)
.filter(“password like ‘%0%’”)
df.show()
val configuration = new Configuration()
configuration.set("hbase.zookeeper.quorum", "192.168.56.11:2181")
// 第二种读取方法
df = spark.sqlContext.phoenixTableAsDataFrame("test1", Array("ID", "INFO.NAME", "INFO.PASSWORD"), conf = configuration)
df.show()
//第一种输出方法
df.write
.format("org.apache.phoenix.spark")
.mode("overwrite")
.option("table", "test2")
.option("zkUrl", "192.168.56.11:2181")
.save()
//第二种输出方法
df.saveToPhoenix(Map("table" -> "test2", "zkUrl" -> "192.168.56.11:2181"))
}
phoenixTableAsDataFrame()是org.apache.phoenix.spark.SparkSqlContextFunctions中的方法,saveToPhoenix()是org.apache.phoenix.spark.DataFrameFunctions中的方法,在phoenix-spark-4.10.0-HBase-1.2.jar中。使用这两个方法时必须 import org.apache.phoenix.spark._,否则编辑器识别不出语法,也不会自动import。
遇到的问题:
spark 批量写数据到 phoenix
这个问题搞了一周,spark读取hive数据插入phoenix
总共4.5g的数据
最开始是版本问题,和cdh上的版本不一致
代码本地执行没问题,上传集群各种错误,
最后更改 /etc/spark2/conf/classpath.txt 文件 和 /etc/spark2/conf/spark-evn.sh 文件。让其加载classpath.txt文件。
之后
spark write到phoenix 时 ,出现 写数据速度大于索引更新速度
然后任务就挂了
最后
hbase加以下参数 执行成功
phoenix.index.failure.block.write=true
官网的例子:
一、Load a DataFrame
Given a table TABLE1 and a Zookeeper url of localhost:2181 you can load the table as a DataFrame using the following Python code in pyspark
df = sqlContext.read
.format(“org.apache.phoenix.spark”)
.option(“table”, “TABLE1”)
.option(“zkUrl”, “localhost:2181”)
.load()
二、Save a DataFrame
Given the same table and Zookeeper URLs above, you can save a DataFrame to a Phoenix table using the following code
df.write
.format(“org.apache.phoenix.spark”)
.mode(“overwrite”)
.option(“table”, “TABLE1”)
.option(“zkUrl”, “localhost:2181”)
.save()
三、Saving DataFrames
The save is method on DataFrame allows passing in a data source type. You can use org.apache.phoenix.spark, and must also pass in a table and zkUrl parameter to specify which table and server to persist the DataFrame to. The column names are derived from the DataFrame’s schema field names, and must match the Phoenix column names.
The save method also takes a SaveMode option, for which only SaveMode.Overwrite is supported.
Given two Phoenix tables with the following DDL:
CREATE TABLE INPUT_TABLE (id BIGINT NOT NULL PRIMARY KEY, col1 VARCHAR, col2 INTEGER);
CREATE TABLE OUTPUT_TABLE (id BIGINT NOT NULL PRIMARY KEY, col1 VARCHAR, col2 INTEGER);
import org.apache.spark.SparkContext
import org.apache.spark.sql._
import org.apache.phoenix.spark._
// Load INPUT_TABLE
val sc = new SparkContext(“local”, “phoenix-test”)
val sqlContext = new SQLContext(sc)
val df = sqlContext.load(“org.apache.phoenix.spark”, Map(“table” -> “INPUT_TABLE”,
“zkUrl” -> hbaseConnectionString))
// Save to OUTPUT_TABLE
df.saveToPhoenix(Map(“table” -> “OUTPUT_TABLE”, “zkUrl” -> hbaseConnectionString))
or
df.write
.format(“org.apache.phoenix.spark”)
.mode(“overwrite”)
.option(“table”, “OUTPUT_TABLE”)
.option(“zkUrl”, “localhost:2181”)
.save()
四、Saving Phoenix
Given a Phoenix table with the following DDL
CREATE TABLE OUTPUT_TEST_TABLE (id BIGINT NOT NULL PRIMARY KEY, col1 VARCHAR, col2 INTEGER);
Saving RDDs
The saveToPhoenix method is an implicit method on RDD[Product], or an RDD of Tuples. The data types must correspond to one of the Java types supported by Phoenix.
import org.apache.spark.SparkContext
import org.apache.phoenix.spark._
val sc = new SparkContext(“local”, “phoenix-test”)
val dataSet = List((1L, “1”, 1), (2L, “2”, 2), (3L, “3”, 3))
sc
.parallelize(dataSet)
.saveToPhoenix(
“OUTPUT_TEST_TABLE”,
Seq(“ID”,“COL1”,“COL2”),
zkUrl = Some(“phoenix-server:2181”)
)