ElasticSearch简单入门

192 阅读9分钟

es是什么

The Elastic Stack, 包括 Elasticsearch、Kibana、Beats 和 Logstash(也称为 ELK Stack)。 能够安全可靠地获取任何来源、任何格式的数据,然后实时地对数据进行搜索、分析和可视化。Elaticsearch,简称为 ES,

ES 是一个开源的高扩展的分布式全文搜索引擎,是整个 Elastic Stack 技术栈的核心。它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上 百台服务器,处理 PB 级别的数据。

为什么会有es

mysql缺点

因为mysql如果无法命中任何索引,会触发全表扫描。 而且数据量大的话,分库分表维护麻烦。

es优点

轻松支持各种复杂的查询条件

会把每一个字段都编入索引(倒排索引),利用高效的倒排索引,以及自定义打分、排序能力与丰富的分词插件等,能实现任意复杂查询条件下的全文检索需求

可扩展性强--天然支持分布式存储

高可用,容灾性能好

据说,clickhouse和doris 以后可能会替代mysql

es 的安装

docker安装

(1)下载
docker pull elasticsearch:7.7.0
docker pull kibana:7.7.0

(2)配置
mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data
echo "http.host: 0.0.0.0" /mydata/elasticsearch/config/elasticsearch.yml
chmod -R 777 /mydata/elasticsearch/

(3)启动Elastic search

docker run --name elasticsearch -d -e ES_JAVA_OPTS="-Xms512m -Xmx512m" -e "discovery.type=single-node" -p 9200:9200 -p 9300:9300 
-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \
-v  /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.7.0

    设置开机启动elasticsearch
docker update elasticsearch --restart=always

   (4) 启动kibana

docker run --name kibana -e ELASTICSEARCH_HOSTS=http://192.168.10.102:9200 -p 5601:5601 -d kibana:7.7.0

   设置开机启动kibana
   
   docker update kibana  --restart=always
   
   (5)测试
   查看elasticsearch版本信息: <http://yourip:9200/>
   {
        "name": "0adeb7852e00",
        "cluster_name": "elasticsearch",
        "cluster_uuid": "9gglpP0HTfyOTRAaSe2rIg",
        "version": {
            "number": "7.6.2",
            "build_flavor": "default",
            "build_type": "docker",
            "build_hash": "ef48eb35cf30adf4db14086e8aabd07ef6fb113f",
            "build_date": "2020-03-26T06:34:37.794943Z",
            "build_snapshot": false,
            "lucene_version": "8.4.0",
            "minimum_wire_compatibility_version": "6.8.0",
            "minimum_index_compatibility_version": "6.0.0-beta1"
        },
        "tagline": "You Know, for Search"
    }
    这样式儿的就ok。

es的基本概念

Index (索引)       ----相当于mysql的 库

Type  (类型)       ----相当于mysql的表

document  (文档)   ----相当于表中的数据

image.png

es的基本测试

postman的使用

使用postman来对es发送请求,因为从网站上只能发送get请求,而postman,则都可以。

接下来我们就使用postman来对es做一些基本的测试和使用。

_cat---查看基本信息

首先查看状态

192.168.10.102:9200/_cat/health

image.png

查看主节点

192.168.10.102:9200/_cat/master

image.png

image.png 主节点就是我们生成的那个

接下来,查看索引---相当于 showdatabases

192.168.10.102:9200/_cat/indices

image.png

索引一个文档————————mysql(保存)

保存一个数据,要知道保存到那个索引的那个类型下,指定使用那个标识。 可以用put和post来保存

首先用put带id的来保存

192.168.10.102:9200/customer/external/1

image.png 如果再次发送,他就会变成一个更新操作。版本号会发生变化。 image.png

image.png

接下来用post不带id的来保存。

如果加了id,他还是一个更新操作

image.png 不加id,他就会随即生成一个。

image.png

那put如果不带id呢?

image.png 会报错。

小总结

put只能带id的保存。post既可以带id保存,也可以不带id保存。

查询

查询发送get请求就可以

192.168.10.102:9200/customer/external/1

image.png 每个字段代表的意思

"_index" : "customer",//在哪个索引
"_type" : "external", //在哪个类型
"_id": "1",           //记录id
"_version": 2,        //版本号
"_seq_no" : 1,        //并发控制字像,每次更新就会+1,用来做乐观锁
" _primary_term": 1,  //同上,主分片重新分配,如重启,就会变化"found": true,
"_source": {
//真正的内容
"name" : "lao bai"
}

_primary_term用来做更新操作的时候,可以在后面加上参数。 比如

  192.168.10.102:9200/customer/external/1?if_seq_no=17&if_primary_term=1

通过“if_seq_no=16&if_primary_term=1 ”,当序列号匹配的时候,才进行修改,否则不修改

image.png 这个是发送成功的。 如果再次发送请求就会报错

image.png

更新文档

(1)POST更新文档,带有_update

post更新的操作就是在后面添加一个/_update

带update的时候,记得把他的数据都写到doc里面

192.168.10.102:9200/customer/external/1/_update

image.png 如果再次点击发送请求,他的状态会显示noop,表示没有更新成功,并且版本号不会发生变化。

image.png

(2)POST更新文档,不带_update

image.png

image.png

在更新过程中,重复执行更新操作,数据也能够更新成功,不会和原来的数据进行对比。

删除索引/文档

删除文档

delete请求

192.168.10.102:9200/customer/external/1

image.png 再次点击send

image.png 会发现result里面会报notfound

删除索引

直接把索引放上去就ok

192.168.10.102:9200/customer

image.png

bulk批量处理

POST customer/external/_bulk
"index":{"_id":"1""}
{"name": "John Doe" }  //两个为一组
"index":{"_id":"2"
"{"name": "Jane Doe"}
语法格式:
{ action: { metadata lln{request body]\n
{ action: { metadata ln{request body}\n
复杂实例:
POST /_bulk
{ "delete" : { "_index": "website" , "_type": "blog"."_id": "123" l
{"create": { "_index" : "website" , "_type": ""blog","_id": "123" "}}
{"title"": "My first blog post" }
{ "index": { "_index" : "website", " _type": "blog" }}

接下来,我们做个测试

POST customer/external/_bulk
{"index":{"_id":"1"}}
{"name":"John Doe"}
{"index":{"_id":"2"}}
{"name":"John Doe"}

image.png 每条操作都是独立的,也就说,上面的操作不会影响到下一条的执行。

在测一个复杂的

POST /_bulk
{"delete":{"_index":"website","_type":"blog","_id":"123"}}
{"create":{"_index":"website","_type":"blog","_id":"123"}}
{"title":"my first blog post"}
{"index":{"_index":"website","_type":"blog"}}
{"title":"my second blog post"}
{"update":{"_index":"website","_type":"blog","_id":"123"}}
{"doc":{"title":"my updated blog post"}}

  
  
  返回的结果
  
  
  #! Deprecation: [types removal] Specifying types in bulk requests is deprecated.
{
  "took" : 231,  //消耗的时间
  "errors" : false,  //是否错误
  "items" : [
    {
      "delete" : {
        "_index" : "website",
        "_type" : "blog",
        "_id" : "123",
        "_version" : 1,
        "result" : "not_found",   //返回结果,我们没有个这个类型
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 0,
        "_primary_term" : 1,
        "status" : 404
      }
    },
    {
      "create" : {
        "_index" : "website",
        "_type" : "blog",
        "_id" : "123",
        "_version" : 2,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 1,
        "_primary_term" : 1,
        "status" : 201
      }
    },
    {
      "index" : {
        "_index" : "website",
        "_type" : "blog",
        "_id" : "j9s8OYIBhvx6bCrS0D2N",
        "_version" : 1,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 2,
        "_primary_term" : 1,
        "status" : 201
      }
    },
    {
      "update" : {
        "_index" : "website",
        "_type" : "blog",
        "_id" : "123",
        "_version" : 3,
        "result" : "updated",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 3,
        "_primary_term" : 1,
        "status" : 200
      }
    }
  ]
}

发现没有问题。

接下来,我们引入数据来测试。下面这个是测试地址。

elasticsearch/accounts.json at v7.4.2 · elastic/elasticsearch (github.com)

POST bank/account/_bulk.

后面加上我们的测试数据

es进阶

检索

ES支持两种基本方式检索;

  • 通过REST request uri 发送搜索参数 (uri +检索参数);
  • 通过REST request body 来发送它们(uri+请求体);

信息检索

一切检索从_search开始
GET bank/_search   //检索bank下所有信息,包括type和docs

GET bank/_search?q=*&sort=account_number:asc   //请求参数方式检索

响应结果解释:
took -Elasticsearch    执行搜索的时间(毫秒)
time_out-              告诉我们搜索是否超时
_shards-               告诉我们多少个分片被搜索了,以及统计了成功/失败的搜索分片
hits-                  搜索结果
hits.total-            搜索结果
hits.hits-             实际的搜索结果数组(默认为前10的文档)sort-结果的排序key(键)(没有则按score排序)
score和 max_score     -相关性得分和最高得分(全文检索用)

第一种实例

GET bank/ _search?q=*&sort=account_number:asc

第二种实例 QueryDSL

GET bank/_search
{
  "query": {
    "match_all": {}  //找* ,也可以使用 _source:["field","field"],里面填参数,找具体的内容
  },
  "sort": [   //表示用什么字段排序
    {
      "balance": {
        "order": "desc"
      }
    }
  ],
  "from": 0,  //从哪开始
  "size": 50  //找多少数据
}

全文检索

GET bank/_search
{
  "query": {
    "match": {
      "address": "mill lone"
    }
  }
}

全文检索会按照评分排序来返回结果

image.png

这样式儿的 这样评分都差不多

有个短语匹配,他会匹配短语里面所有的词儿。

短语匹配&字段匹配

GET bank/_search
{
  "query": {
    "match_phrase": {
      "address": "mill road"
    }
  }
}

短语匹配是只要有,他就可以匹配到。 image.png 而match的字段匹配则是要精准 image.png

文本字段的匹配,使用keyword,匹配的条件就是要显示字段的全部值,要进行精确匹配的。

match_phrase是做短语匹配,只要文本中包含匹配条件,就能匹配到。

多字段匹配

state或者address中包含mill,并且在查询过程中,会对于查询条件进行分词。

GET bank/_search
{
  "query": {
    "multi_match": {
      "query": "mill",
      "fields": [
        "state",
        "address"
      ]
    }
  }
}

bool复核查询

复合语句可以合并,任何其他查询语句,包括符合语句。这也就意味着,复合语句之间 可以互相嵌套,可以表达非常复杂的逻辑。

must:必须达到must所列举的所有条件

must_not,必须不匹配must_not所列举的所有条件。

should,应该满足should所列举的条件。满足的话加的分更多。

GET bank/_search
{
   "query":{
        "bool":{
             "must":[  //must里面包含查询条件match/match_all/match_phrase
              {"match":{"address":"mill"}},  
              {"match":{"gender":"M"}}
             ]
         }
    }
}

filter

并不是所有的查询都需要产生分数,特别是哪些仅用于filtering过滤的文档。为了不计算分数,elasticsearch会自动检查场景并且优化查询的执行。

GET bank/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "address": "mill"
          }
        }
      ],
      "filter": {
        "range": {
          "balance": {
            "gte": "10000",
            "lte": "20000"
          }
        }
      }
    }
  }
}

这里先是查询所有匹配address=mill的文档,然后再根据10000<=balance<=20000进行过滤查询结果

没有添加之前是4个 image.png 添加完之后是1个

image.png

term

match在匹配时会对所查找的关键词进行分词,然后按分词匹配查找,而term会直接对关键词进行查找。一般模糊查找的时候,多用match,而精确查找时可以使用term。 ( 参考)

聚合

聚合提供了从数据中分组和提取数据的能力。最简单的聚合方法大致等于SQL Group by和SQL聚合函数。在elasticsearch中,执行搜索返回this(命中结果),并且同时返回聚合结果,把以响应中的所有hits(命中结果)分隔开的能力。这是非常强大且有效的,你可以执行查询和多个聚合,并且在一次使用中得到各自的(任何一个的)返回结果,使用一次简洁和简化的API啦避免网络往返。

"aggs":{
    "aggs_name这次聚合的名字,方便展示在结果集中":{
        "AGG_TYPE聚合的类型(avg,term,terms)":{}
     }
}

按照年龄聚合,并且求这些年龄段的这些人的平均薪资

GET bank/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "ageAgg": {
      "terms": {
        "field": "age",
        "size": 100
      },
      "aggs": {
        "ageAvg": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  },
  "size": 0
}

映射

在ES中映射类似关系型数据库中的schema,描述了文档中有哪些字段和字段的类型,如string,data,int等。而 类型 是一组具有相关性的映射组成,然后使用"properties"来表示该类型中可能包含的字段属性。

部分如下

{
  "bank" : {
    "mappings" : {
      "properties" : {
        "account_number" : {
          "type" : "long"
        },
        "address" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "age" : {
          "type" : "long"
        },
        "balance" : {
          "type" : "long"
        },
        "city" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "email" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
          
          

具体的参考