ElasticSearch初体验

以你之姓@ 2022-05-16 04:27 255阅读 0赞

需要明白的问题

  1. 什么是倒排索引?它的组成是什么?
  2. 常见的相关性算分方法有哪些?
  3. 为什么查询语句没有返回预期的文档?
  4. 常用的数据类型有哪些?Text和Keyword的区别是什么?
  5. 集群是如何搭建起来的?是如何实现故障转移的?
  6. Shard具体是由什么组成的?

Elastic Stack

构建在开源基础之上, Elastic Stack 让您能够安全可靠地获取任何来源、任何格式的数据,并且能够实时地对数据进行搜索、分析和可视化

Elasticsearch 是基于 JSON 的分布式搜索和分析引擎,专为实现水平扩展、高可用和管理便捷性而设计。

Kibana 能够以图表的形式呈现数据,并且具有可扩展的用户界面,供您全方位配置和管理 Elastic Stack。

Logstash 是动态数据收集管道,拥有可扩展的插件生态系统,能够与 Elasticsearch 产生强大的协同作用。

Beats 是轻量型采集器的平台,从边缘机器向 Logstash 和 Elasticsearch 发送数据。

基础概念

  • 文档 Document :用户存储在ES中的数据文档
  • 索引 Index :由具有一些相同字段的文档的集合
  • 类型 Type : 允许将不同类型的文档存储在同一索引中,6.0开始官方不允许在一个index下建立多个type,统一type名称:doc
  • 节点 Node :一个Elasticsearch的运行实例,是集群的构成单元,存储部分或全部数据,并参与集群的索引和搜索功能
  • 集群 Cluster :由一个或多个节点组成的集合,共同保存所有的数据,对外提供服务(包括跨所有节点的联合索引和搜索功能等)
  • 分片 Shards :分片是为了解决存储大规模数据的问题,将数据切分分别存储到不同的分片中
  • 副本 Replicas :副本可以在分片或节点发生故障时提高可用性,而且由于可以在所有副本上进行并行搜索,所以也可以提高集群的吞吐量
  • 近实时 Near Realtime(NRT):从索引文档到可搜索文档的时间有一点延迟(通常为一秒)

note:

  1. 在创建索引的时候如果没有配置索引Mapping,一个索引默认有5个shard和1个副本,一个索引总共有10个shard(算上副本shard)
  2. Elasticsearch 的shard实际上是一个Lucene索引,截止Lucene-5843,一个Lucene索引限制的最大文档数为2,147,483,519 (= Integer.MAX_VALUE - 128)

安装Elasticsearch & Kibana

ES和Kibana的安装很简单,前提需要先安装好Java8,然后执行以下命令即可

elasticsearch单节点最简安装
  1. # 在Ubuntu16.04上安装,方式有很多种,选择二进制压缩包的方式安装
  2. # 1. 在普通用户家目录下,下载压缩包
  3. curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.3.2.tar.gz
  4. # 2. 解压
  5. tar -xvf elasticsearch-6.3.2.tar.gz
  6. # 3. 移动至/opt目录下
  7. sudo mv elasticsearch-6.3.2 /opt
  8. # 4. 修改配置文件elasticsearch.yml中的 network.host 值为 0.0.0.0,其他的配置参考官方文档
  9. cd /opt/elasticsearch-6.3.2
  10. vi config/elasticsearch.yml
  11. # 5. 启动单节点,然后浏览器访问host:9200即可看到ES集群信息
  12. bin/elasticsearch

image

kibana最简安装
  1. wget https://artifacts.elastic.co/downloads/kibana/kibana-6.3.2-linux-x86_64.tar.gz
  2. shasum -a 512 kibana-6.3.2-linux-x86_64.tar.gz
  3. tar -xzf kibana-6.3.2-linux-x86_64.tar.gz
  4. sudo mv kibana-6.3.2-linux-x86_64 /opt
  5. cd /opt/kibana-6.3.2-linux-x86_64
  6. # 修改 config/kibana.yml中 server.host: 0.0.0.0
  7. # 启动Kibana,访问 host:5601即可进入kibana界面

image

交互方式 Rest API

Elasticsearch集群对外提供RESTful API

  • Curl命令行
  • Kibana Devtools
  • Java API
  • 其他各种API,如Python API等

note:
我们后面主要使用 Kibana Devtools 这种交互方式

image

数据类型

  • 字符串: text(分词), keyword(不分词)
  • 数值型: long, integer, byte, double, float, half_float, scaled_float
  • 布尔: boolean
  • 日期: date
  • 二进制: binary
  • 范围类型: integer_range, float_range, long_range, double_range, date_range
  • 复杂数据类型: Array, Object, Nested
  • 地理: geo_point, geo_shape
  • 专业: ip,completion, token_count, murmur3, Percolator, join
  • 组合的

探索ES集群

使用_cat API探索集群的健康情况
  1. GET /_cat/health?v
  2. # 结果
  3. epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
  4. 1534319381 15:49:41 elasticsearch green 3 3 118 59 0 0 0 0 - 100.0%

集群的健康状态(status)有三种:
- green:一切正常(集群功能齐全)
- yellow:所有数据都可用,但存在一些副本未分配(群集功能齐全)
- red:一些数据由于某种原因不可用(群集部分功能失效)

查看节点信息
  1. GET /_cat/nodes?v
  2. # 结果(我的ES集群安装了三个节点)
  3. ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
  4. 10.100.97.207 30 96 13 0.15 0.08 0.08 mdi * master
  5. 10.100.97.246 68 96 3 0.00 0.00 0.00 mdi - hadoop2
  6. 10.100.98.22 15 97 2 0.00 0.02 0.04 mdi - hadoop3
查看索引信息
  1. GET /_cat/indices?v
  2. # 结果
  3. health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
  4. green open logstash-2015.05.20 4BjPjpq6RhOSCNUPMsY0MQ 5 1 4750 0 46.8mb 24.5mb
  5. green open logstash-2015.05.18 mDkUKHSWR0a8UeZlKzts8Q 5 1 4631 0 45.6mb 23.8mb
  6. green open hockey g1omiazvRSOE117w_uy_wA 5 1 11 0 45.3kb 22.6kb
  7. green open .kibana AGdo8im_TxC04ARexUxqxw 1 1 143 10 665.6kb 332.8kb
  8. green open shakespeare 5009bDa7T16f5qTeyOdTlw 5 1 111396 0 43.9mb 22mb
  9. green open logstash-2015.05.19 az4Jen4nT7-J9yRYpZ0A9A 5 1 4624 0 44.7mb 23.1mb
  10. ...

操作数据

插入文档并查询
  1. # 插入一个文档
  2. PUT /customer/_doc/1?pretty
  3. {
  4. "name": "John Doe"
  5. }
  6. # 结果
  7. {
  8. "_index": "customer",
  9. "_type": "_doc",
  10. "_id": "1",
  11. "_version": 1,
  12. "result": "updated",
  13. "_shards": {
  14. "total": 2,
  15. "successful": 2,
  16. "failed": 0
  17. },
  18. "_seq_no": 1,
  19. "_primary_term": 1
  20. }
  21. # 查询该文档
  22. GET /customer/_doc/1
  23. #结果
  24. {
  25. "_index": "customer",
  26. "_type": "_doc",
  27. "_id": "1",
  28. "_version": 1,
  29. "found": true,
  30. "_source": {
  31. "name": "John Doe"
  32. }
  33. }

note:

  1. customer 为索引名,_doc 为type,1为文档_id,需要注意的是:在es6.x建议索引的type值固定为_doc,在之后的版本将删除type了;文档id若不指定,es会自动分配一个_id给文档
  2. 插入文档后,查看索引信息GET /_cat/indices?v可以看到多了 customer 的索引信息
  3. 文档结果,_source字段是原始的json内容,其他的为文档元数据
文档元数据

用于标注文档的元信息

  • _index: 文档所在的索引名
  • _type: 文档所在的类型名
  • _id: 文档的唯一id
  • _uid: 组合id,由_type和_id组成(6.0开始_type不再起作用,同_id一样)
  • _source: 文档的原始json数据,可以从这里获取每个字段的内容
  • _all: 整合所有字段内容到该字段,默认禁用
  • _routing 默认值为 _id,决定文档存储在哪个shard上:shard_num = hash(_routing) % num_primary_shards
删除索引
  1. DELETE customer
  2. #结果
  3. {
  4. "acknowledged": true
  5. }
  6. GET /_cat/indices?v
  7. # 再次查看索引信息,可以发现 customer 不存在,已被删除
更新文档
  1. PUT /customer/_doc/1?pretty
  2. {
  3. "name": "John Doe"
  4. }
  5. POST /customer/_doc/1/_update
  6. {
  7. "doc": { "name": "Jane Doe" }
  8. }
  9. POST /customer/_doc/1/_update
  10. {
  11. "doc": { "name": "Jane Doe", "age": 20 }
  12. }
  13. # 可以看到 \_version的值一直在增加
删除文档
  1. DELETE /customer/_doc/2
批量操作

es提供了_bulk API供批量操作,可以提高索引、更新、删除等操作的效率

_bulk操作的类型有四种:
- index 索引:若已存在,则覆盖,文档不存在则创建
- create 创建:文档不存在则异常
- delete 删除
- update 更新

  1. # _bulk 任务:
  2. # 1. index创建 customer索引下id为3的文档
  3. # 2. delete删除 customer索引下id为3的文档
  4. # 3. create创建 customer索引下id为3的文档
  5. # 4. update更新 customer索引下id为3的文档
  6. POST _bulk
  7. {
  8. "index":{
  9. "_index":"customer","_type":"_doc","_id":"3"}}
  10. {
  11. "name":"whirly"}
  12. {
  13. "delete":{
  14. "_index":"customer","_type":"_doc","_id":"3"}}
  15. {
  16. "create":{
  17. "_index":"customer","_type":"_doc","_id":"3"}}
  18. {
  19. "name":"whirly2"}
  20. {
  21. "update":{
  22. "_index":"customer","_type":"_doc","_id":"3"}}
  23. {
  24. "doc":{
  25. "name":"whirly3"}}

image

note:

  1. 批量查询用的是 Multi Get API

探索数据

一个简单的数据集,数据结构如下:

  1. {
  2. "account_number": 0,
  3. "balance": 16623,
  4. "firstname": "Bradshaw",
  5. "lastname": "Mckenzie",
  6. "age": 29,
  7. "gender": "F",
  8. "address": "244 Columbus Place",
  9. "employer": "Euron",
  10. "email": "bradshawmckenzie@euron.com",
  11. "city": "Hobucken",
  12. "state": "CO" }

导入这个简单的数据集到es中

  1. # 下载
  2. wget https://raw.githubusercontent.com/elastic/elasticsearch/master/docs/src/test/resources/accounts.json
  3. # 导入
  4. curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_doc/_bulk?pretty&refresh" --data-binary "@accounts.json"

上述命令是通过 _bulk API 将 account.json 的内容插入 bank 索引中,type 为 _doc

  1. # account.json的内容:
  2. {
  3. "index":{
  4. "_id":"1"}}
  5. {
  6. "account_number":1,"balance":39225,"firstname":"Amber","lastname":"Duke","age":32,"gender":"M","address":"880 Holmes Lane","employer":"Pyrami","email":"amberduke@pyrami.com","city":"Brogan","state":"IL"}
  7. ...
  8. # 导入完成后可以看到 bank 索引已存在 1000 条数据
  9. GET bank/_search
查询数据 API

任务:查询所有数据,根据 account_number 字段升序排序

  1. URI Search 方式

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

  2. Request Body Search 方式

    GET /bank/_search
    {
    “query”: { “match_all”: {} },
    “sort”: [

    1. { "account_number": "asc" }

    ]
    }

结果

  1. {
  2. "took": 41,
  3. "timed_out": false,
  4. "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 },
  5. "hits": { "total": 1000, "max_score": null, "hits": [ { "_index": "bank", "_type": "account", "_id": "0", "_score": null, "_source": { "account_number": 0, "balance": 16623, "firstname": "Bradshaw", "lastname": "Mckenzie", "age": 29, "gender": "F", "address": "244 Columbus Place", "employer": "Euron", "email": "bradshawmckenzie@euron.com", "city": "Hobucken", "state": "CO" }, "sort": [ 0 ] }... ] } }

各个参数意思:
- took:本次查询耗费的时间(单位:毫秒)
- timed_out:是否超时
- _shards:本次查询搜索的 shard 的数量,包括成功的和失败的
- hits:查询结果
- hits.total:匹配的文档数量
- hits.hits:匹配的文档,默认返回10个文档
- hits.sort:排序的值
- _score:文档的得分
- hits.max_score:所有文档最高的得分

简要介绍 Query DSL

这个Elasticsearch提供的基于 json 的查询语言,我们通过一个小任务来了解一下

任务要求:

  1. 查询 firstname 中为 “R” 开头,年龄在 20 到 30 岁之间的人物信息
  2. 限制返回的字段为 firstname,city,address,email,balance
  3. 根据年龄倒序排序,返回前十条数据
  4. 对 firstname 字段进行高亮显示
  5. 同时求所有匹配人物的 平均balance

    GET bank/_search
    {
    “query”: {

    1. "bool": {
    2. "must": [
    3. {
    4. "match_phrase_prefix": {
    5. "firstname": "R"
    6. }
    7. }
    8. ],
    9. "filter": {
    10. "range": {
    11. "age": {
    12. "gte": 20,
    13. "lte": 30
    14. }
    15. }
    16. }
    17. }

    },
    “from”: 0,
    “size”: 10,
    “sort”: [

    1. {
    2. "age": {
    3. "order": "desc"
    4. }
    5. }

    ],
    “_source”: [

    1. "firstname",
    2. "city",
    3. "address",
    4. "email",
    5. "balance"

    ],
    “highlight”: {

    1. "fields": {
    2. "firstname": {}
    3. }

    },
    “aggs”: {

    1. "avg_age": {
    2. "avg": {
    3. "field": "balance"
    4. }
    5. }

    }
    }

其中:
- query 部分可以写各种查询条件
- from, size 设置要返回的文档的起始序号
- sort 设置排序规则
- _source 设置要返回的文档的字段
- highlight 设置高亮的字段
- aggs 为设置聚合统计规则

更多查询示例
  • match_all 查询 bank 索引所有文档

    GET /bank/_search
    {
    “query”: {

    1. "match_all": {}

    },
    “size”: 2
    }

  • match 全文搜索,查询 address 字段值为 mill lane 的所有文档

    GET /bank/_search
    {
    “query”: {

    1. "match": {
    2. "address": "mill lane"
    3. }

    }
    }

  • match_phrase 短语匹配

    GET /bank/_search
    {
    “query”: {

    1. "match_phrase": {
    2. "address": "mill lane"
    3. }

    }
    }

note:
match 和 match_phrase 的区别:
- match 中会分词,将 mill lane 拆分为 mill 和 lane, 实际查询 address 中有 mill 或者 lane 的文档
- match_phrase:将 mill lane 作为一个整体查询,实际查询 address 中有 mill lane 的文档

  • 布尔查询(多条件查询)

    GET /bank/_search
    {
    “query”: {

    1. "bool": {
    2. "must": [
    3. { "match": { "age": "40" } }
    4. ],
    5. "must_not": [
    6. { "match": { "state": "ID" } }
    7. ]
    8. }

    }
    }

  • 布尔查询-过滤
    查询 bank 索引中 balance 值在 20000 到 30000 之间的文档

    GET /bank/_search
    {
    “query”: {

    1. "bool": {
    2. "must": { "match_all": {} },
    3. "filter": {
    4. "range": {
    5. "balance": {
    6. "gte": 20000,
    7. "lte": 30000
    8. }
    9. }
    10. }
    11. }

    }
    }

  • 聚合查询
    对所有文档进行聚合,state 值相同的分到同一个桶里,分桶结果命名为 group_by_state ,再对每个桶里的文档的 balance 字段求平均值,结果命名为 average_balance,通过设置 size 的值为0,不返回任何文档内容

    GET /bank/_search
    {
    “size”: 0,
    “aggs”: {

    1. "group_by_state": {
    2. "terms": {
    3. "field": "state.keyword"
    4. },
    5. "aggs": {
    6. "average_balance": {
    7. "avg": {
    8. "field": "balance"
    9. }
    10. }
    11. }
    12. }

    }
    }

分别计算 age 值在 20~30 ,30~40,40~50 三个年龄段的男和女的平均存款balance

  1. GET /bank/_search
  2. {
  3. "size": 0,
  4. "aggs": {
  5. "group_by_age": {
  6. "range": {
  7. "field": "age",
  8. "ranges": [
  9. {
  10. "from": 20,
  11. "to": 30
  12. },
  13. {
  14. "from": 30,
  15. "to": 40
  16. },
  17. {
  18. "from": 40,
  19. "to": 50
  20. }
  21. ]
  22. },
  23. "aggs": {
  24. "group_by_gender": {
  25. "terms": {
  26. "field": "gender.keyword"
  27. },
  28. "aggs": {
  29. "average_balance": {
  30. "avg": {
  31. "field": "balance"
  32. }
  33. }
  34. }
  35. }
  36. }
  37. }
  38. }
  39. }

更多内容请访问我的个人网站: http://laijianfeng.org
参考文档:

  1. elasticsearch 官方文档 Getting Started
  2. 慕课网 Elastic Stack从入门到实践

发表评论

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

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

相关阅读

    相关 ElasticSearch体验

    需要明白的问题 1. 什么是倒排索引?它的组成是什么? 2. 常见的相关性算分方法有哪些? 3. 为什么查询语句没有返回预期的文档? 4. 常用的数据类型有哪些?