Spring Boot + Elasticsearch实现大批量数据集下中文的精确匹配

Stella981
• 阅读 959

缘由

数据存储在MYSQ库中,数据基本维持不变,但数据量又较大(几千万)放在MYSQL中查询效率上较慢,寻求一种简单有效的方式提高查询效率,MYSQL并不擅长大规模数据量下的数据查询。

技术方案

考虑后期同样会使用到es,此次直接结合spring-boot框架形成一个独立服务,并不涉及UI展现内容,(ES版本2.4.5,5.0+版本的话就不能再使用spring data elasticsearch)技术组合如下:

Spring Boot+ Spring-data-elasticsearch + Elasticsearch

结合elasticsearch-jdbc插件,全量将数据一次性导入es中,后期不涉及数据变更。

es安装

测试期间单机安装,官网下载对应版本,由于笔者工作环境基于JDK7,所以下载5.0以下版本,5.0+均依赖Java8,同时使用到elasticsearch-jdbc插件,一并下载安装完成。

走过的大弯路

直接使用elasticsearch-jdbc工具,编写脚本文件,抽取数据到es中,脚本样例如下:

  1. #!/bin/sh

  2. DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

  3. bin=${DIR}/../bin

  4. lib=${DIR}/../lib

  5. echo '

  6. {

  7. "type": "jdbc",

  8. "jdbc": {

  9. "elasticsearch.autodiscover": true,

  10. "url": "jdbc:mysql://192.168.1.3:3306/test",

  11. "user": "root",

  12. "password": "root",

  13. "sql": "SELECT * from tb_name1",

  14. "elasticsearch": {

  15. "host": "192.168.1.1",

  16. "port": 9300

  17. },

  18. "index": "my-index",

  19. "type": "my-type"

  20. }

  21. }

  22. ' | java \

  23. -cp "${lib}/*" \

  24. -Dlog4j.configurationFile=${bin}/log4j2.xml \

  25. org.xbib.tools.Runner \

  26. org.xbib.tools.JDBCImporter

数据导入成功后,可使用head插件直接查看到。使用基本查询测试,查询条件是name=测试&num=100,使用精确匹配term语句,查询数据未果,实际使用num=100独立查询时,有相关数据。

问题跟踪解决

导致此现象的原因在于中文分词的问题,使用elasticsearch-jdbc脚本中并未处理列的mapping类型。(中间做过一次尝试,在脚本中定义对应的type_mapping,但并未成功,有兴趣的朋友可再做尝试)。

注:es与ik分词插件结合,版本匹配需要特别关注,但本案例并不涉及

结合此案例,查询时并不需要分词,而是精确匹配,但es默认情况下是指定string类型的分词,所以在index创建之前我们需要手动指定相关列不需要分词:not_analyzed,形如:

  1. CURL -XPOST http://192.168.1.105:9200/my-index -d {

  2. {

  3. "mappings": {

  4. "my-type": {

  5. "properties": {

  6. "name": {

  7. "type": "string",

  8. "index": "not_analyzed"

  9. },

  10. "num": {

  11. "type": "string",

  12. "index": "not_analyzed"

  13. }

  14. }

  15. }

  16. }

  17. }

创建索引成功后,再使用elasticsearch-jdbc的脚本导入数据,相关数据列不会再使用分词分析,再使用term组合精确查询时,就可以查询相关数据来。

SpringBoot应用

pom.xml关键配置

  1. <dependency>

  2. <groupId>org.springframework.boot</groupId>

  3. <artifactId>spring-boot-starter-data-elasticsearch</artifactId>

  4. </dependency>

  5. <dependency>

  6. <groupId>org.springframework.boot</groupId>

  7. <artifactId>spring-boot-starter-web</artifactId>

  8. <exclusions>

  9. <exclusion>

  10. <artifactId>log4j-over-slf4j</artifactId>

  11. <groupId>org.slf4j</groupId>

  12. </exclusion>

  13. </exclusions>

  14. </dependency>

  15. <dependency>

  16. <groupId>org.springframework.boot</groupId>

  17. <artifactId>spring-boot-starter</artifactId>

  18. <exclusions>

  19. <exclusion>

  20. <groupId>org.springframework.boot</groupId>

  21. <artifactId>spring-boot-starter-logging</artifactId>

  22. </exclusion>

  23. </exclusions>

  24. </dependency>

  25. <dependency>

  26. <groupId>org.springframework.boot</groupId>

  27. <artifactId>spring-boot-starter-test</artifactId>

  28. <scope>test</scope>

  29. </dependency>

  30. <dependency>

  31. <groupId>org.springframework.boot</groupId>

  32. <artifactId>spring-boot-starter-log4j</artifactId>

  33. <version>1.3.1.RELEASE</version>

  34. </dependency>

与elasticsearch交互实体

  1. [@Data](https://my.oschina.net/difrik)

  2. @Document(indexName = "my-index", type = "my-type", shards = 5, replicas = 1, indexStoreType = "fs", refreshInterval = "-1")

  3. public class DataBean {

  4. /**

  5. * code:名称

  6. *

  7. * [@since](https://my.oschina.net/u/266547) JDK 1.6

  8. */

  9. public String name;

  10. /**

  11. * msg:编号

  12. *

  13. * [@since](https://my.oschina.net/u/266547) JDK 1.6

  14. */

  15. public String num;

  16. }

与es交互接口类,返回数据的唯一_id值,若查得数据表示命中数据,若为空并未数据不存在

  1. public interface DataBeanRepository extends ElasticsearchRepository<DataBean, Long> {

  2. //案例中并未使用,但可以用

  3. public List<BlackGreyData> findByNameAndNum(String name, String num);

  4. }

下面是业务处理层,采用BoolQueryBuilder构建查询条件,也即可基于DSL模块查询数据,还可以采用Criteria查询。

  1. @Autowired

  2. DataBeanRepository repository;

  3. @Override

  4. public List<DataBean> query(String name, String num, String type) {

  5. //采用过滤器的形式,提高查询效率

  6. BoolQueryBuilder builder = QueryBuilders.boolQuery();

  7. builder.must(QueryBuilders.termQuery("name", name)).must(QueryBuilders.termQuery("num", num));

  8. Iterable<DataBean> lists = repository.search(builder);

  9. List<DataBean> datas = new ArrayList<>();

  10. for (DataBean dataBean : lists) {

  11. datas.add(dataBean);

  12. logger.info("---------------------->>>Request result = 【" + dataBean + "】");

  13. }

  14. return datas;

  15. }

其它再编写对应的请求响应逻辑,即可完成简单服务的完成。

测试结果

GPS数据量5000W+,精确匹配查询出来50条数据,耗时700ms左右,结果查询缓存机制,基本可以稳定在300ms左右。这也是在单节点,未作任何优化的情况的结果。

源码地址

https://github.com/backkoms/spring-boot-elasticsearch

Spring Boot + Elasticsearch实现大批量数据集下中文的精确匹配

扩展阅读:

歪脖贰点零  ∣ 认知升级· 终身学习

Spring Boot + Elasticsearch实现大批量数据集下中文的精确匹配

程序员,除了编码,生活还应该有沉淀!

长按,识别二维码,加关注

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这