一篇文章带你入门并使用Elastic Stack

柔情只为你懂 2023-09-26 23:27 163阅读 0赞

为什么需要ElasticSearch?

根本:用like查询 不能满足需求。

比如一个短语叫:蔡徐坤喜欢打篮球。如果我们搜索”蔡徐坤篮球”,那么利用like是查询不到的。

且在大量数据的前提下,like查询效率低下,速度慢。

ElasticSearch概念

核心:类比数据库

可以简单理解成一个数据库,例如MySQL.

学习es的时候可以对照着学。例如elasticSearch的索引就可以类比成mysql中的表。

安装

www.elastic.co/cn/

www.elastic.co/guide/index…

es迭代超级快!建议读官方文档,本文章只是初步讲解,可以跟着这个思路去读es的官方文档。

首先明确几个概念:

  • estack 是一套技术栈,包含了数据的整合,抽取,存储 ,使用
  • beats 从不同类型的文件/应用来获取,采集数据(数据采集器)
  • logstash:从多个采集器或者数据源中抽取,转换数据(转成我们需要的)。并向es输送。
  • es:存储查询数据
  • kibana:可视化es数据

安装ES,kibana

在该页面中www.elastic.co/guide/index…

找到

format_png

format_png 1

把两个页面打开到新的标签页。

注意不要用8的版本!

很不适合新手!会有https以及安全校验,初次使用问题很多。

我这里选择7.17版本

www.elastic.co/guide/en/el…

format_png 2

www.elastic.co/guide/en/ki…

注意,这一套技术栈的版本一定要一致!!

ES相比于MySQL,能自动做分词,能高效灵活查询内容。

Quick start

www.elastic.co/guide/en/el…

kibana启动后:http://localhost:5601/app/dev_tools#/console

以下只是笔者写的demo,更多实操需要同学们自己去官网阅读。

这是es的DSL语法,仅仅供es使用

基本使用

增加文档

  1. POST article/_doc
  2. {
  3. "title":"文章",
  4. "desc":"好文章"
  5. }
  6. 复制代码

查询文档

后面的索引是增加文档后返回的

  1. GET article/_doc/_-cifocBWgoYUa01_dPj
  2. 复制代码

查询结果

  1. {
  2. "_index" : "article",
  3. "_type" : "_doc",
  4. "_id" : "_-cifocBWgoYUa01_dPj",
  5. "_version" : 1,
  6. "_seq_no" : 1,
  7. "_primary_term" : 1,
  8. "found" : true,
  9. "_source" : {
  10. "title" : "文章",
  11. "desc" : "好文章"
  12. }
  13. }
  14. 复制代码

compound queries

  1. POST _search
  2. {
  3. "query": {
  4. "bool" : {
  5. //must:必须要包含
  6. "must" : {
  7. //term必须完全符合,不允许模糊
  8. "term" : { "user.id" : "kimchy" }
  9. },
  10. //过滤器,不影响评分
  11. "filter": {
  12. "term" : { "tags" : "production" }
  13. },
  14. //must_not:必须不包含
  15. "must_not" : {
  16. "range" : {
  17. "age" : { "gte" : 10, "lte" : 20 }
  18. }
  19. },
  20. //should 和 minimum_should_match连起来用,这里minimum_should_match为1,说明should中的条件至少需要满足一个
  21. "should" : [
  22. { "term" : { "tags" : "env1" } },
  23. { "term" : { "tags" : "deployed" } }
  24. ],
  25. "minimum_should_match" : 1,
  26. "boost" : 1.0
  27. }
  28. }
  29. }
  30. 复制代码

更新文档

  1. POST article/_doc/_-cifocBWgoYUa01_dPj
  2. {
  3. "title":"文章",
  4. "desc":"坏文章"
  5. }
  6. 复制代码

更新结果

  1. {
  2. "_index" : "article",
  3. "_type" : "_doc",
  4. "_id" : "_-cifocBWgoYUa01_dPj",
  5. "_version" : 2,
  6. "_seq_no" : 2,
  7. "_primary_term" : 1,
  8. "found" : true,
  9. "_source" : {
  10. "title" : "文章",
  11. "desc" : "坏文章"
  12. }
  13. }
  14. 复制代码

删除文档

  1. DELETE article/_doc/_-cifocBWgoYUa01_dPj
  2. 复制代码

索引的概念

正向索引:理解为书籍的目录,可以高速找到对应的内容。(怎么根据页码找到文章)

倒排索引:

根据内容找到文章。本质:构建倒排索引。

比如两句句话:

文章A:nika是南信大的学生

文章B:南信大是nika的母校

构建倒排索引


























分词 内容id
nika 文章A,文章B
南信大 文章A,文章B
学生 文章A
母校 文章B,文章B

用户搜 nika学生

es先切词。nika,学生

所以根据nika,找到了文章A,B,学生找到了文章A。

Mapping的概念

format_png 3

官网也有。

理解为数据库的表结构,有哪些字段,有哪些类型

查询表结构

Dynamic Mapping

  1. GET article/_mapping
  2. 复制代码

表结构结果

  1. {
  2. "article" : {
  3. "mappings" : {
  4. "properties" : {
  5. "desc" : {
  6. "type" : "text",
  7. "fields" : {
  8. "keyword" : {
  9. "type" : "keyword",
  10. "ignore_above" : 256
  11. }
  12. }
  13. },
  14. "title" : {
  15. "type" : "text",
  16. "fields" : {
  17. "keyword" : {
  18. "type" : "keyword",
  19. "ignore_above" : 256
  20. }
  21. }
  22. }
  23. }
  24. }
  25. }
  26. }
  27. 复制代码

这里的mapping虽然说可以类比成mysql中的表结构,但是比它更加灵活。你在添加数据的时候,甚至可以添加结构中没有的字段,比如:

  1. POST article/_doc/1
  2. {
  3. "title":"文章",
  4. "desc":"好文章",
  5. "extra":"我是表结构中没有的字段"
  6. }
  7. 复制代码

结果也是成功的

  1. {
  2. "_index" : "article",
  3. "_type" : "_doc",
  4. "_id" : "1",
  5. "_version" : 1,
  6. "result" : "created",
  7. "_shards" : {
  8. "total" : 2,
  9. "successful" : 1,
  10. "failed" : 0
  11. },
  12. "_seq_no" : 4,
  13. "_primary_term" : 1
  14. }
  15. 复制代码

Explicit mapping

当然,es也支持显式的表结构创建

创建一个user表,定义好年龄是整数,email是keyword(不可分词的字符串,它不会把这个词拆分),name是text

  1. PUT /user
  2. {
  3. "mappings": {
  4. "properties": {
  5. "age": { "type": "integer" },
  6. "email": { "type": "keyword" },
  7. "name": { "type": "text" }
  8. }
  9. }
  10. }
  11. 复制代码

再查询一下user的mapping

  1. {
  2. "user" : {
  3. "mappings" : {
  4. "properties" : {
  5. "age" : {
  6. "type" : "integer"
  7. },
  8. "email" : {
  9. "type" : "keyword"
  10. },
  11. "name" : {
  12. "type" : "text"
  13. }
  14. }
  15. }
  16. }
  17. }
  18. 复制代码

小总结

至此,es的增删改查你应该已经学会了,我们需要类比着去学。mapping可以类比成MySQL的表结构(schema),索引就是MySQL中的表

EQL

ESC简介

在了解EQL之前,我们需要知道什么是ECS(Elastic Common Schema)。是一种用于定义各种类型日志和指标的公共数据模型规范

ECS提供了一组字段名称和数据类型,包括一组预定义字段,涵盖常见的数据点,如时间戳,用户代理,ip地址,URL,Http状态码,除了这些预定义字段,es也能允许自定义字段,使用户将数据点添加到公共数据模型中。

所以,本质上,用人话说就是,ESC就是一个别人给你提供好的表结构,它很规范,你可以在它的基础上加表结构字段,就这么简单

ESC简介

EQL其实就是 专门查询 ESC 的语法。

使用EQL

首先,我们先创建一个ESC的表结构,其实在官网上的Quick Start 的demo中,创建的就是ESC的表结构。

我们粘一下官网的demo,改以下表名,就叫他my_ecs吧

  1. POST my_ecs/_doc
  2. {
  3. "@timestamp": "2099-05-06T16:21:15.000Z",
  4. "event": {
  5. "original": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] "GET /images/bg.jpg HTTP/1.0" 200 24736"
  6. }
  7. }
  8. 复制代码

创建完了以后,我们就可以使用EQL语法了,打开官方文档。www.elastic.co/guide/en/el…

这个文档里面的东西别去记忆,用的时候找就好了。

SQL

es不仅支持以上语法,如果你学过sql的话,那你甚至可以直接用sql进行查询!

www.elastic.co/guide/en/el…

  1. POST /_sql?format=txt
  2. {
  3. "query": "SELECT * FROM article WHERE title like '%文章%'"
  4. }
  5. 复制代码
  6. desc | extra | title
  7. ---------------+---------------+---------------
  8. 好文章 |null |文章
  9. 好文章 |我是表结构中没有的字段 |文章
  10. 复制代码

这是学习成本最低的方式

小总结

DSL的语法,类似HTTP请求,以json的格式操作es,简单,灵活,拓展性好。但是可读性差

EQL的语法,声明式,易于读写,支持多个索引,多个类型的查询,结果更加直观,但是只适用于特定场景。

SQL语法,学习成本低,但需要额外插件或者工具,性能差

DSL是应用最广,最推荐的语言

分词器

www.elastic.co/guide/en/el…

分词的一种规则

比如官方的示例:使用了whitespace(空格)的分析器,分析这个text

  1. POST _analyze
  2. {
  3. "analyzer": "whitespace",
  4. "text": "The quick brown fox."
  5. }
  6. 复制代码

结果

  1. {
  2. "tokens" : [
  3. {
  4. "token" : "The",
  5. "start_offset" : 0,
  6. "end_offset" : 3,
  7. "type" : "word",
  8. "position" : 0
  9. },
  10. {
  11. "token" : "quick",
  12. "start_offset" : 4,
  13. "end_offset" : 9,
  14. "type" : "word",
  15. "position" : 1
  16. },
  17. {
  18. "token" : "brown",
  19. "start_offset" : 10,
  20. "end_offset" : 15,
  21. "type" : "word",
  22. "position" : 2
  23. },
  24. {
  25. "token" : "fox.",
  26. "start_offset" : 16,
  27. "end_offset" : 20,
  28. "type" : "word",
  29. "position" : 3
  30. }
  31. ]
  32. }
  33. 复制代码

标准分词规则,但是一般不用

  1. POST _analyze
  2. {
  3. "tokenizer": "standard",
  4. "filter": [ "lowercase", "asciifolding" ],
  5. "text": "Is this déja vu?"
  6. }
  7. 复制代码

结果:我们发现,词被分成了四个,且全变成了小写,韵母也没了,这就是filter的作用

  1. {
  2. "tokens" : [
  3. {
  4. "token" : "is",
  5. "start_offset" : 0,
  6. "end_offset" : 2,
  7. "type" : "<ALPHANUM>",
  8. "position" : 0
  9. },
  10. {
  11. "token" : "this",
  12. "start_offset" : 3,
  13. "end_offset" : 7,
  14. "type" : "<ALPHANUM>",
  15. "position" : 1
  16. },
  17. {
  18. "token" : "deja",
  19. "start_offset" : 8,
  20. "end_offset" : 12,
  21. "type" : "<ALPHANUM>",
  22. "position" : 2
  23. },
  24. {
  25. "token" : "vu",
  26. "start_offset" : 13,
  27. "end_offset" : 15,
  28. "type" : "<ALPHANUM>",
  29. "position" : 3
  30. }
  31. ]
  32. }
  33. 复制代码

IK分词器(常用,国内友好,ES插件)

github.com/medcl/elast…

这是个github 15k Star 的开源项目。这是对es的一个扩展。

由于官网上默认的分词器都是针对英文的。比如,英文都是空格分割的,而中文一般不这样分词,因此我们需要插件帮我们去给中文分词。

由于我们用的是7.17版本,因此我们需要下7.17版本的依赖

github.com/medcl/elast…

我们这里下7.17.7。下zip压缩包。

在你的es文件夹里面新建一个plugins目录,将这个压缩包解压进去。

format_png 4

然后最关键一点来了。

由于我们的es是7.17.9,然而我们的插件是7.17.7,因为下不到7.17.9,只能退而求其次。我们这个时候要修改一个文件

format_png 5

打开prperties文件,把里面所有7.17.7改为7.17.9即可。

再次去bin目录,启动es。

我们用再到kibana的控制台,去试试ik的分词器。

用一下他的分词器ik_smart

  1. POST _analyze
  2. {
  3. "analyzer": "ik_max_word",
  4. "text": "nika是南信大的学生"
  5. }
  6. 复制代码

结果

  1. {
  2. "tokens" : [
  3. {
  4. "token" : "nika",
  5. "start_offset" : 0,
  6. "end_offset" : 4,
  7. "type" : "ENGLISH",
  8. "position" : 0
  9. },
  10. {
  11. "token" : "是",
  12. "start_offset" : 4,
  13. "end_offset" : 5,
  14. "type" : "CN_CHAR",
  15. "position" : 1
  16. },
  17. {
  18. "token" : "南",
  19. "start_offset" : 5,
  20. "end_offset" : 6,
  21. "type" : "CN_CHAR",
  22. "position" : 2
  23. },
  24. {
  25. "token" : "信",
  26. "start_offset" : 6,
  27. "end_offset" : 7,
  28. "type" : "CN_CHAR",
  29. "position" : 3
  30. },
  31. {
  32. "token" : "大",
  33. "start_offset" : 7,
  34. "end_offset" : 8,
  35. "type" : "CN_CHAR",
  36. "position" : 4
  37. },
  38. {
  39. "token" : "的",
  40. "start_offset" : 8,
  41. "end_offset" : 9,
  42. "type" : "CN_CHAR",
  43. "position" : 5
  44. },
  45. {
  46. "token" : "学生",
  47. "start_offset" : 9,
  48. "end_offset" : 11,
  49. "type" : "CN_WORD",
  50. "position" : 6
  51. }
  52. ]
  53. }
  54. 复制代码

我们发现,他确实生效了,把学生归为一个词了,但是仍然不能按照我们的想法去分词,比如我要分成:我是。南信大的。学生。

其实这个也很简单,可以自己自定义一个词库。可以自己配置。感兴趣同学再搜索搜索。

ik_max_word

再试试ik_max_word,他其实做的是尽可能多的去分词。比如

  1. POST _analyze
  2. {
  3. "analyzer": "ik_max_word",
  4. "text": "大学生"
  5. }
  6. 复制代码

他的结果是

  1. {
  2. "tokens" : [
  3. {
  4. "token" : "大学生",
  5. "start_offset" : 0,
  6. "end_offset" : 3,
  7. "type" : "CN_WORD",
  8. "position" : 0
  9. },
  10. {
  11. "token" : "大学",
  12. "start_offset" : 0,
  13. "end_offset" : 2,
  14. "type" : "CN_WORD",
  15. "position" : 1
  16. },
  17. {
  18. "token" : "学生",
  19. "start_offset" : 1,
  20. "end_offset" : 3,
  21. "type" : "CN_WORD",
  22. "position" : 2
  23. }
  24. ]
  25. }
  26. 复制代码

ik_smart

而ik_smart的结果则是

  1. {
  2. "tokens" : [
  3. {
  4. "token" : "大学生",
  5. "start_offset" : 0,
  6. "end_offset" : 3,
  7. "type" : "CN_WORD",
  8. "position" : 0
  9. }
  10. ]
  11. }
  12. 复制代码

看上去更加智能些!

ES打分机制

es有自己独特的打分机制。

举个例子,四句话

  1. nika是南信大的大二学生
  2. nika是大二学生
  3. nika是学生

这里用户搜nika,如果只要求返回一条数据,那么就会返回第三条。

因为es默默地去给每句话打分,哪个高,哪个排在前面,因为第三条不仅匹配,且占这句话的比例高。

其实背后也很复杂,有自己的算法,感兴趣的同学深入了解。

Spring Data Elasticsearch

spring-data 系列:是spring提供的操作数据的框架。

比如spring-data-redis,spring-data-mongodb之类的

我们这次要用的是 spring-data-elasticsearch

这里我们去看spring的官方文档

spring.io/projects/sp…

这里要注意的是,我们安装的es的版本也必须要和依赖版本强一致!!

docs.spring.io/spring-data…

这里进行查看版本依赖关系

format_png 6

我们用的是7.17.7,因此,选择spring-data-elasticsearch 4.4.x版本的,回到spring.io/projects/sp…页面,选择对应版本的GA版本。

点进去之后,我们先阅读核心概念

docs.spring.io/spring-data…

这个文章里有相关增删改查的示例。不得不说,写的相当差。但没办法。

Java代码实操

创建ES Mapping(表结构)

tip:如果你声明一个字段的type是text,那么你可以传一个text,也可以传一个text的数组,这是es的一个特性。

es中尽量存放需要用户筛选,并且搜索的数据,且频繁变化的数据也不建议放入es中。

  1. PUT article_v1
  2. {
  3. "aliases": {
  4. "article": {}
  5. },
  6. "mappings": {
  7. "properties": {
  8. "title": {
  9. "type": "text",
  10. "analyzer": "ik_max_word",
  11. "search_analyzer": "ik_smart",
  12. "fields": {
  13. "keyword": {
  14. "type": "keyword",
  15. "ignore_above": 256
  16. }
  17. }
  18. },
  19. "content": {
  20. "type": "text",
  21. "analyzer": "ik_max_word",
  22. "search_analyzer": "ik_smart",
  23. "fields": {
  24. "keyword": {
  25. "type": "keyword",
  26. "ignore_above": 256
  27. }
  28. }
  29. },
  30. "tags": {
  31. "type": "keyword"
  32. },
  33. "thumbNum": {
  34. "type": "long"
  35. },
  36. "favourNum": {
  37. "type": "long"
  38. },
  39. "userId": {
  40. "type": "keyword"
  41. },
  42. "createTime": {
  43. "type": "date"
  44. },
  45. "updateTime": {
  46. "type": "date"
  47. },
  48. "isDelete": {
  49. "type": "keyword"
  50. }
  51. }
  52. }
  53. }
  54. 复制代码
  • alias:别名,你用article也能找到这个索引(表)article_v1,方便数据迁移
  • 如果你的字段类型是text,这个字段就是可以被分词,可以模糊查询的;如果是keyword,就只能完全匹配,精确查询。
  • analyser(存储时生效的分词器),存储时候生效的分词器,用ik_max_word,拆解的多,尽可能被搜出。
  • search_analyser(查询时生效的分词器):用ik_smart,更偏向于用户想搜的分词
  • 如果想让text的分词字段也支持精确查询

    1. "fields": {
    2. "keyword": {
    3. "type": "keyword",
    4. "ignore_above": 256
    5. //超过字符数忽略查询
    6. }
    7. }
    8. 复制代码

这些结构的选择,都是实践状态下的最佳实践,以后创建表结构参考这个案例就好了。

增删改查

第一种方式:通过ElasticsearchRepository.可以根据方法名,生成查询语句

  1. public interface PostEsDao extends ElasticsearchRepository<PostEsDTO, Long> {
  2. List<PostEsDTO> findByUserId(Long userId);
  3. }
  4. 复制代码
  5. ArticleEsDTO articleEsDTO = new ArticleEsDTO();
  6. articleEsDTO.setTitle("nika");
  7. articleEsDTO.setContent("nika是南信大的学生");
  8. articleEsDTO.setUserId(0L);
  9. articleEsDTO.setCreateTime(new Date());
  10. articleEsDTO.setUpdateTime(new Date());
  11. articleEsDTO.setIsDelete(0);
  12. articleEsDao.save(articleEsDTO);
  13. System.out.println(articleEsDTO);
  14. 复制代码

其它的api自己追一追源码即可,非常简单,和mybatis和mongoTemplate之类的东西非常类似,这里不一一列举。

第二种方式:更灵活,传入参数更多。

  1. @Resource
  2. private ElasticsearchRestTemplate elasticsearchRestTemplate;
  3. 复制代码

同理,不一一列举

小总结

在写es的过程中,你会发现,只要你能在kibana的devtools里面把DSL写出来,就能映射到Java的api中。

因此我们在写复杂的业务的时候,尽量先在kibana的devtools中把这个DSL能跑通之后,再把这段逻辑映射到代码实现里。

尽量不要用sql,性能低下

ES数据同步

es的运用场景有很多,比如熟知的日志系统,搜索系统等。

他的主要优势点是查。而这个数据是从哪里来呢?没错就是从其它数据源来,当其他数据源的数据增加的时候,我们需要对es的数据做相应的同步。

同步策略无非以下几种:

  1. 定时任务同步。如每隔一段时间,根据上一分钟updatetime字段,从 MySQL中查询相应数据,同步到es。这个适用于,MySQL写的不多,查询需求不紧急的情况。
  2. 双写。在写MySQL的同时,写es。这个比较危险,需要事务进行保证双写要么成功要么失败。
  3. logStash组件

LogStash

www.elastic.co/guide/en/lo…

这个比较简单,同学们按照本文讲的方式自己探索即可

发表评论

表情:
评论列表 (有 0 条评论,163人围观)

还没有评论,来说两句吧...

相关阅读

    相关 文章入门音视频

    一、概述 1)流媒体协议是服务器与客户端之间通信遵循的规定。当前网络上主要的流媒体协议如表所示。 2)封装格式的主要作用是把视频码流和音频码流按照一定的格式存储在一个文件中