目前针对SQL on ElasticSearch
已经有了比较好的解决方案:elasticsearch-sql,其实Spark-SQL也可以满足一些基本的ES数据探查的需求,实现起来也相对简单。
elasticsearch-spark的包针对ES扩展了Spark
Datasource,我们可以使用sql查询es中的数据,中间Spark充当了“SQL解析器”的角色。作者:PowerMe 链接:https://www.jianshu.com/p/6b5230fde602 来源:简书
但是很多时候我们还不得不面临一些对elasticsearch中的多个索引的关联sql查询需求,这时候就只能使用spark-sql来实现。
环境准备
pom.xml中添加依赖:
<properties>
<java.version>1.8</java.version>
<scala.version>2.10.5</scala.version>
</properties>
<dependencies>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>6.6.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>6.6.1</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.13.Final</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.10</artifactId>
<version>2.1.2</version>
<exclusions>
<exclusion>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
</exclusion>
<exclusion>
<groupId>org.codehaus.janino</groupId>
<artifactId>commons-compiler</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.10</artifactId>
<version>2.1.2</version>
<exclusions>
<exclusion>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
</exclusion>
<exclusion>
<groupId>org.codehaus.janino</groupId>
<artifactId>commons-compiler</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
<exclusions>
<exclusion>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
</exclusion>
<exclusion>
<groupId>org.codehaus.janino</groupId>
<artifactId>commons-compiler</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.scala-lang.modules</groupId>
<artifactId>scala-xml_2.11</artifactId>
<version>1.0.6</version>
<exclusions>
<exclusion>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
</exclusion>
<exclusion>
<groupId>org.codehaus.janino</groupId>
<artifactId>commons-compiler</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch-spark-20_2.10</artifactId>
<version>6.3.2</version>
<exclusions>
<exclusion>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
</exclusion>
<exclusion>
<groupId>org.codehaus.janino</groupId>
<artifactId>commons-compiler</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>commons-compiler</artifactId>
<version>2.7.8</version>
</dependency>
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>2.7.8</version>
</dependency>
</dependencies>
创建客户端
es rest客户端:
restClient = RestClient.builder(
new HttpHost("cdh01", 9200, "http"),
new HttpHost("cdh02", 9200, "http"),
new HttpHost("cdh03", 9200, "http"),
new HttpHost("cdh04", 9200, "http"),
new HttpHost("cdh05", 9200, "http")).build();
java spark context:
SparkConf sparkConf = new SparkConf().setAppName("HBaseTest").setMaster("local[*]");
sparkConf.set("es.index.auto.create","true"); //在spark中自动创建es中的索引
sparkConf.set("es.nodes","192.168.1.1");//设置在spark中连接es的url和端口
sparkConf.set("es.port","9200");
sparkConf.set("es.nodes.wan.only","true");
sc = new JavaSparkContext(sparkConf);
动态导入数据及sql执行
下面是在我们系统中使用的部分关键代码的简化版本。
"table_array"是外部传入的一个索引名的链表,"v"是我们在es中创建的type。
// spark
SQLContext sql = new SQLContext(sc);
// 注册临时表
for (int i = 0; i < tableArray.size(); i++){
sql.read().format("es").load(table_Array.get(i) + "/v").registerTempTable(table_Array.get(i));
}
// 执行sql
Dataset df = sql.sql(sqlString);
总结
这种设计方法虽然做到了功能的demo实现,但是在性能上存在严重的问题,由于sql.read()进行了全表导入,会占用大量的内存和计算时间。
比较推荐的还是SparkSql 读取elasticsearch 表数据,先通过query条件创建RDD以减少读入数据量,然后在RDD的基础上进行合并sql操作。
更新
使用RDD的方法虽然解决了加载数据量过多的问题,但是也使我们的接口失去了通用性,所以最后我们使用了elasticsearch on hadoop接口可以使用 string url query 的特性,对DataSet的生成进行了优化。
Dataset d = JavaEsSparkSQL.esDF(sql, tableNameSpace + tableName, query);
d.registerTempTable(tableNameSpace + tableName);