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

Stella981
• 阅读 545

缘由

数据存储在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实现大批量数据集下中文的精确匹配

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

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

点赞
收藏
评论区
推荐文章
浅梦一笑 浅梦一笑
2个月前
初学 Python 需要安装哪些软件?超级实用,小白必看!
编程这个东西是真的奇妙。对于懂得的人来说,会觉得这个工具是多么的好用、有趣,而对于小白来说,就如同大山一样。其实这个都可以理解,大家都是这样过来的。那么接下来就说一下python相关的东西吧,并说一下我对编程的理解。本人也是小白一名,如有不对的地方,还请各位大神指出01名词解释:如果在编程方面接触的比较少,那么对于软件这一块,有几个名词一定要了解,比如开发环
技术小男生 技术小男生
2个月前
linux环境jdk环境变量配置
1:编辑系统配置文件vi /etc/profile2:按字母键i进入编辑模式,在最底部添加内容: JAVAHOME/opt/jdk1.8.0152 CLASSPATH.:$JAVAHOME/lib/dt.jar:$JAVAHOME/lib/tools.jar PATH$JAVAHOME/bin:$PATH3:生效配置
光头强的博客 光头强的博客
2个月前
Java面向对象试题
1、 请创建一个Animal动物类,要求有方法eat()方法,方法输出一条语句“吃东西”。 创建一个接口A,接口里有一个抽象方法fly()。创建一个Bird类继承Animal类并实现 接口A里的方法输出一条有语句“鸟儿飞翔”,重写eat()方法输出一条语句“鸟儿 吃虫”。在Test类中向上转型创建b对象,调用eat方法。然后向下转型调用eat()方
Jacquelyn38 Jacquelyn38
1年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。 1、使用解构获取json数据let jsonData   id: 1, status: "OK", data: ['a', 'b'] ; let  id, status, data: number   jsonData; console.log(id, status, number )
刚刚好 刚刚好
2个月前
css问题
1、 在IOS中图片不显示(给图片加了圆角或者img没有父级) <div<img src""/</div div {width: 20px; height: 20px; borderradius: 20px; overflow: h
blmius blmius
1年前
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:SQL Mode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。 全局s
晴空闲云 晴空闲云
2个月前
css中box-sizing解放盒子实际宽高计算
我们知道传统的盒子模型,如果增加内边距padding和边框border,那么会撑大整个盒子,造成盒子的宽度不好计算,在实务中特别不方便。boxsizing可以设置盒模型的方式,可以很好的设置固定宽高的盒模型。 盒子宽高计算假如我们设置如下盒子:宽度和高度均为200px,那么这会这个盒子实际的宽高就都是200px。但是当我们设置这个盒子的边框和内间距的时候,那
艾木酱 艾木酱
1个月前
快速入门|使用MemFire Cloud构建React Native应用程序
> MemFire Cloud是一款提供云数据库,用户可以创建云数据库,并对数据库进行管理,还可以对数据库进行备份操作。它还提供后端即服务,用户可以在1分钟内新建一个应用,使用自动生成的API和SDK,访问云数据库、对象存储、用户认证与授权等功能,可专
Wesley13 Wesley13
1年前
cobol学习之十数据库的增删改查模板
这次连接数据库使用的是ODBC连接access数据库,里面主要是一个增删改查的模板备份,方便以后查询。 000001 IDENTIFICATION DIVISION. 000002 PROGRAM-ID. SAMPLEDB2. 00
helloworld_34035044 helloworld_34035044
4个月前
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。 uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid() 或 uuid(sep)参数说明:sep 布尔值,生成的uuid中是否包含分隔符'',缺省为
helloworld_28799839 helloworld_28799839
2个月前
常用知识整理
# Javascript ## 判断对象是否为空 ```js Object.keys(myObject).length === 0 ``` ## 经常使用的三元运算 > 我们经常遇到处理表格列状态字段如 `status` 的时候可以用到 ``` vue