1.概述

  上次给大家分享了关于 Kafka SQL 的实现思路,这次给大家分享如何实现 Kafka SQL。要实现 Kafka SQL,在上一篇《Kafka - SQL 引擎分享》中分享了其实现的思路,核心包含数据源的加载,以及 SQL 树的映射。今天笔者给大家分享相关实现的代码。

2.内容

  这里,将数据映射成 SQL Tree 是使用了 Apache Calcite 来承接这部分工作。在实现代码之前,我们首先来了解下 Apache Calcite 的相关内容,Apache Calcite 是一个面向 Hadoop 的查询引擎,它提供了业界标准的 SQL 语言,以及多种查询优化和连接各种存储介质的适配器。另外,还能处理 OLAP 和流处理场景。因为存在这么多优秀和闪光的特性, Hadoop 生态圈中 Apache Calcite 越发引人注目,被诸多项目所集成,常见的有:

  • Apache Drill:基于大数据的实时查询引擎
  • Apache Spark:继 Hadoop 之后的新一代大数据分布式处理框架。
  • 更多详情,这里就不一一列举了,详情查看地址:《Adapters

2.1 数据类型

  这里数据源的数据类型,我们分为两种,一种是 SQL,另一种是基于编程语言的,这里我们所使用的是 Java,定义内容如下:

public static Map<String, SqlTypeName> SQLTYPE_MAPPING = new HashMap<String, SqlTypeName>();
public static Map<String, Class> JAVATYPE_MAPPING = new HashMap<String, Class>(); public static void initRowType() {
SQLTYPE_MAPPING.put("char", SqlTypeName.CHAR);
JAVATYPE_MAPPING.put("char", Character.class);
SQLTYPE_MAPPING.put("varchar", SqlTypeName.VARCHAR);
JAVATYPE_MAPPING.put("varchar", String.class);
// ......
}

2.2 表的相关描述

  另外,我们需要对表进行一个描述,在关系型数据库中,一个正常的表由行列组成,定义内容如下:

    public static class Database {
public List<Table> tables = new LinkedList<Table>();
} public static class Table {
public String tableName;
public List<Column> columns = new LinkedList<Column>();
public List<List<String>> data = new LinkedList<List<String>>();
} public static class Column {
public String name;
public String type;
}

  在每个集合中存储数据库相关名称,每个数据库存储多个集合的表对象,每个表对象下面又有一系列的列以及绑定的数据源。在每个列对象中包含字段名和类型,层层递进,依次关联。在使用 Calcite 是,需要遵循其 JSON Model,上篇博客我们已经定义过其 JSON Model,这里我们直接拿来使用,内容如下:

{
version: '1.0',
defaultSchema: 'kafka',
schemas: [
{
name: 'kafka',
type: 'custom',
factory: 'cn.smartloli.kafka.visual.engine.KafkaMemorySchemaFactory',
operand: {
database: 'kafka_db'
}
}
]
}

  要实现其 Model ,这里需要我们去实现 org.apache.calcite.schema.SchemaFactory 的接口,内容如下所示:

public class KafkaMemorySchemaFactory implements SchemaFactory {
@Override
public Schema create(SchemaPlus parentSchema, String name, Map<String, Object> operand) {
return new KafkaMemorySchema(name);
}
}

  而在 KafkaMemorySchema 类中,我们只需要实现它的 getTableMap 方法,内容如下所示:

 @Override
protected Map<String, Table> getTableMap() {
  Map<String, Table> tables = new HashMap<String, Table>();
Database database = KafkaMemoryData.MAP.get(this.dbName);
if (database == null)
  return tables;
for (KafkaMemoryData.Table table : database.tables) {
  tables.put(table.tableName, new KafkaMemoryTable(table));
}
return tables;
}

  从上述代码中,可以知道通过内存中的 Map 表查看对应的数据库对象,然后根据数据库对象中的表作为 Schema 中的表,而表的类型为 KafkaMemoryTable。

2.3 表类型

  这里笔者就直接使用全表扫描,使用 org.apache.calcite.schema.impl.AbstractTable 的默认方式,实现其 getRowType 方法和 scan 方法,内容如下所示:

public RelDataType getRowType(RelDataTypeFactory typeFactory) {
  if(dataType == null) {
  RelDataTypeFactory.FieldInfoBuilder fieldInfo = typeFactory.builder();
for (KafkaMemoryData.Column column : this.sourceTable.columns) {
  RelDataType sqlType = typeFactory.createJavaType(
  KafkaMemoryData.JAVATYPE_MAPPING.get(column.type));
  sqlType = SqlTypeUtil.addCharsetAndCollation(sqlType, typeFactory);
  fieldInfo.add(column.name, sqlType);
}
this.dataType = typeFactory.createStructType(fieldInfo);
}
return this.dataType;
}
public Enumerable<Object[]> scan(DataContext root) {
final List<String> types = new ArrayList<String>(sourceTable.columns.size());
for(KafkaMemoryData.Column column : sourceTable.columns) {
types.add(column.type);
}
final int[] fields = identityList(this.dataType.getFieldCount());
return new AbstractEnumerable<Object[]>() {
public Enumerator<Object[]> enumerator() {
return new KafkaMemoryEnumerator<Object[]>(fields, types, sourceTable.data);
}
};
}

  代码中,表中的字段名和类型是根据初始化时,每个表中的数据类型映射匹配的,在 KafkaMemoryData.SQLTYPE_MAPPING 和 KafkaMemoryData.JAVATYPE_MAPPING 中有描述相关自定义类型映射,这里就不多做赘述了。

  实现流程大致就是这个样子,将每次的 SQL 查询,通过 Calcite 解析成标准可执行的 SQL 计划,执行期间会根据定义的信息,初始化每一个 Schema,在通过调用 getTableMap 获取字段名和类型,根据这些信息判断查询的表,字段名,类型以及 SQL 语法是否标准规范。然后在使用 Calcite 内部机制,生成物理执行计划。查询计划是 Tree 形式的,底层是进行扫表操作(可看作为 FROM),获取每个表的数据,之后在根据表数据进行上层的关联操作,如 JOIN,GROUP BY,LIMIT 等操作。

3.测试

  完成上述流程后,进行代码测试,测试代码如下所示:

public static void main(String[] args) {
try {
Class.forName("org.apache.calcite.jdbc.Driver");
} catch (Exception ex) {
ex.printStackTrace();
}
Properties info = new Properties();
try {
Connection connection = DriverManager.getConnection("jdbc:calcite:model=/Users/dengjie/hadoop/workspace/kafka/kafka-visual/src/main/resources/plugins.json",info);
Statement st = connection.createStatement();
// String sql = "select * from \"Kafka\" where \"_plat\"='1004' limit 1";
String sql = "select * from \"Kafka\" limit 10"; long start = System.currentTimeMillis();
result = st.executeQuery(sql);
ResultSetMetaData rsmd = result.getMetaData();
List<Map<String, Object>> ret = new ArrayList<Map<String,Object>>(); while (result.next()) {
Map<String, Object> map = new HashMap<String, Object>();
for (int i = 1; i <= rsmd.getColumnCount(); i++) {
System.out.print(result.getString(rsmd.getColumnName(i)) + " ");
map.put(rsmd.getColumnName(i), result.getString(rsmd.getColumnName(i)));
}
ret.add(map);
System.out.println();
}
System.out.println(new Gson().toJson(ret));
result.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

4.总结

  以上便是将 Kafka 中数据消费后,作为数据源加载和 SQL Tree 映射的实现代码,实现不算太困难,在编写 SQL 查询的时候,需要遵循标准的 SQL 语法来操作数据源。

5.结束语

  这篇博客就和大家分享到这里,如果大家在研究学习的过程当中有什么问题,可以加群进行讨论或发送邮件给我,我会尽我所能为您解答,与君共勉!

Kafka - SQL 代码实现的更多相关文章

  1. EntityFramework 7 如何查看执行的 SQL 代码?

    EF 其他版本:EntityFramework 如何查看执行的 SQL 代码? 在 EF7 中,并没有 Context.Database.Log 属性访问方式,但改变更加强大了,我们可以使用下面方式配 ...

  2. EntityFramework 如何查看执行的 SQL 代码?

    在 VS 调试的时候,如果我们项目中使用的是 EntityFramework,查看 SQL 执行代码就不像 ADO.NET 那样直观了,我们需要设置下,可以参考下: How can I log the ...

  3. Visual Studio Entity Framework (EF) 生成SQL 代码 性能查询

    Visual Studio Entity Framework (EF) 生成SQL 代码 性能查询     SQL 中,有SQL Server Profiler可以用来查询性能以及查看外部调用的SQL ...

  4. iOS开发数据库篇—SQL代码应用示例

    iOS开发数据库篇—SQL代码应用示例 一.使用代码的方式批量添加(导入)数据到数据库中 1.执行SQL语句在数据库中添加一条信息 插入一条数据的sql语句: 点击run执行语句之后,刷新数据 2.在 ...

  5. MySQL查询今天/昨天/本周、上周、本月、上个月份数据的sql代码

    MySQL查询本周.上周.本月.上个月份数据的sql代码 作者: 字体:[增加 减小] 类型:转载 时间:2012-11-29我要评论 MySQL查询的方式很多,下面为您介绍的MySQL查询实现的是查 ...

  6. 同样的一句SQL语句在pl/sql 代码块中count 没有数据,但是直接用SQl 执行却可以count 得到结果

    pl/sql 代码块: SELECT count(distinct t2.so_nbr) INTO v_count2 FROM KFGL_YW_STEP_qd t2 WHERE t2.partitio ...

  7. 将PL/SQL代码封装在机灵的包中

    将代码封装在机灵的包中 http://www.oracle.com/technetwork/issue-archive/2013/13-jan/o13plsql-1872456.html 绝大多数基于 ...

  8. Kafka - SQL 引擎

    Kafka - SQL 引擎分享 1.概述 大多数情况下,我们使用 Kafka 只是作为消息处理.在有些情况下,我们需要多次读取 Kafka 集群中的数据.当然,我们可以通过调用 Kafka 的 AP ...

  9. SQL代码整理

    --SQL代码整理: create database mingzi--创建数据库go--连接符(可省略)create table biao--创建表( lieming1 int not null,-- ...

随机推荐

  1. jquery 编码解码

    中文转Unicode:HttpUtility.UrlEncodeUnicode(string str); 转换后中文格式:"%uxxxx" 举例:"柳_abc123&qu ...

  2. MySQL prepare 原理

    Prepare的好处  Prepare SQL产生的原因.首先从mysql服务器执行sql的过程开始讲起,SQL执行过程包括以下阶段 词法分析->语法分析->语义分析->执行计划优化 ...

  3. Dynamic CRM 2013学习笔记(四十四)CRM技术支持

    有时我们经常遇到一些CRM的问题,一时又无法解决,这时我们可能要找下外援,下面列出一些基本的技术支持.   1. CRM 论坛 https://community.dynamics.com/crm/f ...

  4. MySql获取表的字段名称、字段注解、字段类型、字段长度

    SELECT  COLUMN_NAME as '列名',COLUMN_COMMENT,DATA_TYPE as '字段类型' ,COLUMN_TYPE as '长度加类型' FROM informat ...

  5. Devexpress GridView内嵌dx:ASPxGridLookup取得控件值乱跳解决方案

    一 页面嵌入ASPxGridLookup控件 <dx:ASPxGridView ID="gridDetail" runat="server"        ...

  6. 由Memcached使用不当而引发性能问题的两个经验总结

    在这个cache everywhere的时代,在这个人人都会说分布式缓存的时代,Memcached几乎已成为网站开发中的标配. 作为一名普通的coder,我们在编写缓存代码的时候,很多情况下可能都只是 ...

  7. C++ 用于大型程序的工具

    <C++ Primer 4th>读书笔记 相对于小的程序员团队所能开发的系统需求而言,大规模编程对程序设计语言的要求更高.大规模应用程序往往具有下列特殊要求: 1. 更严格的正常运转时间以 ...

  8. JavaScript事件详解-zepto的事件实现

    zepto的event 可以结合上一篇JavaScript事件详解-原生事件基础(一)综合考虑源码暂且不表,github里还有中文网站都能下到最新版的zepto.整个event模块不长,274行,我们 ...

  9. lucene如何通过docId快速查找field字段以及最近距离等信息?

    http://www.cnblogs.com/LBSer/p/4419052.html 1 问题描述 我们的检索排序服务往往需要结合个性化算法来进行重排序,一般来说分两步:1)进行粗排序,这一过程由检 ...

  10. JSP取得绝对路径

    在JavaWeb开发中,常使用绝对路径的方式来引入JavaScript和CSS文件,这样可以避免因为目录变动导致引入文件找不到的情况,常用的做法如下: 一.使用${pageContext.reques ...