SpringBoot结合Elasticsearch实现个性化搜索推荐模型-01

前言: 到底什么是搜索引擎?

对于我们传统的应用来说, 常见的搜索无非是用户通过前端发起请求给后端, 后端去DataBase中比如MySQL, Oracle 中完成查询,并返回,这个查询可能是精确匹配,模糊匹配等等,但是这样的搜索功能可以被称之为搜索引擎吗? 或者说合格的搜索引擎,显然是不能的,因为在一些特殊情况下,我们单纯基于DataBase的搜索召回过于局限了,举个例子,比如现在数据库中有一些关于国家的信息,比如中华人民共和国,美国,法国等等,现在用户就是输入了一个中国,那么传统的关系型数据如何匹配到中华人民共和国呢,无论是精确匹配还是模糊匹配都不可能搜索到结果,除非我们专门做了一层映射关系,让系统知道它应该查找哪条数据,当然这显然也是不合理的,因为我们的数据量可能是非常庞大的,这种办法是根本不可靠,还有常见的排序问题,关系型数据库中的orderBy操作也有一定的局限性,过于死板,因此,搜索引擎的介入就是为了以一种更加优雅的方式来解决我们常见关系型数据库当中查询召回的一些痛点,常见的搜索引擎有ElasticSearch, Solr, 本文章主要以ElasticSearch来展开

第一章

ElasticSearch是什么

  1. Elasticsearch 是一个分布式的开源搜索和分析引擎,适用于所有类型的数据,包括文本、数字、地理空间数据等。它能够提供近乎实时的搜索和分析功能。Elasticsearch 常被用于解决各种搜索和数据分析问题,例如日志分析、全文检索、以及需要高性能和高扩展性的复杂查询场景。
  2. Elasticsearch 是基于 Lucene 开发的,但它的目标是更容易地实现分布式搜索和索引,提供一个更为高级的结构化搜索接口以及一个可扩展的系统来处理大量数据。它还提供了 RESTful 接口,使得开发者可以方便地通过 HTTP 协议用 JSON 数据格式进行交互。

ElasticSearch的一些名词定义

ElasticSearch中的索引 = 数据库
ElasticSearch中的类型 = 表
ElasticSearch中的文档 = 数据库表中的行数据

ElasticSearch中一些关键词概念与传统关系型数据库的对应关系

Relational DatabaseElasticSearch
DataBaseIndex
TableType
RowDocument
ColumnField
SchemaMapping
IndexEverything Index
SQLQuery DSL
SELECT * FROM TABLEGET http://…
UPDATE TABLE SETPUT http://

ElasticSearch的一些特点

  1. 分布式:ElasticSearch 可以部署在一个或多个服务器上,形成一个集群,能够处理大量的数据和流量
  2. 实时性:数据可以在短时间内被索引并供搜索使用
  3. 可扩展性:随着数据量的增长,可以通过添加更多的节点来扩展 ElasticSearch 集群
  4. 高可用性:通过复制数据到多个节点上,即使某个节点失败也能保证服务的可用性
  5. 丰富的 API:提供多种语言的客户端库,以及易于使用的 RESTful API, 由于支持 RESTful 风格的方式进行访问,也就是基于HTTP的方式, 让它可以几乎可以跟任何代码开发的应用进行交互

ElasticSearch的基本原理是什么? 什么是倒排索引?

  1. Elasticsearch 被设计为可分布式的,这意味着它可以跨多个节点运行,每个节点都是集群的一部分。集群中的每个节点都可以包含一个或多个索引,索引可以被复制和分割成多个分片来提高可用性和性能
  2. Elasticsearch 使用倒排索引来加速搜索。传统的正向索引是从文档到词的映射,而倒排索引则是从词到文档的映射。这意味着对于给定的查询词,Elasticsearch 可以迅速找到包含该词的所有文档。这个具体意思是什么后面我会画图来解释。
  3. Elasticsearch 中的数据是以 JSON 格式存储的文档。每个文档都属于一个特定的索引,索引可以包含多种类型的文档。不过,在 Elasticsearch 6.x 及更高版本中,类型的概念已被弃用,现在索引中只能有一种类型,默认为"_doc"。
  4. 当执行搜索请求时,请求会发送到主节点,主节点将请求广播到相关的分片所在的节点上。每个节点会处理请求然后返回结果给主节点,主节点汇总这些结果后返回给客户端。这里也看到了,ElasticSearch的多节点主从架构和类似Redis的多节点主从架构是有一些区别,ElasticSearch的主节点是不存储数据的, 只负责集群分片的维护, 告诉你数据应该去哪个分片上找, 同时也负责索引的创建/删除/分片分配/节点状态管理等操作, 这和Redis集群的主节点是不一样的, Redis主节点也会写入数据, 还有同步给从节点, 这是有区别的
  5. 为了提高容错性和可用性,Elasticsearch 允许创建索引的副本。如果一个节点失败了,另一个节点上的副本可以接管请求。此外,大的索引会被分成多个分片来分散负载和提高检索速度。
  6. 所有的功能都可以通过 HTTP 请求与响应来访问。这意味着你可以使用任何语言来与 Elasticsearch 进行交互。
  7. Elasticsearch 支持实时插入、更新和删除文档,并且可以在数据写入的同时进行查询。
  8. Elasticsearch 设计为易于水平扩展,即可以通过添加更多的节点来增加系统的存储能力和处理能力。
  9. Elasticsearch 经常与 Kibana 结合使用,后者是一个用于可视化 Elasticsearch 数据的强大工具。Kibana也是ElasticSearch最常见的一个搭配组件中的一个, 也就是ELK中的K,L是什么后面再说

倒排索引的解析

在了解倒排索引的基础上,我们先要了解一下正排索引
正排索引:

  1. 假设我们现在有若干篇文档,每篇文档内容都不一样,对应也分出了若干个词, 现在我们输入了一个“中国”,那么对于正排索引,它会怎么搜索呢? 它会从头到尾依次遍历文档,直到找出哪篇文档当中出现了我要搜索的词, 这里的问题就在于,乐观的情况下,比如中国这个词正好就只出现在了第一篇文档当中,一次就查到了,不理想的情况,我可能要遍历完所有的文档才能找到结果,比如下图可能doc1 doc2 doc3都没找到我要的内容,那就比较糟糕了,只能继续往下遍历了
  2. 在这里插入图片描述

倒排索引:
1.倒排索引的设计思想是说,既然正排搜索是文档到词的映射,那么我可以建立一个词到文档的映射,也就是说在建立索引的时候,中国这个词就相当于一个key,与之对应的就是,哪些文档中出现过中国,那么这个时候显而易见,我可以直接根据词把对应的文档找出来,比如下图,找到了word1和word2关联的文档有中国这个词,不需要在再逐个去遍历文档了,这当中的效率的效率提升是非常高的
2.在这里插入图片描述

ElasticSearch的分词概念(倒排索引的构建也是依赖分词)

  1. 在 Elasticsearch 中,分词是文本处理的重要组成部分,尤其是在建立索引和搜索过程中。分词是指将文档中的文本分解成更小单元——通常是单词或短语,这些单元被称为“tokens”。分词器是负责将输入文本转换为 tokens 的组件,比如ik分词器(专门为中文文本处理设计的一种分词器), 举个例子,现在我们有一个字符串"小明是我的同学", 那么对应分词器可能被分为 小明,是,我,我的,同学这几个词,当然具体的分词粒度是可以设置的,比如最大粒度的切分,最小粒度的切分, 就是说尽最大可能性拆分,比如直接拆成一个字一个字的,或者尽可能的满足语义的拆分
  2. 分词并没有最好的方式,只有更加适合业务的方式,比如设置ik分词器的分词方式为ik_max_word,虽然搜索召回的可能性比较高,任意一个词命中了都会返回,但是召回的数据不一定是用户真正想要的,如果设置ik分词器的分词方式为ik_smart,那么虽然拆分的词,精确度更好了,但是召回的可能性会下降,不是很贴近语义的输入就会导致无法命中分词
  3. 那有没有一种好的方式我既要分词最大化,并且我还要保证搜索时精确度呢,当然是有的,最简单的做法就是我们将ik_max_word和ik_smart结合使用,什么意思呢? 就是说我们在设置分词构建索引的时候设置为ik_max_word, 但是在搜索解析时我们设置为ik_smart,这样可以利用 ik_max_word 的高召回特性,同时利用 ik_smart 的高精确度特性。

ElasticSearch中TF-IDF打分的策略

  1. Term Frequency (TF):词频,表示一个词在文档中出现的频率。TF 越高,说明该词在文档中的重要性越高。
  2. Inverse Document Frequency (IDF):逆文档频率,表示一个词在整个文档集合中的重要性。IDF 越高,说明该词越罕见,因此越重要。
  3. TF很好理解, 那么IDF是什么意思呢? 总的来说,可以理解为稀有度,也就是说虽然这个词,在整个文档当中只出现了一次,但是它的IDF值就会很高,它很稀有
  4. 一个词如果在一个文档中出现得很少,但只要它在整个文档集合中出现得非常频繁,它的 IDF 值仍然会很低。相反,如果一个词在一个文档中出现得很少,但在整个文档集合中也非常罕见,那么它的 IDF 值会很高。
  5. Elasticsearch 中关于 TF-IDF 的打分公式可以简化为:Score = TF×IDF, 一旦相乘的结果分数较高,那么它将会被优先推荐出来

ElasticSearch以及Kibana的安装以及部署

  1. 下载链接: ElasticSearch7.3.0
  2. 下载链接: Kibana7.3.0
  3. ELK所有的下载版本最好是相同的, ElasticSearch官方本身在发布的时候都是同版本发布的,也就是说ElasticSearch是什么版本,Kibana就是什么版本的
  4. 启动ElasticSearch检查启动状态 http://127.0.0.1:9200/_cat/health
  5. 启动Kibana
  6. 在这里插入图片描述
  7. Kibana就是一个与ElasticSearch一起工作的可视化界面,它可以用来搜索、查看和分析存储在Elasticsearch索引中的数据,并通过各种图表、表格和地图等形式直观地展示数据,帮助用户更好地理解和分析其数据集

ElasticSearch的分布式原理

  1. 在我们第一次单机部署ElasticSerach时,/_cat/health 检查健康状态时,会发现时green状态,但是如果我们向节点中添加一条索引记录,再次检查,将会发现状态变为了 yellow,这是为什么呢?
    在这里插入图片描述
  2. 当我们在创建索引时(分片是基于索引的),如果没有指定主分片和从分片,那么ElasticSearch会默认帮我们创建一主一从,也就是Primaries:1. Replicas:1
  3. 当我们在单节点部署时,这个单节点本身就是Master节点, 并且由于是单机部署的,导致默认帮我们创建的从分片也在同一台机器上,也导致了健康状态变成了yellow,原因正是因为主从都在一个节点上,这是不符合高可用架构设计的,总结来说,在单节点环境中部署ElasticSearch并不适合生产环境,因为它缺乏冗余和容错能力。对于生产级别的应用,建议至少使用三个节点来构建集群,以确保数据的安全性和服务的连续性
  4. 即使我们在创建索引的时候将从分片设置为0,虽然看似状态恢复为了green,实际上还是不满足高可用,因为一旦机器挂掉了,那么整个ElasticSearch的服务将会彻底不可用了
  5. 在这里插入图片描述
  6. 在这里插入图片描述
  7. 因此多节点(>=3)的部署方式才是更好的做法
  8. 在创建索引时,主分片和从分片的数量并不固定,但是注意的事项在于从分片可以在创建完索引后修改数量,而主分片不可以,因此主分片在初次创建时就要尽可能的多预留一些,当然也并不是无脑加,如果本身数据量并没有达到一定级别,反而创建了很多的主分片,那么在做数据聚合的时候,就不得不遍历所有的主分片才能拿到我们最终想要的结果了
  9. 那么如果未来发现我确实需要增加主分片的数量怎么办呢? 一个比较好的做法就是数据迁移, 就是说我们可以再创建一个索引,新建的索引将主分片设置大一些,然后将现有分片的数据同步过去
  10. 这里注意一下,主从分片的概念可不是主从节点,维度是不一样的,尽管“主从分片”和“主从节点”听起来类似,但实际上它们描述的是集群中不同层次的概念。分片指的是索引内部的数据组织方式,而节点则指的是集群中物理或虚拟的计算单元的角色。主节点只能有一个,它负责整个集群的元数据更改,如创建/删除索引、跟踪哪些分片位于哪些节点等。
  11. ElasticSearch它可以自动的进行负载均衡操作,当一个节点加入集群,Elasticsearch 会自动地重新分配分片(包括主分片和副本分片)到新的节点上,以达到更好的负载均衡。这种重新分配是为了确保集群中的工作负载可以均匀地分布在所有可用节点上。
  12. 健康的集群状态是说我任何一台机器挂掉,第一我的服务能由其他节点选举后继续充当Master来提供服务,第二我的主从分片散落均匀,数据不会丢失,当然这些是理想的状态,极端情况下也可能数据丢失,比如说两个节点同时故障,而每个分片只有一个副本,那么数据将无法恢复,这里也建议实际应该定期对索引数据进行备份,保证极端情况能够快速恢复
  13. 一次写操作可能被分给任何一个节点,但是如果操作被分配给了从节点,那么从节点会再转发给主节点,因为只有主节点能完整的掌握整个集群的信息,主节点是负责管理所有的写请求的,再由主节点转发给真正的目的地节点,当目的地节点的主分片将此次写请求完成后,此次操作就完成了,会告诉主节点OK了, 但是它还会有一步操作,就是异步的将数据同步给它的从分片
  14. 读请求是可以由任何一个节点进行路由转发的,并不是强制要经过Master

ElasticSearch集群搭建

  1. 在进行集群搭建的时候最好是将压缩包重写解压生成一个干净的文件,而不是copy之前已经解压好的文件
  2. 修改ElasticSearch的配置文件,也就是elasticserach.yml
# 修改集群的名称 统一的 就是你的集群叫什么
cluster.name: xxx
# 每一个节点在集群中应该有自己唯一的名字 比如节点一就叫node1 节点二就叫node2 以此类推
node.name: node-1
# 这个节点启动在哪个ip上 如果是伪分布式那么就写127.0.0.1 就是说一台机器部署了三个节点
network.host: 192.xxx.xxx.xxx
# 启动端口号 多节点启动端口也需要修改比如9201 9202
http.port: 9200
# 内部集群之间通信的端口号 后续节点改成 9301 9302就可以了
transport.tcp.port: 9300
# 允许跨域请求
http.cors.enabled: true
http.cors.allow-origin: "*"
# 配置集群的通信ip节点
discovery.seed_hosts: ["192.xxx.xxx.xxx:9300", "192.xxx.xxx.xxx:9301", "192.xxx.xxx.xxx:9302"]
# 初始化有资格竞选master的节点
cluster.initial_master_nodes: ["192.xxx.xxx.xxx:9300", "192.xxx.xxx.xxx:9301", "192.xxx.xxx.xxx:9302"]
  1. 分别启动对应的ElasticSearch即可
  2. 输入ip:9200/_cat/nodes检查即可

ElasticSearch的基本语法以及应用

  1. 对应简单的创建/更新/删除索引就不说了, PUT/DELETE 后面跟随想要操作的内容即可,ElasticSearch的函数是非常多, 是根本说不完的
  2. 对于不存在的记录PUT操作是新增,对于已存在的记录,PUT操作是修改
  3. 每次修改操作对应的_version都会加1操作在这里插入图片描述
索引复杂查询
  1. 带关键字的查询
    在这里插入图片描述
    这里也看出来了, ElasticSearch搜索与传统DB的区别了, 我们输入了菜鸡,但是包含菜的文档都被查出来了,这是单纯的模糊匹配无法做到的
  2. 带排序字段的查询
    在这里插入图片描述
    这里可以发现对应score的打分变为null了, 这是因为我们使用了定制化的排序规则,所以默认的打分策略也就不生效了,会根据sort的大小进行优先级展示
  3. 带过滤条件的查询
    在这里插入图片描述
    这里可以看到当使用了filter之后. 对应的打分全部变成了0, 也就是并没有什么优先级的概念了,match操作会将我们的搜索输入,进行一个分词,然后再进行匹配,虽然我们输入的123菜鸟哈哈哈是根本没有创建过的数据,但是它也能被查出来,原因就是菜鸟这两个字被命中了,那么我们还可以将match关键字改为term, term的意思是说,我现在不要分词了,我输入什么你就直接拿来匹配,那么一旦换为了term, 势必是查不到任何数据的,如下图所示
    在这里插入图片描述
  4. 带聚合的查询可以理解为传统DB中的group by, 比如说我想知道不同年龄段对应的文档有多少个
    在这里插入图片描述
    最后的buckets中的意思就是说18岁对应的文档有4个,69岁对应的文档有一个

ElasticSearch的高级语法以及应用

应用中结合了ElasticSearch后一般数据是如何返回的
  1. 在实际结合ElasticSearch的应用中, 普遍并不是说直接将数据存储到了ElasticSearch中,而是一个同步->分析->返回给应用->查询DB->返回的过程, 也就是说先将DB中一些需要分析的数据同步给ElasticSearch, 再由ElasticSearch进行分析,最后将返回的结果给应用,应用再去数据库中查到对应的记录返回,这里就有问题了,为什么不是直接让拿ElasticSearch的数据进行返回呢,为什么要再查一遍数据库,其实这个就跟设计有关了,当一些表字段很多的时候,对于不影响分析结果的字段没有必要同步给ElasticSearch,但是最后我们返回给前端的时候是需要的,所以我们会再次查询一次数据库,当然这样的查询不见得就慢,因为我们可以将数据库主键作为一个标识,由于聚簇索引的缘故,所以查询的速度依然会非常快
  2. 分析的过程也可以理解为分词的过程,包括创建索引时的分词,查询时的分词
  3. 至于如何将DB的数据同步给ElasticSearch, 官方也提供了对应的组件, ELK中的L,Logstash,专门用于同步数据给ElasticSearch,不过Logstash的同步更多的是全量同步,而不是增量同步,虽然我们可以设计一些规则,比如按照时间戳的方式进行增量同步,但是这样的增量同步存在一些隐藏问题,比如实时性的问题
analyze的分析过程,ElasticSearch到底是如何帮我们分词的呢?
  1. 之前我们说过,在创建索引的时候,可以指定分词的策略,默认是standard,也可以指定别的分词策略
  2. 可以使用关键字_analyze来分析分词的结果
    在这里插入图片描述
  3. 可以看到ElasticSearch的标准分词器将我们的这一段文本拆分为了不同的词,那么在查询的时候同样的道理,在加入了match等关键字的时候,ElasticSearch也会帮我们将请求的字符串进行拆分,一旦请求的字符串拆分后的内容与本身索引拆分的内容有任何的交集,那么对应的数据就可以被返回
  4. 注意看, ElasticSearch在使用默认分词器解析的时候,默认会进行大小写转化,可以看到语句中的大写字符被转为了小写, 比如我们搜索JAVA,即使是大写的,对应的数据仍然可以返回
标准分词器(standard)

标准分词器虽然大部分的字符和字符串都能拆分,但是一些带有英文语义的词就不一定能分出来了,比如doing,对应
标准分词器来说它不会将doing分析一个它对应的动词出来,也就是do,所以当根据do查询过滤时,往往就查不到了,虽然说看似doing和do对应我们所掌握的英语知识来说它俩的意思是非常相近的,只是doing是现在进行时,而do是动词,但是计算机是不会考虑这个,这时就需要别的分词策略介入了

英文分词器 (english)

针对标准分词器无法拆分一些带有英文语义的词,我们可以使用英文分词器

analyze-ik分词器 (中文分词器)
  1. ik_smart
  2. ik_max_word

ElasticSearch当中的字段类型

  1. Text类型: 就是字符串类型,只不过它是会被ElasticSearch分析的字符串
  2. Keyword类型: 也是字符串,但是它不能被分析,只能精确匹配
  3. Date类型: 日期类型
  4. 数字类型: long,integer,short,double等
  5. 布尔类型: true/false
  6. 数组类型: [“XXX”,“XXX”]
  7. Object类型: 嵌套的json就可以被认为是Object
  8. IP类型: 比如127.0.0.1
  9. Geo_point: 用于存储地址位置,比如经纬度
    既然有了Text类型为什么还要Keyword类型,Text类型不就相当于包含了Keyword类型吗? Keyword其实的确是有必要的,因为在某些场景下,我们就是要精准的匹配,比如淘宝中的一些类目,比如你要搜索电子设备的类目,那么家电类目其实就不应该被关联出来了,因为虽然都是带有电这个字,但是却完全不是一种类型的东西

IK分词器(中文分词器)构建

  1. 进入ElasticSearch的bin目录
  2. 使用如下命令下载对应的ik分词器 elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.3.0/elasticsearch-analysis-ik-7.3.0.zip
  3. 下载完之后进入plugins目录在这里插入图片描述
  4. 下载完毕后还需要重启ElasticSearch才能正确加载此插件,日志中也会打印对应的信息
    在这里插入图片描述
  5. 测试IK分词器是否生效
    在这里插入图片描述
    可以看到分词的效果,CN_CHAR代表中文字符,CN_WORD代表中文词语,但是哔哩哔哩被拆分成了单个独立的字,那按照我们的想法是说哔哩哔哩就可以算作一个词,那么怎么让它能够拆分出哔哩哔哩呢,这个设计到词典的概念了,之所以哔哩哔哩没有被拆分出来,就是因为词典中没有这个词,我们可以在词典中加上哔哩哔哩后再查看拆分的效果
  6. 自定义词典
    进入ElasticSearch的config目录,找到对应的ik分词器配置
    在这里插入图片描述
    这里面_stopword是不拆分词的意思,就是说哪些词不要拆分,main.dic就是要拆分的词,我们可以自定义一个词典方便后续使用,比如创建一个user.dic, 然后再IKAnalyzer.cfg.xml中进行引用即可, 示范: 我创建了一个全新的mydic.dic, 其中只存放了哔哩哔哩这个词
    在这里插入图片描述
    在这里插入图片描述
  7. 重启再次尝试分词查看效果
    在这里插入图片描述

搜索引擎当中的查全率和查准率的概念

  1. 查全率(Recall)和查准率(Precision)通常是相互影响的,它们之间存在一种权衡关系
  2. 查全率是指检索出的相关文档数占文档库中所有相关文档的比例
  3. 查准率是指检索出的相关文档数占检索出的所有文档的比例
  4. 通常情况下,提高查全率可能会导致查准率下降,反之亦然。这是因为:
    如果你放宽检索条件以捕获更多的相关文档(提高查全率),那么也可能引入更多的不相关文档(降低查准率)。
    相反,如果你严格限制检索条件以减少不相关文档的数量(提高查准率),那么可能会错过一些相关文档(降低查全率)。
  5. 实际实践当中,根据具体的需求场景,查全率和查准率的侧重点有所不同,比如在医学诊断中,可能更关心查全率,因为漏掉任何一个潜在的病例都可能导致严重的后果。而在产品推荐系统中,可能更关心查准率,因为不希望用户收到太多不相关的产品推荐。

根据数据库的字段完成对应的ElasticSearch索引的构建

  1. 因为后续我们会将类似于MYSQL这样的关系型数据库中的部分所需数据同步给ElasticSearch,那么在此之前需要先将对应的索引构建好,方便后续的同步
  2. 做一个简单的示范,假设我们现在有一张表是关于一些商家的信息,类似下图的结构
idvarchar
namevarchar
tagsvarchar
longitudedecimal
latitudedecimal
scoredecimal
average_consumptiondecimal
category_idvarchar
category_namevarchar
disable_flagtinyint

以上是一个基本的商家信息的表结构包含了名称,经纬度也就是地理坐标点,消费者评分值,平均消费价格,是否禁用标识等字段,那么我们应该如何基于这样的一个表结构去构建对应的ElasticSearch对应的索引呢?
3. 根据表结构构建索引
在这里插入图片描述

ElasticSearch全量索引同步构建 logstash-input-jdbc

  1. 上面根据数据库表的结构完成了索引的创建,那么如何将MYSQL中的数据同步到ElasticSearch中呢? ElasticSearch官方也提供对应的工具logstash-input-jdbc,jdbc应该都不陌生(Java Database Connectivity,Java数据库连接),接下来将解释一下logstash-input-jdbc是如何完成数据的同步操作的
  2. 在这里插入图片描述
    当我们基于全量索引构建时会遗留一些问题,比如数据的一致性问题,比如说现在有一条记录A它在第一次同步完成之后,发生了改变,变为了B,这个时候第二次同步是不会再次同步一遍的,因为我们是分页的方式同步的,实际也不可能说再覆盖一遍,那怎么办呢,其实类似于推荐搜索的系统我们允许存在一定的不一致的,对于实时性要求高的系统当然也有解决方案比如使用阿里巴巴开源的Canal来完成基于监听MYSQL的binlog的方式进行同步,达到近似于准实时的增量同步
  3. Logstash7.3.0下载
  4. 进入bin目录,通过如下命令安装logstash-input-jdbc
logstash-plugin install logstash-input-jdbc
  1. 在bin目录下创建一个文件夹,例如mysql,这个文件夹中将要存放与MYSQL相关的一些配置以及MYSQL的驱动包,那这个驱动去哪里找呢,其实也非常简单,如果你本地有java项目并且是maven工程,在引入对应的mysql驱动依赖后,对应的maven仓库就会下载对应驱动jar包,或者说去网上找一个下载也可以
  2. 在mysql目录下创建jdbc.conf文件
input {
    stdin {
    }
    jdbc {
        #mysql连接地址
        jdbc_connection_string => "jdbc:mysql://数据库ip:3306/数据库名称"
        #用户名和密码
        jdbc_user => ""
        jdbc_password => ""
        #驱动路径
        jdbc_driver_library => "D:/logstash-7.3.0/logstash-7.3.0/bin/mysql/mysql-connector-java-5.1.42.jar"
        #驱动信息
        jdbc_driver_class => "com.mysql.jdbc.Driver"
        jdbc_paging_enabled => "true"
        jdbc_page_size => "50000"
        #执行的SQL文件地址
        statement_path => "D:/logstash-7.3.0/logstash-7.3.0/bin/mysql/jdbc.sql"
        #cron表达式 间隔时间
        schedule => "* * * * *"
    }
}

output {
    elasticsearch {
        #ElasticSearch的IP地址
        hosts => ["192.xxx.xxx.xxx:9200"]
        #索引的名称 你要往哪个索引同步数据
        index => "business_info"
        #文档的id我们直接使用数据库的主键id
        document_id => "%{id}"
    }
    stdout {
        #json格式输出
        codec => json_lines
    }
}
  1. 在mysql目录下创建jdbc.sql文件,这个sql文件就是用作查询DB的也就是select语句
select 
a.id,
a.name,
a.tags,
concat(a.longitude, ',', a.longitude) as location,
a.score,
a.average_consumption,
a.category_id,
a.category_name,
a.disable_flag
from business_info_table a
  1. bin目录下通过logstash执行jdbc.conf
    在这里插入图片描述
  2. 至此 全量同步索引的构建就完成了

ElasticSearch增量索引同步构建 logstash-input-jdbc

  1. 如何使用logstash-input-jdbc实现类似增量的方式构建呢?其实关键点就在于这个SQL语句应该怎么写,我们一般在建表的时候会有一些表字段是必须携带的字段,比如createTime,updateTime,那么我们就可以利用这个updateTime时间戳的方式来进行增量同步
  2. 在jdbc.conf中设置时区, 这里为什么要设置时区呢,因为默认是UTC的时区,也就是格林威治的时区,那显然和中国所在的时区是完成不同的,而我们的业务系统中时间生成往往是按照中国所在的位置对应的时区来赋值的,默认的时区就对应不上了,当然设置的时区具体是什么,要看你的系统部署在哪了
jdbc_default_timezone => "Asia/Shanghai"
  1. jdbc.conf新建一个配置项目 last_run_metadata_path
last_run_metadata_path => "D:/logstash-7.3.0/logstash-7.3.0/bin/mysql/last_value_meta"
  1. 新增一个文件last_value_meta, 这个文件用于记录最后一次执行的时间

在这里插入图片描述
在这里插入图片描述

  1. 改造jdbc.sql
select 
a.id,
a.name,
a.tags,
concat(a.longitude, ',', a.longitude) as location,
a.score,
a.average_consumption,
a.category_id,
a.category_name,
a.disable_flag
from business_info_table a where a.update_time > :sql_last_value
  1. 进入logstash的bin目录再次执行 logstash -f mysql/jdbc.conf
  2. 这样就可以实现一个增量的同步方式,即使后续的某段时间内,一行记录的updateTime发生变更了,也就是业务修改了数据,没关系,由于我们记录了最后一次执行的时间,那么只要updateTime大于我们记录的时间,那么再次执行同步任务的时候,对应的修改过的数据一样可以完成同步
  3. 即使这样完成了增量的同步但是还有一些问题的存在,那就是实时性问题,由于logstash-input-jdbc是通过定时任务的方式去轮询查找数据库,这就导致了轮询时间并不好确定,假设业务量比较大,设置了一分钟这样的间隔进行轮询同步,不算同步的时间,我们最少也有一分钟这样的数据延迟,那么可以设置1秒钟一次轮询吗,貌似好像可以但实际上却根本不合理,第一,先不说logstash本身的压力,频繁的查询会对数据库造成额外的压力,增加CPU、内存和磁盘I/O的负担,从而影响数据库的整体性能,第二,如果一个业务在一秒钟内根本没有发生任何变动,数据库记录也没有更新,那就相当于白白的做了一次无用功,后续我们将会采用阿里巴巴的Canal中间件,通过管道监听MYSQL的binlog来完成准实时的同步,并且避免一些资源的浪费

未完待续…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值