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. java环境配置笔记

    1.使用Eclipse,要安装jdk,jdk现在可用1.7版本 2.打开Eclipse,配置maven,打开window-preferencess,在maven-user settings处,设置ma ...

  2. Mono、Unity和Xamarin三者关系

    1.Mono: .net是微软出的标准.如果站在Mono的角度来说,这套标准能规定编译器产生一些符合一定条件的文件出来,这些中间文件最后在目标平台上被解析成跟机器相关的东西.问题是,开始只有Windo ...

  3. beetle 2.7海量消息广播测试

    由于client资源限制,只进行了300物体互动广播测试:物体活动频率是每秒20次,服务器每秒转发的消息量大概180W条. 转发消息结构: class Po : IMessage { public i ...

  4. 解决“在多字节的目标代码页中,没有此Unicode字符可以映射到的字符”

    今天在处理Google网站管理员中的500错误时发现这样一些URL: http://www.cnblogs.com/Garnai/tag/3D%3F%96%CA/ http://www.cnblogs ...

  5. spring mvc 配置对静态资源的访问

    在spring mvc的配置文件中做如下配置: 1. <?xml version="1.0" encoding="UTF-8"?> <bean ...

  6. JavaScript中for..in循环陷阱介绍

    for...in循环中的循环计数器是字符串,而不是数字它包含当前属性的名称或当前数组元素的索引,下面有个不错的示例大家可以参考下   大家都知道在JavaScript中提供了两种方式迭代对象: (1) ...

  7. 【转】关于KDD Cup '99 数据集的警告,希望从事相关工作的伙伴注意

    Features From: Terry Brugger Date: 15 Sep 2007 Subject: KDD Cup '99 dataset (Network Intrusion) cons ...

  8. atittit.表单验证的实现方式以及原理本质以及选型以及自定义兼容easyui dsl规则的表单验证

    atittit.表单验证的实现方式以及原理本质以及选型以及自定义兼容easyui dsl规则的表单验证 1. 需求,表单验证需要弹框式,但目前easyui ms绑定死了tooltip式样 1 2. 表 ...

  9. Javascript中DOM技术的的简单学习

    第十四课DOM技术概述1:DOM概述 文档对象模型DOM(Document Object Model)定义访问和处理HTML文档的标准方法.DOM 将HTML文档呈现为带有元素.属性和文本的树结构(节 ...

  10. hdu 2844 多重背包coins

    http://acm.hdu.edu.cn/showproblem.php?pid=2844 题意: 有n个硬币,知道其价值A1.....An.数量C1...Cn.问在1到m价值之间,最多能组成多少种 ...