简单REST 客户端使用 SQL,Hadoop,Drill
根据福雷斯特研究公司(Forrester Research)的观点,SQL将成为Hadoop生态系统中最多产的应用方案之一。Apache Drill 是一个应用于大数据搜索的开源SQL查询引擎。REST服务和客户端已经成为互联网流行的技术。 Apache HBase则是一个广受欢迎的Hadoop NoSQL数据库。在本文中,我将结合 SQL、Hadoop、Drill、REST with JSON、NoSQL 及 HBase 等技术,讨论并展示如何使用 Drill REST API来查询 HBase 和 Hive。同时我也会分享一个使用Drill REST API的简单jQuery客户端,利用JSON做数据交换,提供给用户一个基本的操作界面。
Apache Drill教程和MapR沙箱
Apache Drill提供了直接查询文件中自描述和半结构化数据(类似与JSON和Parquet)以及HBase表的功能,并且查询HBase表时无需在类似Hive元存储的集中式存储中定义和维护表的模式。这个例子是建立在使用Drill的MapR沙箱和Drill教程中示例数据基础上的。含有Apache Drill的MapR沙箱是一个功能全面,单节点的集群,它可以让你在一个Hadoop环境中了解Apache Drill的概况。
浏览用例数据
Drill教程中的案例是一个在线零售系统,用户可以通过Web的界面和手机应用来购买商品。主要的客户概要信息和产品目录在MapR-DB中管理,这是一个NoSQL的HBase数据库。来自网页端和手机应用端的点击流数据以JSON格式文件的形式存储在Hadoop中,订单数据则是存储在Hive中。通过Drill,我们可以动态查询这些不同数据源,而且甚至可以结合不同的数据源查询。下面的关系图展示了该教程中不同数据源之间的“关系”。
![](http://7xiur2.com1.z0.glb.clouddn.com/0156.png)
浏览日志数据
利用Drill,你可以对文件和目录直接执行SQL操作,而无需做预先模式定义或者任何模型更改的模式管理动作。查询的同时也能得到相应的模式。下图是使用Drill Explorer来浏览JSON数据,无需定义和管理任何集中的模式。
![](http://7xiur2.com1.z0.glb.clouddn.com/0157.png)
下面的例子是对一个JSON文件执行SQL语句,查询事务id、设备名称和以cr开头的关键字。
![](http://7xiur2.com1.z0.glb.clouddn.com/0158.png)
浏览Hive上订单表(Orders)数据
下面的例子是使用Drill Explorer来浏览Hive的order表。Drill可以在不用Hive执行引擎的条件下使用Hive元存储。
![](http://7xiur2.com1.z0.glb.clouddn.com/0159.png)
浏览HBase的产品表(Products)
HBase是一个NoSQL数据库。MapR-DB是一个企业级的Hadoop NoSQL数据库,它实现了HBase的API,并将其开放以支持应用开发。MapR-DB的使用起来就和HBase一样,但底层实现更为高效,而且它与MapR文件系统是集成的。HBase是一个面向列族的数据库,且是“无模式”的,这意味着当你创建一个表的时候只需要定义列族。在你写数据的时候,列会被动态定义,并且数据都以字节数组的形式存储,而非输入时的类型。
HBase的产品表(products)有两个列族(下图中的details和pricing):
![](http://7xiur2.com1.z0.glb.clouddn.com/0160.png)
如果查询HBase采用Hive或其他基于Hadoop的SQL(SQL-on-Hadoop)方式,你不得不包装一个模式定义以此明确如何转换二进制的值。不像其他基于Hadoop的SQL方式,Drill在Hive中不需要包装模式定义来操作HBase数据。你可以像如下使用Drill explorer来浏览HBase中产品(products)表。
![](http://7xiur2.com1.z0.glb.clouddn.com/0161.png)
这是一个使用Drill命令行界面来查询HBase中产品(products)表的示例。
0: jdbc:drill:> select * from maprdb.products limit 3;
+------------+------------+------------+
| row_key | details | pricing |
+------------+------------+------------+
| [B@1babffd6 | {"category":"bGFwdG9w","name":"IlNvbnkgbm90ZWJvb2si"} | {"price":"OTU5"} |
| [B@456a24be | {"category":"RW52ZWxvcGVz","name":"IzEwLTQgMS84IHggOSAxLzIgUHJlbWl1bSBEaWFnb25hbCBTZWFtIEVudmVsb3Blcw=="} | {"price":"MTY="} |
| [B@3b92598c | {"category":"U3RvcmFnZSAmIE9yZ2FuaXphdGlvbg==","name":"MjQgQ2FwYWNpdHkgTWF4aSBEYXRhIEJpbmRlciBSYWNrc1BlYXJs"} | {"price":"MjEx"} |
+------------+------------+------------+
鉴于HBase中任何数据都是以字节数组存储的,以及Drill不需要预先的模式定义来指明数据类型,查询所返回列值的原始字节数组与其存储状态时一致。通过Drill,你可以使用convert_from函数将数据转换到相应的数据类型。另外,列可以像这样指定别名:
![](http://7xiur2.com1.z0.glb.clouddn.com/0162.png)
为了避免反复写入冗长且复杂的HBase查询语句,通用的工作流程是先创建一个包含有用信息的视图,然后再查询该视图。在ODBC工具,JDBC客户端或本例中的REST客户端中,视图可以使数据以更可用的”表格”形式来便于使用。REST客户端所用的视图是如上所示经过转换与Hive表连接的HBase数据。这是一个基于HBase数据创建视图的查询:
首先切换到可写的工作区:
0: jdbc:drill:> use dfs.mydata;
+------------+------------+
| ok | summary |
+------------+------------+
| true | Default schema changed to 'dfs.mydata' |
+------------+------------+
1 row selected (0.078 seconds)
创建一个HBase中产品表(Products)的视图:
0: jdbc:drill:> create or replace view prodview as SELECT CAST(row_key AS INTEGER) AS prod_id, CAST(t.details.category AS VARCHAR(40)) AS category, CAST(t.details.name AS VARCHAR(129)) AS name FROM maprdb.products t;
+------------+------------+
| ok | summary |
+------------+------------+
| true | View 'prodview' replaced successfully in 'dfs.mydata' schema |
+------------+------------+
与传统数据库所不同,视图通常都是由DBA/开发人员来操作,Drill中基于文件系统的视图是非常轻量的。一个Drill视图事实上是在一个简单的JSON 文件中定义的虚拟数据集。来看看创建的JSON文件:
# cat /mapr/demo.mapr.com/data/views/prodview.view.drill
{
"name" : "prodview",
"sql" : "SELECT CAST(`row_key` AS INTEGER) AS `prod_id`, CAST(`t`.`details`['category'] AS VARCHAR(40)) AS `category`, CAST(`t`.`details`['name'] AS VARCHAR(129)) AS `name`nFROM `maprdb`.`products` AS `t`",
"fields" : [ {
"name" : "prod_id",
"type" : "INTEGER"
}, {
"name" : "category",
"type" : "VARCHAR",
"precision" : 40
}, {
"name" : "name",
"type" : "VARCHAR",
"precision" : 129
} ],
"workspaceSchemaPath" : [ "dfs", "mydata" ]
}
你可以像下面一样使用Drill explorer来浏览产品视图表(prodview):
![](http://7xiur2.com1.z0.glb.clouddn.com/0163.png)
在查询中关联Hive的订单表(Orders)和产品视图表(Prodview)
![](http://7xiur2.com1.z0.glb.clouddn.com/0164.png)
通过Drill,我们可以在查询中动态地关联Hive、文件和HBase表。举个例子,该查询是从json日志文件中获取device和prod_id,从Hive的订单表中获取order,其中json日志文件的prod_id等于order表中的prod_id。【译者注:原文作者笔误,将查询条件的prod_id写成了cust_id。】
创建一个视图orderprodview,连接HBase的产品表视图(prodview)
【译者注:此处原文作者有误,写的是产品表product】和Hive的订单表(Order):
0: jdbc:drill:> create or replace view orderprodview as select o.order_id, o.`month`, o.cust_id, o.state, o.prod_id, o.order_total, p.category, p.name from hive.orders o, dfs.mydata.prodview p where o.prod_id=p.prod_id limit 100;
+------------+------------+
| ok | summary |
+------------+------------+
| true | View 'orderprodview' replaced successfully in 'dfs.mydata' schema |
+------------+------------+
这是查询视图orderprodview的例子:
![](http://7xiur2.com1.z0.glb.clouddn.com/0165.png)
Drill视图的用途如下:
简化复杂查询
从多种数据源中聚合数据
与表一样
配有像Tableau一样的BI(Business Intelligence)工具
数据管理灵活
接下来,让我们看看在REST中如何使用这个视图
Drill REST接口
Drill提供了一个简单的REST接口,你可以从Drill wiki上了解到更多https://cwiki.apache.org/confluence/display/DRILL/Apache+Drill+Wiki。这是提交一个查询并收到结果的REST API。你可以通过HTTP POST请求到Drill URL,即:8047/query.json,body携带一个JSON格式的Request,如下图所示。你所收到的response响应将是一个JSON对象列表。
POST <drill_node_ip_address>:8047/query.json
Request body:
{
"queryType" : "SQL",
"query" : "select * from dfs.mydata.orderprodview limit 5”
}
Response body (list of JSON objects):
[
{
"order_total" : 13,
"category" : "Binders and Binder Accessories",
"prod_id" : 909,
"name" : "Wilson Jones Ledger-SizePiano-Hinge Binder2Blue",
"state" : "ca", }, …
]
使用CURL或者浏览器插件来测试REST接口非常方便。下面这个例子就是从Linux命令行用CURL来发送对orderprodview的查询:
# curl
--header "Content-type: application/json"
--request POST
--data '{
"queryType" : "SQL",
"query" : "select * from dfs.mydata.orderprodview limit 1"
}'
http://192.168.110.133:8047/query.json
[ {
"order_total" : 13,
"category" : "Binders and Binder Accessories",
"prod_id" : 909,
"name" : "Wilson Jones Ledger-SizePiano-Hinge Binder2Blue",
"state" : "ca",
"month" : "June",
"order_id" : 67212,
"cust_id" : 10001
}]
这是使用Google Chrome的一个REST Client扩展来查询orderprodview:
![](http://7xiur2.com1.z0.glb.clouddn.com/0166.png)
![](http://7xiur2.com1.z0.glb.clouddn.com/0167.png)
![](http://7xiur2.com1.z0.glb.clouddn.com/0168.png)
使用Drill REST接口查询订单产品视图的JQuery客户端
现在我们来看看为刚刚的REST查询而做的一个简单JQuery客户端。这是客户端的一个截图。它发起查询给Drill并且在一个表格中展示出了结果。
![](http://7xiur2.com1.z0.glb.clouddn.com/0169.png)
使用Google Chrome开发者工具,你可以看到HTTP请求如下所示:
![](http://7xiur2.com1.z0.glb.clouddn.com/0170.png)
而返回的response如下:
![](http://7xiur2.com1.z0.glb.clouddn.com/0171.png)
一个jQuery客户端
这是参与服务调用的jQuery代码。jQuery客户端使用jquery.ajax来执行HTTP POST到Drill查询URL,将查询数据以JSON的格式传递过去。
var rootURL = "http://host:8047/query.json";
var query = "select * from dfs.mydata.orderprodview"
function doQuery(query) {
console.log('doQuery');
$.ajax({
type: 'POST',
contentType: 'application/json',
url: rootURL,
dataType: "json",
data: queryToJSON(query),
success: function(data) {
console.log(' success: ' + data);
renderList(data);
},
error: function(jqXHR, textStatus, errorThrown) {
alert('error: ' + textStatus);
}
});
}
function queryToJSON(query) {
return JSON.stringify({
"queryType": "SQL",
"query": query
});
}
如果请求成功,renderList会被调用,该方法如下所示。返回的response数据是一个JSON对象数据,renderList会把订单对象添加到index.html页面的html表格中。
function renderList(data) {
var list = data == null ? [] : (data instanceof Array ? data : [data])
$.each(list, function(order) {
$('#orderList').append('<tr><td>' + order.order_id + '</td><td>'
+ order.cust_id + '</td><td>'
+ order.state + '</td><td>' + order.month + '</td><td>'
+ order.order_total + '</td><td>'
+ order.prod_id + '</td><td>' + order.category + '</td><td>'
+ order.name + '</td></tr>');
});
}
Index.html
<table id="list" class='tablesorter-blue'>
<thead>
<tr>
<th>< b>Order Id</b></th>
<th>< b>Customer Id</b></th>
<th>< b>State</b></th>
<th>< b>Month</b></th>
<th>< b>Order Total</b></th>
<th>< b>Product Id</b></th>
<th>< b>Product Category</b></th>
<th>< b>Product Name</b></th>
</tr>
</thead>
<tbody id="orderList" ></tbody>
</table>
jQuery客户端还提供了搜索产品类目(Product Category)的功能,如下图。
![](http://7xiur2.com1.z0.glb.clouddn.com/0172.png)
搜索功能的代码只是在查询中添加了where category like ‘searchKey’:
function search(searchKey) {
if (searchKey != '')
query = query + ' where category like '' + searchKey + '%'';
doQuery(query);
}
在Chrome开发者工具窗口看到的JSON请求数据是这样的:
![](http://7xiur2.com1.z0.glb.clouddn.com/0173.png)
以上的例子包含了一个简单的REST客户端,它采用Drill REST API来动态查询HBase和Hive。
想要了解更多如何使用Drill的示例,下载MapR沙箱,然后尝试一下Drill沙箱教程。更多信息可到Apache Drill官方网站获取。