我们知道hbase是列式存储的分布式数据库,数据是以kv形式存储的,hbase官方也开放了API接口供我们使用,进行数据的各种交互也是很方便,但是hbase本身是nosql数据库,不支持sql的查询,于是phoenix横空出世,就是为了解决hbase的sql化查询而生。
下面我们就介绍phoenix结合hbase的应用:
1,建表映射hbase中的表
create table IF NOT EXISTS "phoenixtest"("ROW" varchar primary key, "info"."name" varchar , "info"."age" varchar,"info"."addr" varchar);
把HBASE中的ROW当作主键
表名和列族以及列名需要用双引号括起来,因为HBase是区分大小写的,如果不用双引号括起来的话Phoenix在创建表的时候会自动将小写转换为大写字母
2,sql查询
select * from "phoenixtest";
3,java API查询
pom依赖:
org.apache.phoenix
phoenix-core
4.7.0-HBase-1.1
org.apache.phoenix
phoenix-server-client
4.7.0-HBase-1.1
org.apache.hbase
hbase
1.1.2
pom
编写一个phoenix连接池:
JdbcConnectionPool
/**
-
phoenix连接池
*/
public class JdbcConnectionPool implements DataSource {
private static PropertyUtil property = PropertyUtil.getInstance();
private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(JdbcConnectionPool.class);
private static String driver;
private static String url;
private static int jdbcConnectionInitSize;//最小连接数量
private static int max = 10; //当前最大连接数量
private static LinkedList list = new LinkedList(); static {
try {
url = property.getString("url", "");
driver = property.getString("driver", "");
Class.forName(driver);
jdbcConnectionInitSize = property.getInt("jdbcConnectionInitSize", 0);
//创建最小数据库的连接
for (int i = 0; i < jdbcConnectionInitSize; i++) {
final Connection conn = DriverManager.getConnection(url);
System.out.println("connected to phoenix...");
list.add(conn);
}
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}@Override
public Connection getConnection() throws SQLException {
if (list.size() == 0 && max <= 50) {
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
for (int i = 0; i < jdbcConnectionInitSize; i++) {
final Connection conn = DriverManager.getConnection(url);
list.add(conn);
}
max++;
}
if (list.size() > 0) {
final Connection conn1 = (Connection) list.removeFirst();
return (Connection) Proxy.newProxyInstance(JdbcConnectionPool.class.getClass().getClassLoader(),
new Class[]{Connection.class}, new InvocationHandler() {@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (!method.getName().equalsIgnoreCase("close")) { return method.invoke(conn1, args); } else { list.add(conn1); return null; } } }); } else { System.out.println("connect phoenix error."); } return null;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}@Override
publicT unwrap(Class iface) throws SQLException {
return null;
}@Override
public boolean isWrapperFor(Class> iface) throws SQLException {
return false;
}@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}@Override
public void setLogWriter(PrintWriter out) throws SQLException {}
@Override
public void setLoginTimeout(int seconds) throws SQLException {}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
编写连接工具:
JdbcConnection
public class JdbcConnection {
private static JdbcConnectionPool pool = new JdbcConnectionPool();
private final static Connection conn = null;
private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(JdbcConnection.class);
/**
* 获取链接
*
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
return pool.getConnection();
}
/**
* 释放连接
*
* @param conn
* @param st
* @param rs
*/
public static void release(final Connection conn, final PreparedStatement st, final ResultSet rs) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
super.run();
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (st != null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
});
}
}
加载配置文件:
//加载配置文件
public class PropertyUtil extends Properties {
private static final long serialVersionUID = 50440463580273222L;
private static PropertyUtil instance = null;
public static synchronized PropertyUtil getInstance() {
if (instance == null) {
instance = new PropertyUtil();
}
return instance;
}
public String getProperty(String key, String defaultValue) {
String val = getProperty(key);
return (val == null || val.isEmpty()) ? defaultValue : val;
}
public String getString(String name, String defaultValue) {
return this.getProperty(name, defaultValue);
}
public int getInt(String name, int defaultValue) {
String val = this.getProperty(name);
return (val == null || val.isEmpty()) ? defaultValue : Integer.parseInt(val);
}
public long getLong(String name, long defaultValue) {
String val = this.getProperty(name);
return (val == null || val.isEmpty()) ? defaultValue : Integer.parseInt(val);
}
public float getFloat(String name, float defaultValue) {
String val = this.getProperty(name);
return (val == null || val.isEmpty()) ? defaultValue : Float.parseFloat(val);
}
public double getDouble(String name, double defaultValue) {
String val = this.getProperty(name);
return (val == null || val.isEmpty()) ? defaultValue : Double.parseDouble(val);
}
public byte getByte(String name, byte defaultValue) {
String val = this.getProperty(name);
return (val == null || val.isEmpty()) ? defaultValue : Byte.parseByte(val);
}
public PropertyUtil() {
InputStream in;
try {
in = this.getClass().getClassLoader().getResourceAsStream("cloudConfig.properties");
this.load(in);
in.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
编写连接测试:
public class PhoenixTest {
public static void main(String[] args) {
Connection conn = null;
Statement statement = null;
final PropertyUtil property = PropertyUtil.getInstance();
final String url = property.getString("url", "");
final String driver = property.getString("driver", "");
final String sql = property.getString("sql", "");
try {
Class.forName(driver);
conn = DriverManager.getConnection(url);
statement = conn.createStatement();
System.out.println("--------------------------");
final ResultSet rs = statement.executeQuery(sql);
while (rs.next()) {
System.out.println(rs.getString("name"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
conn.close();
statement.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
配置文件
cloudConfig.properties:
jdbc info
url=jdbc:phoenix:192.168.1.12:2181
driver=org.apache.phoenix.jdbc.PhoenixDriver
jdbcConnectionInitSize=10
sql=select * from "phoenixtest"
注意此时直接运行是连接不上的,为什么?
因为phoenix需要加载hbase的配置文件才能找到需要的相关参数,于是我们将hbase-site.xml放入到source 目录conf下
hbase-site.xml
dfs.domain.socket.path
/var/lib/hadoop-hdfs/dn_socket
hbase.bulkload.staging.dir
/apps/hbase/staging
hbase.client.keyvalue.maxsize
1048576
hbase.client.retries.number
35
hbase.client.scanner.caching
100
hbase.cluster.distributed
true
hbase.coprocessor.master.classes
hbase.coprocessor.region.classes
org.apache.hadoop.hbase.security.access.SecureBulkLoadEndpoint
hbase.custom-extensions.root
/hdp/ext/2.6/hbase
hbase.defaults.for.version.skip
true
hbase.hregion.majorcompaction
604800000
hbase.hregion.majorcompaction.jitter
0.50
hbase.hregion.max.filesize
10737418240
hbase.hregion.memstore.block.multiplier
4
hbase.hregion.memstore.flush.size
134217728
hbase.hregion.memstore.mslab.enabled
true
hbase.hstore.blockingStoreFiles
10
hbase.hstore.compaction.max
10
hbase.hstore.compactionThreshold
3
hbase.local.dir
${hbase.tmp.dir}/local
hbase.master.info.bindAddress
0.0.0.0
hbase.master.info.port
16010
hbase.master.namespace.init.timeout
2400000
hbase.master.port
16000
hbase.master.ui.readonly
false
hbase.master.wait.on.regionservers.timeout
30000
hbase.region.server.rpc.scheduler.factory.class
org.apache.hadoop.hbase.ipc.PhoenixRpcSchedulerFactory
hbase.regionserver.executor.openregion.threads
20
hbase.regionserver.global.memstore.size
0.4
hbase.regionserver.handler.count
30
hbase.regionserver.info.port
16030
hbase.regionserver.port
16020
hbase.regionserver.wal.codec
org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec
hbase.rootdir
hdfs://node3:8020/apps/hbase/data
hbase.rpc.protection
authentication
hbase.rpc.timeout
90000
hbase.security.authentication
simple
hbase.security.authorization
false
hbase.superuser
hbase
hbase.tmp.dir
/tmp/hbase-${user.name}
hbase.zookeeper.property.clientPort
2181
hbase.zookeeper.quorum
node1,node2,node3,node4,node6,node7,node8,node9,node10
hbase.zookeeper.useMulti
true
hfile.block.cache.size
0.4
phoenix.functions.allowUserDefinedFunctions
true
phoenix.query.timeoutMs
60000
zookeeper.recovery.retry
6
zookeeper.session.timeout
90000
zookeeper.znode.parent
/hbase-unsecure
程序目录结构如下:
select * from "ott_deviceinfo_buffer" where "userid"='13860507270';
注意,13860507270字符串必须用单引号,userid字段必须用双引号
创建二级索引
create index idex0 on "ott_deviceinfo_buffer" ("info"."deviceid");
创建多级索引
create local index idex2 on "ott_deviceinfo_buffer" ("info"."terminalid","info"."terminalmode");
查看是否应用了二级索引
explain select * from "ott_deviceinfo_buffer" where "userid"='13860507270';
创建异步索引表
CREATE INDEX async_index ON ""ott_deviceinfo_buffer"" (v) ASYNC
通过create index的时候指定 ASYNC 关键字来指定异步创建索引。执行这个命令之后并不会引起索引表与源表的直接同步。这个时候查询并不会使用这个索引表。那么索引数据的导入还需要采用phoenix提供的索引同步工具类 IndexTool , 这是一个mapreduce工具类,使用方式如下:
${HBASE_HOME}/bin/hbase org.apache.phoenix.mapreduce.index.IndexTool
--schema MY_SCHEMA --data-table MY_TABLE --index-table ASYNC_IDX
--output-path ASYNC_IDX_HFILES
执行结束以后才会应用索引表