天天看點

簡單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,我們可以動态查詢這些不同資料源,而且甚至可以結合不同的資料源查詢。下面的關系圖展示了該教程中不同資料源之間的“關系”。

浏覽日志資料

利用Drill,你可以對檔案和目錄直接執行SQL操作,而無需做預先模式定義或者任何模型更改的模式管理動作。查詢的同時也能得到相應的模式。下圖是使用Drill Explorer來浏覽JSON資料,無需定義和管理任何集中的模式。

下面的例子是對一個JSON檔案執行SQL語句,查詢事務id、裝置名稱和以cr開頭的關鍵字。

浏覽Hive上訂單表(Orders)資料

下面的例子是使用Drill Explorer來浏覽Hive的order表。Drill可以在不用Hive執行引擎的條件下使用Hive元存儲。

浏覽HBase的産品表(Products)

HBase是一個NoSQL資料庫。MapR-DB是一個企業級的Hadoop NoSQL資料庫,它實作了HBase的API,并将其開放以支援應用開發。MapR-DB的使用起來就和HBase一樣,但底層實作更為高效,而且它與MapR檔案系統是內建的。HBase是一個面向列族的資料庫,且是“無模式”的,這意味着當你建立一個表的時候隻需要定義列族。在你寫資料的時候,列會被動态定義,并且資料都以位元組數組的形式存儲,而非輸入時的類型。

HBase的産品表(products)有兩個列族(下圖中的details和pricing):

如果查詢HBase采用Hive或其他基于Hadoop的SQL(SQL-on-Hadoop)方式,你不得不包裝一個模式定義以此明确如何轉換二進制的值。不像其他基于Hadoop的SQL方式,Drill在Hive中不需要包裝模式定義來操作HBase資料。你可以像如下使用Drill explorer來浏覽HBase中産品(products)表。

這是一個使用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函數将資料轉換到相應的資料類型。另外,列可以像這樣指定别名:

為了避免反複寫入冗長且複雜的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):

在查詢中關聯Hive的訂單表(Orders)和産品視圖表(Prodview)

通過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的例子:

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:

使用Drill REST接口查詢訂單産品視圖的JQuery用戶端

現在我們來看看為剛剛的REST查詢而做的一個簡單JQuery用戶端。這是用戶端的一個截圖。它發起查詢給Drill并且在一個表格中展示出了結果。

使用Google Chrome開發者工具,你可以看到HTTP請求如下所示:

而傳回的response如下:

一個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)的功能,如下圖。

搜尋功能的代碼隻是在查詢中添加了where category like ‘searchKey’:

function search(searchKey) {
    if (searchKey != '')
        query = query + ' where category like '' + searchKey + '%'';
    doQuery(query);
}
      

在Chrome開發者工具視窗看到的JSON請求資料是這樣的: