博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基于SQL-on-HBase产品的一种有趣的触发器实现
阅读量:6221 次
发布时间:2019-06-21

本文共 5370 字,大约阅读时间需要 17 分钟。

背景

现在市面上有不少的SQ on HBase的产品:Trafodion、Impala、Greplum等等,但是很少有支持触发器功能的产品,也许是因为在这种应用场景下很少使用到触发器,但不可否认触发器是一项非常有用的功能,本文主要针对一些缺少触发器功能的产品提供一种有趣实现的思路。

概览

如下图所示:

图片描述

  1. 当有数据插入到SQEngine中时,数据会存储至HBase.
  2. 在HBase中借助Observer,当有数据触发时会通知BaseRegionObserver, 根据项目需要实现prePut、PostPut等方法完成Event的处理,在本例中,会以HTTP请求的方式发送至Trigger Server。
  3. 执行PLSQL或者是SQL, 此步骤可以根据产品自己的特点决定,如果有PLSQL功能则Trigger内容中可以书写PLSQL语法,反之可以以SQL的形式执行。
  4. PLSQL最终还是要通过SQ Engine执行,所以这一步一般数据数据库本身功能。

实现HBase Observer

继承BaseRegionObserver,实现相应的方法,本例中实现put,也就是在插入/更新时触发操作。

import org.apache.hadoop.conf.Configuration;  import org.apache.hadoop.hbase.*;  import org.apache.hadoop.hbase.client.*;  import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;  import org.apache.hadoop.hbase.coprocessor.ObserverContext;   import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;  import org.apache.hadoop.hbase.filter.ByteArrayComparable;import org.apache.hadoop.hbase.filter.CompareFilter;import org.apache.hadoop.hbase.regionserver.RegionScanner;import org.apache.hadoop.hbase.regionserver.wal.WALEdit;import org.apache.http.HttpResponse;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.net.URI;import java.io.IOException;import java.util.List;public class EsgObserver extends BaseRegionObserver {    @Override    public void prePut(ObserverContext
e, Put put, WALEdit edit, Durability durability) throws IOException { String tableName = e.getEnvironment().getRegion().getTableDesc().getNameAsString(); e.getEnvironment().getRegionInfo().getRegionId(); if(put.getTimeStamp() != ts){ sendRequest(tableName,"prePut"); } this.ts = put.getTimeStamp();}private void sendRequest(String tableName, String API){ HttpClient client = new DefaultHttpClient(); HttpGet get = new HttpGet(); HttpResponse response; String url, ip, port; Configuration conf = HBaseConfiguration.create(); try { Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(TableName.valueOf(tableName)); HTableDescriptor htd = t.getTableDescriptor(); String value = htd.getValue(“coprocessor$4”); String args = value.split(“\\|”)[3]; ip = (args.split(“,”)[0]).split(“=”)[1];port = (args.split(“,”)[1]).split(“=”)[1]; url = “http://” + ip + “:” + port + “/esg/exec/”; } catch (Exception e) { logger.error(e.getMessage(), e); return; } String table = tableName.split(“:”)[1]; String uri = url + API + “?tableName=” + table; logger.info(“uri:” + uri); try{ get.setURI(new URI(uri)); response = client.execute(get); logger.info(“request response code:” + response.getStatusLine().getStatusCode()); logger.info(“request response reason:” + response.getStatusLine().getReasonPhrase()); } catch (Exception e){ logger.error(e.getMessage(),e); } }}

Trigger Server的核心实现

Trigger Server中采用了Spring boot + Antlr4实现。其中,Spring boot负责web 项目的搭建,Antlr4实现触发器语句的解析分离。

语句解析如下:

语句解析主要进行触发创建、修改、删除语句的解析。

grammar Sql;@header {    package com.esg.hbase.generated;}program : block EOF;//block: T_CREATE T_OR T_REPLACE;block: create_trigger_stmt | drop_stmt | switch_trigger_stmt;create_trigger_stmt: T_CREATE has_replace T_TRIGGER trigger_name before_after cuid_ops T_ON table_name for_each_row plsql_block;has_replace: (T_OR T_REPLACE)? ;trigger_name: L_ID;before_after: (T_BEFORE | T_AFTER);cuid_ops: (T_INSERT | T_DELETE | T_UPDATE T_OF L_ID);table_name: L_ID;for_each_row: (T_FOR T_EACH T_ROW)?;plsql_block: L_PLSQL_BLOCK;drop_stmt: T_DROP T_TRIGGER trigger_name T_SEMICOLON?;enable_disable: (T_ENABLE | T_DISABLE);switch_trigger_stmt: (switch_trigger_all_stmt | switch_trigger_specify_stmt);switch_trigger_specify_stmt: T_ALTER T_TRIGGER trigger_name enable_disable;switch_trigger_all_stmt: T_ALTER T_TABLE table_name enable_disable T_ALL T_TRIGGERS;
核心接收请求的类
import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.sql.Statement;import java.util.Iterator;import java.util.List;@RequestMapping("esg/exec") @RestController public class ExecutorController {  @Autowired private TriggerService triggerService; @GetMapping("/prePut") public void prePut(String tableName) {    List
triggers = this.triggerService.find(tableName, true, 1); Iterator
iter = triggers.iterator(); while(iter.hasNext()){ Trigger trigger = iter.next(); if (trigger.getHasEnabled()){ String plsql = trigger.getPlsql(); executeQuery(url, username, password, plsql); } } } }

总结

本文提供了一种在SQL-on-HBase数据库上实现触发器的一种思路,这种方式实现了触发器功能的独立部署执行,方便简洁,借助于Web系统完成触发器的执行。此方案可以实现数据同步,例如同步到Kafka消息系统中,同步到其他库中等,有一些适合的应用场景。

但是,此方法也有着较为明显的缺陷,性能上可能会带来比较大的问题,所有的触发事件都需要经过同一个web服务器,增加了Web系统的负担,一旦有大量数据插入修改时,性能会成为瓶颈。事务方面,触发器中的语句和最初的插入语句不属于同一个事务,会造成数据不一致。容错也不在此方案的考虑范围内。
本文为抛砖引玉,欢迎各位讨论提出更好的更合理方案。

转载地址:http://egrja.baihongyu.com/

你可能感兴趣的文章
炸弹人——NABCD分析
查看>>
【前端积累】背景图像和背景替换
查看>>
ibatis sqlmap动态SQL
查看>>
使用WdatePicker日期组件时,选择日期后,执行某个方法
查看>>
HttpClient---------demo
查看>>
把玩爬虫框架Gecco
查看>>
递 归
查看>>
CSS3实现纸张边角卷起效果
查看>>
Windows平台AnyChat视频显示
查看>>
Altium 拼板方法以及 注意的 地方
查看>>
《推荐系统实践》序言、样章欢迎阅读!
查看>>
Android系统源码学习步骤
查看>>
JavaScript脚本关闭浏览器窗口不出现提示框小技巧
查看>>
浅谈Android View事件分发机制
查看>>
【转】FlashBack总结之闪回查询与闪回表
查看>>
python的多态
查看>>
alpha阶段总结
查看>>
js友好提示是否继续,post提交
查看>>
文本框,下拉框,单选框只读状态属性
查看>>
js 中for循环和indexOf()性能对比
查看>>