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

为什么需要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…

找到

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

注意不要用8的版本!

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

我这里选择7.17版本

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

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使用

基本使用

增加文档

POST article/_doc
{
  "title":"文章",
  "desc":"好文章"
}
复制代码

查询文档

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

GET article/_doc/_-cifocBWgoYUa01_dPj
复制代码

查询结果

{
  "_index" : "article",
  "_type" : "_doc",
  "_id" : "_-cifocBWgoYUa01_dPj",
  "_version" : 1,
  "_seq_no" : 1,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "title" : "文章",
    "desc" : "好文章"
  }
}
复制代码

compound queries

POST _search
{
  "query": {
    "bool" : {
        //must:必须要包含
      "must" : {
        //term必须完全符合,不允许模糊  
        "term" : { "user.id" : "kimchy" }
      },
        //过滤器,不影响评分 
      "filter": {
        "term" : { "tags" : "production" }
      },
        //must_not:必须不包含
      "must_not" : {
        "range" : {
          "age" : { "gte" : 10, "lte" : 20 }
        }
      },
        //should 和 minimum_should_match连起来用,这里minimum_should_match为1,说明should中的条件至少需要满足一个
      "should" : [
        { "term" : { "tags" : "env1" } },
        { "term" : { "tags" : "deployed" } }
      ],
      "minimum_should_match" : 1,
      "boost" : 1.0
    }
  }
}
复制代码

更新文档

POST article/_doc/_-cifocBWgoYUa01_dPj
{
  "title":"文章",
  "desc":"坏文章"
}
复制代码

更新结果

{
  "_index" : "article",
  "_type" : "_doc",
  "_id" : "_-cifocBWgoYUa01_dPj",
  "_version" : 2,
  "_seq_no" : 2,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "title" : "文章",
    "desc" : "坏文章"
  }
}
​
复制代码

删除文档

DELETE article/_doc/_-cifocBWgoYUa01_dPj
复制代码

索引的概念

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

倒排索引:

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

比如两句句话:

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

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

构建倒排索引

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

用户搜 nika学生

es先切词。nika,学生

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

Mapping的概念

官网也有。

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

查询表结构

Dynamic Mapping

GET article/_mapping
复制代码

表结构结果

{
  "article" : {
    "mappings" : {
      "properties" : {
        "desc" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "title" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}
复制代码

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

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

结果也是成功的

{
  "_index" : "article",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 4,
  "_primary_term" : 1
}
复制代码

Explicit mapping

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

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

PUT /user
{
  "mappings": {
    "properties": {
      "age":    { "type": "integer" },  
      "email":  { "type": "keyword"  }, 
      "name":   { "type": "text"  }     
    }
  }
}
复制代码

再查询一下user的mapping

{
  "user" : {
    "mappings" : {
      "properties" : {
        "age" : {
          "type" : "integer"
        },
        "email" : {
          "type" : "keyword"
        },
        "name" : {
          "type" : "text"
        }
      }
    }
  }
}
复制代码

小总结

至此,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吧

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

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

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

SQL

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

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

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

这是学习成本最低的方式

小总结

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

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

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

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

分词器

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

分词的一种规则

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

POST _analyze
{
  "analyzer": "whitespace",
  "text":     "The quick brown fox."
}
复制代码

结果

{
  "tokens" : [
    {
      "token" : "The",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "quick",
      "start_offset" : 4,
      "end_offset" : 9,
      "type" : "word",
      "position" : 1
    },
    {
      "token" : "brown",
      "start_offset" : 10,
      "end_offset" : 15,
      "type" : "word",
      "position" : 2
    },
    {
      "token" : "fox.",
      "start_offset" : 16,
      "end_offset" : 20,
      "type" : "word",
      "position" : 3
    }
  ]
}
​
复制代码

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

POST _analyze
{
  "tokenizer": "standard",
  "filter":  [ "lowercase", "asciifolding" ],
  "text":      "Is this déja vu?"
}
复制代码

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

{
  "tokens" : [
    {
      "token" : "is",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "this",
      "start_offset" : 3,
      "end_offset" : 7,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "deja",
      "start_offset" : 8,
      "end_offset" : 12,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "vu",
      "start_offset" : 13,
      "end_offset" : 15,
      "type" : "<ALPHANUM>",
      "position" : 3
    }
  ]
}
复制代码

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

github.com/medcl/elast…

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

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

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

github.com/medcl/elast…

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

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

然后最关键一点来了。

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

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

再次去bin目录,启动es。

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

用一下他的分词器ik_smart

POST _analyze
{
  "analyzer": "ik_max_word",
  "text":     "nika是南信大的学生"
}
复制代码

结果

{
  "tokens" : [
    {
      "token" : "nika",
      "start_offset" : 0,
      "end_offset" : 4,
      "type" : "ENGLISH",
      "position" : 0
    },
    {
      "token" : "是",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "CN_CHAR",
      "position" : 1
    },
    {
      "token" : "南",
      "start_offset" : 5,
      "end_offset" : 6,
      "type" : "CN_CHAR",
      "position" : 2
    },
    {
      "token" : "信",
      "start_offset" : 6,
      "end_offset" : 7,
      "type" : "CN_CHAR",
      "position" : 3
    },
    {
      "token" : "大",
      "start_offset" : 7,
      "end_offset" : 8,
      "type" : "CN_CHAR",
      "position" : 4
    },
    {
      "token" : "的",
      "start_offset" : 8,
      "end_offset" : 9,
      "type" : "CN_CHAR",
      "position" : 5
    },
    {
      "token" : "学生",
      "start_offset" : 9,
      "end_offset" : 11,
      "type" : "CN_WORD",
      "position" : 6
    }
  ]
}
复制代码

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

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

ik_max_word

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

POST _analyze
{
  "analyzer": "ik_max_word",
  "text":     "大学生"
}
复制代码

他的结果是

{
  "tokens" : [
    {
      "token" : "大学生",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "大学",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "CN_WORD",
      "position" : 1
    },
    {
      "token" : "学生",
      "start_offset" : 1,
      "end_offset" : 3,
      "type" : "CN_WORD",
      "position" : 2
    }
  ]
}
复制代码

ik_smart

而ik_smart的结果则是

{
  "tokens" : [
    {
      "token" : "大学生",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "CN_WORD",
      "position" : 0
    }
  ]
}
​
复制代码

看上去更加智能些!

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…

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

我们用的是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中。

PUT article_v1
{
  "aliases": {
    "article": {}
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_smart",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "content": {
        "type": "text",
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_smart",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "tags": {
        "type": "keyword"
      },
      "thumbNum": {
        "type": "long"
      },
      "favourNum": {
        "type": "long"
      },
      "userId": {
        "type": "keyword"
      },
      "createTime": {
        "type": "date"
      },
      "updateTime": {
        "type": "date"
      },
      "isDelete": {
        "type": "keyword"
      }
    }
  }
}
复制代码
  • alias:别名,你用article也能找到这个索引(表)article_v1,方便数据迁移

  • 如果你的字段类型是text,这个字段就是可以被分词,可以模糊查询的;如果是keyword,就只能完全匹配,精确查询。

  • analyser(存储时生效的分词器),存储时候生效的分词器,用ik_max_word,拆解的多,尽可能被搜出。

  • search_analyser(查询时生效的分词器):用ik_smart,更偏向于用户想搜的分词

  • 如果想让text的分词字段也支持精确查询

     "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
                  //超过字符数忽略查询
              }
       }
    复制代码

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

增删改查

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

public interface PostEsDao extends ElasticsearchRepository<PostEsDTO, Long> {
​
    List<PostEsDTO> findByUserId(Long userId);
}
复制代码
 ArticleEsDTO articleEsDTO = new ArticleEsDTO();
        articleEsDTO.setTitle("nika");
        articleEsDTO.setContent("nika是南信大的学生");
        articleEsDTO.setUserId(0L);
        articleEsDTO.setCreateTime(new Date());
        articleEsDTO.setUpdateTime(new Date());
        articleEsDTO.setIsDelete(0);
        articleEsDao.save(articleEsDTO);
        System.out.println(articleEsDTO);
复制代码

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

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

 @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;
复制代码

同理,不一一列举

小总结

在写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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值