Springboot整合Elasticsearch、搭建Logstash同步数据

简介: 个人学习笔记——ES

整合Springboot

通过虚拟机搭建ES,这里使用的版本是6.4.3,引入相应依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    # 使用2.2.2是为了对应ES的版本
    <version>2.2.2.RELEASE</version>
</dependency>

引入依赖之后可以自行查看

spring:
  data:
    elasticsearch:
      # 在es中配置的名称
      cluster-name: es
      # 如果是集群,用,分隔
      cluster-nodes: 192.168.1.7:9300

测试实体类

package com.csea.entity;

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;

/**
 * @author Csea
 * @title
 */
@Data
@Document(indexName = "merchant", type = "doc")
public class Merchant {

    @Id
    private Long merchantId;
    // store = true 表示这是要存储的字段
    @Field(store = true)
    private String name;

    @Field(store = true)
    private String mob;

    @Field(store = true)
    private String address;

    @Field(store = true)
    private String descr;
}

创建索引(文档)

当索引不存在时候,会先创建索引,并将数据插入。

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;
    @Test
    public void createIndex() {
        Merchant merchant = new Merchant();
        merchant.setMerchantId(1001L);
        merchant.setName("Csea-杂货铺1");
        merchant.setAddress("大道8888号");
        merchant.setDescr("好吃的不得了");
        merchant.setMob("13899999999");
        IndexQuery indexQuery = new IndexQueryBuilder().withObject(merchant).build();
        elasticsearchTemplate.index(indexQuery);
    }

执行完之后可以看到merchant索引已经创建,并且文档也新增了。

更新文档

    @Test
    public void updateMerchantDoc() {
        Map<String, Object> sourceMap = new HashMap<>();
        sourceMap.put("name", "Csea-杂货铺-update");
        sourceMap.put("adress", "光明大道8888号");
        sourceMap.put("descr", "好吃你就多吃点!~");
        IndexRequest indexRequest = new IndexRequest();
        indexRequest.source(sourceMap);

        UpdateQuery query = new UpdateQueryBuilder().withClass(Merchant.class)
                .withId("1001")
                .withIndexRequest(indexRequest)
                .build();
        elasticsearchTemplate.update(query);
    }

查询文档

    @Test
    public void queryMerchantDoc() {
        GetQuery query = new GetQuery();
        query.setId("1001");
        Merchant merchant = elasticsearchTemplate.queryForObject(query, Merchant.class);
        log.info("查询到的数据是={}", merchant);
    }
com.test.ESTest : 查询到的数据是=Merchant(merchantId=1001, name=Csea-杂货铺-update, mob=13899999999, address=大道8888号, descr=好吃你就多吃点!~)

删除文档数据

    @Test
    public void delMerchantDoc() {
        elasticsearchTemplate.delete(Merchant.class, "1001");
    }

分页文档查询

    @Test
    public void searchPageMerchantDoc() {

        Pageable pageable = PageRequest.of(0, 20);

        SearchQuery query = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchQuery("description", "God Engineer "))
                .withPageable(pageable)
                .build();
        AggregatedPage<Merchant> merchants = elasticsearchTemplate.queryForPage(query, Merchant.class);
        List<Merchant> content = merchants.getContent();
        for (Merchant merchant : content) {
            log.info("查询到的文档为:{}", merchant);
        }

    }

高亮分页查询

    @Test
    public void searchHightMerchantDoc() {

        String preTag = "<font color='red'>";
        String postTg = "</font>";

        Pageable pageable = PageRequest.of(0, 20);
        // 排序
        SortBuilder sortBuilder = new FieldSortBuilder("name")
                .order(SortOrder.DESC);


        SearchQuery query = new NativeSearchQueryBuilder()
                // 查询的字段
                .withQuery(QueryBuilders.matchQuery("mob", "88"))
                .withHighlightFields(new HighlightBuilder.Field("mob")
                        .preTags(preTag)
                        .postTags(postTg))
                .withSort(sortBuilder)
                .withPageable(pageable)
                .build();
        AggregatedPage<Merchant> merchants = elasticsearchTemplate.queryForPage(query, Merchant.class,
                new SearchResultMapper() {
                    @Override
                    public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {
                        SearchHits hits = response.getHits();

                        List<Merchant> stuList = new ArrayList<>();
                        for (SearchHit hit : hits) {
                            HighlightField highlightField = hit.getHighlightFields().get("mob");
                            String mob = highlightField.getFragments()[0].toString();

                            Object merchantId = hit.getSourceAsMap().get("merchantId");
                            String name = (String) hit.getSourceAsMap().get("name");
                            String descr = (String) hit.getSourceAsMap().get("descr");
                            String address = (String) hit.getSourceAsMap().get("address");

                            Merchant merchantHL = new Merchant();
                            merchantHL.setDescr(descr);
                            merchantHL.setMerchantId(Long.valueOf(merchantId.toString()));
                            merchantHL.setName(name);
                            merchantHL.setMob(mob);
                            merchantHL.setAddress(address);
                            stuList.add(merchantHL);

                        }

                        if (!stuList.isEmpty()) {
                            return new AggregatedPageImpl<>((List<T>) stuList);
                        }

                        return null;
                    }
                });
        log.info("分页总数:{}", merchants.getTotalPages());
        List<Merchant> content = merchants.getContent();
        for (Merchant merchant : content) {
            log.info("分页文档数据:{}", merchant);
        }

    }

删除索引

    @Test
    public void deleteDelMerchantIndex() {
        elasticsearchTemplate.deleteIndex(Merchant.class);
    }

小结

不建议使用ElasticsearchTemplate对索引进行管理(创建索引、更新映射、删除索引),因为就像使用Mysql,不会通过java代码去改变表结构,ES也是如此,更多是用来进行CRUD操作。

Logstash

概念

  1. 作为数据采集的工具;
  2. 以id或update_time作为同步边界;

以id同步:初次同步的时候会将所有数据同步过来,之后logstash的定时任务回去检查,比如上次同步到id为2000的数据,那么这次就同步2000之后的数据,使用id作为同步有很大的弊端,就只只能新增数据,无法更新。
以update_time同步:初次同步就将所有的数据同步过来,之后如果有新增或更新的操作,那么就把以上一次更新时间为界,之后的数据全部做一次新增或更新。

  1. 使用logstash-input-jdbd插件同步;
  2. 使用logstash需要跟ES的版本保持一致,比如两者都要是6.4.3版本;
  3. 同步数据时,需要事先创建索引。

安装配置

logstash下载地址
上传并解压,再上传mysql的驱动jar包,以及jdk(logstash需要jdk)

cd进入logstash目录之后,创建文件夹

mkdir sync

进入sync目录,创建配置文件

# 先创建同步配置文件
vim logstash-db-sync.conf
# 拷贝数据库驱动到该路径下
cp /usr/local/mysql-connector-java-8.0.13.jar .

修改logstash-db-sync.conf

input {
    jdbc {
        # 设置Mysql/MariaDB 数据库url以及数据库名称
        jdbc_connection_string => "jdbc:mysql://192.168.1.6:3306/xxg?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false"
        # 数据库用户名密码
        jdbc_user => "root"
        jdbc_password => "123456"
        # 数据库驱动所在位置,可以是绝对路径或相对路径
        #jdbc_driver_library => "/usr/local/logstash-6.4.3/sync/mysql-connector-java-5.1.49.jar"
        jdbc_driver_library => "/usr/local/logstash-6.4.3/sync/mysql-connector-java-8.0.13.jar"
        # 驱动类名
        #jdbc_driver_class => "com.mysql.jdbc.Driver"
        jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
        # 开启分页
        jdbc_paging_enabled => "true"
        # 分页每页数量,可以自定义
        jdbc_page_size => "10000"
        # 执行的sql文件路径
        statement_filepath => "/usr/local/logstash-6.4.3/sync/product.sql"
        # 设置定时任务间隔
        schedule => "* * * * *"
        # 索引类型
        type => "_doc"
        # 是否开启记录上次追踪的结构,也就是上次更新的实际,这个会记录到 last_run_metadata_path 的文件
        use_column_value => true
        # 记录上一次追踪的结果值
        last_run_metadata_path => "/usr/local/logstash-6.4.3/sync/track_time"
        # 如果 use_column_value 为true,配置本参数,追踪的colume名,可以是自增id或者时间
        tracking_column => "update_time"
        # tracking_column 对应字段的类型
        tracking_column_type => "timestamp"
        # 是否清除 last_run_metadata_path 的记录,true则每次都从头开始查询所有
        clean_run => false
        # 数据库字段名称大写转小写
        lowercase_column_names => false
    }
}

output {
    elasticsearch {
        # es地址
        hosts => ["192.168.1.7:9200"]
        # 同步索引名
        index => "product"
        # 设置_docID和数据相同,按照执行sql中的字段来
        document_id => "%{product_id}"
    }
    # 日志输出
    stdout {
        codec => json_lines
    }
}

添加执行同步查询的sql脚本

vim product.sql
SELECT
    * 
FROM
    jh_product 
WHERE
    is_delete = 0 
    AND update_time >= :sql_last_value

:sql_last_value是logstash中要赋值的地方,可以理解为占位符

启动logstash,先cd到目录下的bin目录

./logstash -f /usr/local/logstash-6.4.3/sync/logstash-db-sync.conf

启动之后就可以看到数据同步过去,在ES的索引中查看数据

自定义模板

因为需要用到中文分词,所以需要自定义模板
Get:http://{IP}:{port}/_template/logstash
查看模板信息

将拿到的template复制出来进行自定义修改

{
    "order": 10,
    "version": 1,
    "index_patterns": ["*"],
    "settings": {
        "index": {
            "refresh_interval": "5s"
        }
    },
    "mappings": {
        "_default_": {
            "dynamic_templates": [
                {
                    "message_field": {
                        "path_match": "message",
                        "match_mapping_type": "string",
                        "mapping": {
                            "type": "text",
                            "norms": false
                        }
                    }
                },
                {
                    "string_fields": {
                        "match": "*",
                        "match_mapping_type": "string",
                        "mapping": {
                            "type": "text",
                            "norms": false,
                            "analyzer": "ik_max_word",
                            "fields": {
                                "keyword": {
                                    "type": "keyword",
                                    "ignore_above": 256
                                }
                            }
                        }
                    }
                }
            ],
            "properties": {
                "@timestamp": {
                    "type": "date"
                },
                "@version": {
                    "type": "keyword"
                },
                "geoip": {
                    "dynamic": true,
                    "properties": {
                        "ip": {
                            "type": "ip"
                        },
                        "location": {
                            "type": "geo_point"
                        },
                        "latitude": {
                            "type": "half_float"
                        },
                        "longitude": {
                            "type": "half_float"
                        }
                    }
                }
            }
        }
    },
    "aliases": {}
}

创建完毕之后,在logstash目录的sync下创建新的json文件

vim logstash-ik.json

将自定义模板内容放入json文件中

然后修改logstash-db-sync.conf 配置,在output中添加如下配置,保存之后,重启logstash即可

output {
    elasticsearch {
        # 定义模板名称
        template_name => "myik"
        # 模板所在位置
        template => "/usr/local/logstash-6.4.3/sync/logstash-ik.json"
        # 重写模板
        template_overwrite => true
        #默认为true,false关闭logstash自动管理模板功能,如果自定义模板,则设置false
        manage_template => false
    }
}

中文分词不生效

如果出现中文分词"analyzer": "ik_max_word"不生效的情况可以先将manage_template改为true,然后启动logstash同步,同步之后,使用:
Get:http://{IP}:{port}/_template/{template名称}
来查看模板是否已经上传到es

同步之后将manage_template改为false,重新同步即可。
主要就是模板没有被上传到ES导致的。
另外可以修改模板中order的值,值越大,在 merge 规则的时候优先级越高。

相关实践学习
以电商场景为例搭建AI语义搜索应用
本实验旨在通过阿里云Elasticsearch结合阿里云搜索开发工作台AI模型服务,构建一个高效、精准的语义搜索系统,模拟电商场景,深入理解AI搜索技术原理并掌握其实现过程。
ElasticSearch 最新快速入门教程
本课程由千锋教育提供。全文搜索的需求非常大。而开源的解决办法Elasricsearch(Elastic)就是一个非常好的工具。目前是全文搜索引擎的首选。本系列教程由浅入深讲解了在CentOS7系统下如何搭建ElasticSearch,如何使用Kibana实现各种方式的搜索并详细分析了搜索的原理,最后讲解了在Java应用中如何集成ElasticSearch并实现搜索。 &nbsp;
相关文章
存储 JSON Java
1022 0
|
JSON Java 数据格式
Spring Boot返回Json数据及数据封装
在Spring Boot中,接口间及前后端的数据传输通常使用JSON格式。通过@RestController注解,可轻松实现Controller返回JSON数据。该注解是Spring Boot新增的组合注解,结合了@Controller和@ResponseBody的功能,默认将返回值转换为JSON格式。Spring Boot底层默认采用Jackson作为JSON解析框架,并通过spring-boot-starter-json依赖集成了相关库,包括jackson-databind、jackson-datatype-jdk8等常用模块,简化了开发者对依赖的手动管理。
925 3
|
JSON Java 数据格式
微服务——SpringBoot使用归纳——Spring Boot返回Json数据及数据封装——封装统一返回的数据结构
本文介绍了在Spring Boot中封装统一返回的数据结构的方法。通过定义一个泛型类`JsonResult&lt;T&gt;`,包含数据、状态码和提示信息三个属性,满足不同场景下的JSON返回需求。例如,无数据返回时可设置默认状态码&quot;0&quot;和消息&quot;操作成功!&quot;,有数据返回时也可自定义状态码和消息。同时,文章展示了如何在Controller中使用该结构,通过具体示例(如用户信息、列表和Map)说明其灵活性与便捷性。最后总结了Spring Boot中JSON数据返回的配置与实际项目中的应用技巧。
1103 0
|
JSON Java fastjson
微服务——SpringBoot使用归纳——Spring Boot返回Json数据及数据封装——使用 fastJson 处理 null
本文介绍如何使用 fastJson 处理 null 值。与 Jackson 不同,fastJson 需要通过继承 `WebMvcConfigurationSupport` 类并覆盖 `configureMessageConverters` 方法来配置 null 值的处理方式。例如,可将 String 类型的 null 转为 &quot;&quot;,Number 类型的 null 转为 0,避免循环引用等。代码示例展示了具体实现步骤,包括引入相关依赖、设置序列化特性及解决中文乱码问题。
822 0
|
JSON Java fastjson
微服务——SpringBoot使用归纳——Spring Boot返回Json数据及数据封装——Spring Boot 默认对Json的处理
本文介绍了在Spring Boot中返回Json数据的方法及数据封装技巧。通过使用`@RestController`注解,可以轻松实现接口返回Json格式的数据,默认使用的Json解析框架是Jackson。文章详细讲解了如何处理不同数据类型(如类对象、List、Map)的Json转换,并提供了自定义配置以应对null值问题。此外,还对比了Jackson与阿里巴巴FastJson的特点,以及如何在项目中引入和配置FastJson,解决null值转换和中文乱码等问题。
1891 0
|
前端开发 Cloud Native Java
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
|
Java 关系型数据库 MySQL
SpringBoot 通过集成 Flink CDC 来实时追踪 MySql 数据变动
通过详细的步骤和示例代码,您可以在 SpringBoot 项目中成功集成 Flink CDC,并实时追踪 MySQL 数据库的变动。
3701 45
|
前端开发 Java API
SpringBoot整合Flowable【06】- 查询历史数据
本文介绍了Flowable工作流引擎中历史数据的查询与管理。首先回顾了流程变量的应用场景及其局限性,引出表单在灵活定制流程中的重要性。接着详细讲解了如何通过Flowable的历史服务API查询用户的历史绩效数据,包括启动流程、执行任务和查询历史记录的具体步骤,并展示了如何将查询结果封装为更易理解的对象返回。最后总结了Flowable提供的丰富API及其灵活性,为后续学习驳回功能做了铺垫。
1590 0
SpringBoot整合Flowable【06】- 查询历史数据
|
存储 前端开发 Java
SpringBoot整合Flowable【05】- 使用流程变量传递业务数据
本文介绍了如何使用Flowable的流程变量来管理绩效流程中的自定义数据。首先回顾了之前的简单绩效流程,指出现有流程缺乏分数输入和保存步骤。接着详细解释了流程变量的定义、分类(运行时变量和历史变量)及类型。通过具体代码示例展示了如何在绩效流程中插入全局和局部流程变量,实现各节点打分并维护分数的功能。最后总结了流程变量的使用场景及其在实际业务中的灵活性,并承诺将持续更新Flowable系列文章,帮助读者更好地理解和应用Flowable。 简要来说,本文通过实例讲解了如何利用Flowable的流程变量功能优化绩效评估流程,确保每个环节都能记录和更新分数,同时提供了全局和局部变量的对比和使用方法。
1521 0
SpringBoot整合Flowable【05】- 使用流程变量传递业务数据
|
弹性计算 运维 Serverless
超值选择:阿里云Elasticsearch Serverless在企业数据检索与分析中的高性能与灵活性
本文介绍了阿里云Elasticsearch Serverless服务的高性价比与高度弹性灵活性。
820 8