探索ES-什么?你还不知道ES?(一)

2,479 阅读11分钟

前文回顾

之前有讲过碎碎念-探索计划(九),而且也陆续发了9篇关于Spring Boot的探索系列(当然远远还没有结束)。一个是因为碎碎念-以终为始(四)中提到,我的其中一条目标是能够做出一个有价值的产品,所以探索计划也要扩展一名对未来能够做出一个产品有帮助的新成员ES了。另外一个就是公司也使用到了ES,所以要加快输入和输出的脚步了。今天就来带你从小白到入门ES

介绍

ES是一个搜索引擎,底层基于LucceeElasticSearch刚开始是因为在伦敦的一个程序员要给老婆搜索菜谱才开发出来的。东西被创造出来都是由需求驱动的,然后最后这个东西存在的意义是否还是最初的需求就不一定了。

环境

eskibana都是5.0.2版本,起码今天是这个版本的。不过之后可能考虑升级到6.7版本。

安装步骤

单节点部署

  1. 下载es和kibana的安装包,解压
  2. 修改配置文件 elasticsearch.yml
  • cluster.name:my-application(当前集群名称)
  • node.name:node-1(当前节点名称)
  • network.host:localhost(最好修改为本机IP地址,在集群环境下localhost可能会有问题)
  • http.port:9200(ES端口)

集群部署配置

  • discovery.zen.ping.unicast.host:["host1","host2"] (其他节点的IP地址和自己节点的地址)
  • dicovery.zen.minimum_master_nodes:(node_numbers / 2) + 1

修改JVM内存大小

  1. 修改jvm.options中内存大小 (考虑到性能不要设置太大,要为os cache预留一部分内存,保证磁盘上面的数据大小和os cache的大小是一致的,不懂没有关系,后来慢慢讲解)

启动

  1. 启动 bin/elasticsearch -d (-d表示用守护模式打开)
  2. 访问查看ES状态 curl http://localhost:9200 (有些情况下不能使用localhost,而是必须使用ip;看绑定的地址是什么,如果显示指定了IP,那么必须是IP) 可以返回结果
{
  "name" : "node-1",
  "cluster_name" : "my-application",
  "cluster_uuid" : "0P_LVzBYS6qVl8Wo9o817Q",
  "version" : {
    "number" : "5.0.2",
    "build_hash" : "f6b4951",
    "build_date" : "2016-11-24T10:07:18.101Z",
    "build_snapshot" : false,
    "lucene_version" : "6.2.1"
  },
  "tagline" : "You Know, for Search"
}

Kibana安装

虽然可以不安装Kibana,但是一般安转完ElasticSearch都会安装Kibana。(之后讲解详细安装kibana详细步骤)

查看状态

安装完ES之后,一般人肯定会想要知道一些ES的状态基本信息,就好像安装完数据库之后,就想要知道这个数据库安装在哪个ip地址,里面有多少张表,表占用了多少空间等等基本信息。那么对于ES来说就是下面几点。

查看健康状态

查看健康状态其实是和ES分片有没有被分配有关系。存在主分片没有被分配是Red。主分片已经被分配,但是副本分片没有被分配是Yellow。刚开始学习最常见的Yellow情况就是ES只启动了一个节点,副本分片不能和主分片在同一个节点上,导致副本分片无法被分配。 主分片和副本分片都分配的情况下,集群才是Green。

GET _cat/health?v&pretty

查看节点状态

看完了集群的健康状态之后,可以查看每一个节点的详细信息,内存使用情况,cpu的使用情况等

GET /_cat/nodes?v

参考 cat-nodes

查看ES状态全部索引

查看索引的存在状态

GET _cat/indices?v&pretty

查看ES节点配置信息

GET _nodes?pretty

查看分片信息

可以查看某个特定索引的分片情况,状态、在哪个节点等

GET _cat/shards/indexName*?pretty&v

一般默认的好像不全面,可以使用下面这个语句。

GET _cat/shards?h=index,shard,prirep,state,unassigned.*&pretty&v

通过这个语句可以看到分片级别的状态,我发现ES有些API也不是很好。indices这个API都没有说明没有分片的个数。但是health里面又存在整个集群下没有分片的个数。

分析分片没有被分配原因

GET /_cluster/allocation/explain
{
  "index":"bank",
  "shard":0,
  "primary":false
}

基本概念

Node

节点。每个ES实例都是一个节点。可以对每个启动的ES节点设置一个唯一的节点名称,在elasticsearch.ymlnode.name字段来指定。在ES中节点存在不同的类型,首先数据节点是存储数据的。每次一个请求进来,因为每一个节点都拥有协调能力,在请求进来的时候,节点自动成为协调节点。

另外还有master节点和数据节点。master节点存储了ES集群的元信息。当请求进入到master节点时,master节点根据hash算法和分片信息,将请求打入到对应的分片上面。

Cluster

集群。多个ES节点可以组成一个集群。只要节点的集群名字相同即可。在elasticsearch.ymlcluster.name来指定。只要cluster.name相同,那么在节点之间网络连通的情况下,多个节点会自动组成一个集群。

Index

index就是ES的索引,简单理解为Mysql的数据库。一个ES节点可以有多个索引。

Type

索引的类型。简单理解为Mysql的表。对一个索引进行逻辑上面的区分。一个索引下面可以有多个type。

Document

文档。相当于数据库里面的记录。一个type下面可以多个文档。

Shards And Replicas

分片和副本。一个索引默认会有5个分片和1个备份,会产生5个主分片和5个备份分片。5个分片会分散到集群中不同的节点上面,另外需要注意的是主分片和副本分片一定不能再同一个节点上。如果有多个副本分片,相同数据的副本分片也不能在同一个节点上面。不然会导致分片没有被分配。

Replica是ES实现高可用的一种方式,之后会进行深入的扩展。

也可能会遇到一些问题?比如ES没有分配分片?之后会有文章会讲到故障的排查。

建表、建立索引

跟数据库一样,要使用数据库首先要建库,在ES里面就是建立索引。索引index相当于数据库,type相当于表,id相当于文档的主键。

PUT /people?pretty

然后可以使用查询全部索引的语句查看,可以发现多了一条记录。

ES与关系型数据库不同的地方在于ES是可以动态增加字段的。 所以,建索引(建表)的时候可以不指定所有的字段,当有新的字段存在时,可以使用动态映射的方式(dymatic mapping)来指定字段类型。

要注意的点是一个新的字段是什么类型只有在第一次这个字段进去到ES时会被指定,其他时候不会被指定。

查看节点状态能够看到只是yellow,因为当前只有一个节点,副本分片不能和主分片在同一个节点上,导致副本分片没有被分配,没有实现高可用。

epoch      timestamp cluster        status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1554208915 20:41:55  my-application yellow          1         1      6   6    0    0        6             0                  -                 50.0%
  • docs.count:表示该索引下面所有的文档数

插入ES操作

有了索引之后,可以使用Restful接口插入数据到ES中。使用下面的语句建立。

PUT /people/info/1?pretty
{
  "name": "John Doe"
}

这里的1就是创建的新文档的id,可以指定也可以不指定。建议不指定,会快一些。成功之后提示如下。

{
  "_index": "people",
  "_type": "info",
  "_id": "1",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "created": true
}

这里使用的是PUT指令,表示更新指定的文档。因为是更新指定的文档,所以这里第三个参数1是必须填写的。ES的更新文档的操作其实是会判断文档存不存在,如果不存在那么就是新建一条新的文档。如果想要直接真正新建一条文档,而且这种情况下也不需要填写id,可以直接新增,可以使用POST方式,如下。

POST /people/info?pretty
{
  "name": "John Doe"
}

返回格式如下。

{
  "_index": "people",
  "_type": "info",
  "_id": "AWnjMpZuDCZ3hLyrI_RF",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "created": true
}

还可以使用bulk来批量插入数据,如下所示。要注意的是bulk对于json的格式有严格的要求,每一个完整的json只能是一行,不能多行。而已一个完整json之后必须换行。

bulk还可以这样写

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

ES查询

那么现在测试数据都进入了ES,如何查询呢?一般人可能会有两种查询需求,一种是查询某个索引下面所有的数据,一种是根据id来查询,另外就是根据某一个具体的字段来查询。一个一个来看。 首先所有的查询都必须加上_search字段在请求的末尾。

  • 查询索引下面全部的文档。只要在需要查询的index后面加上_search就好了。
GET /people/_search

也可以这样写。

GET /people/_search
{
  "query": { "match_all": {} }
}

返回如下。

{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 6,
    "max_score": 1,
    "hits": [
      {
        "_index": "people",
        "_type": "info",
        "_id": "AWnjMpZuDCZ3hLyrI_RF",
        "_score": 1,
        "_source": {
          "name": "John Doe"
        }
      },
      .....省略后面5条数据
  • 根据某一个字段来查询,使用match查询。
GET /people/_search
{
  "query": {
    "match": {
      "name": "John"
    }
  }
}

这里就是搜索所有name中含有John的文档。这里的含有就是指定的字段必须是全文索引的。 但是想要精确地搜索nameJohn的文档。就可以使用term查询,而不是match查询。

POST _search
{
  "query": {
    "term" : { "name" : "John" } 
  }
}

ES更新文档

PUT /people/info/1?pretty
{
  "name": "John Doe"
}

通过上面这个更新的语句就可以将文档更新。但是这里重点要讲一下,这个更新操作是覆盖更新。在ES的底层,更新操作会先找到这条ES数据,然后用新的ES数据给完全覆盖掉这条数据。

但是往往有时候我们想要实现的是增量更新,那么应该怎么办呢?

PUT /people/info/1/_update
{
"doc":{
"name":"Mike"
}
}

通过这种方式就可以将某一个文档增量更新了。但是如果要删除某一个文档里面的一个字段,应该只能用全量的方式了吧。注意这里的语法一定要加上doc,url上面要加上_update。

查询全部文档

GET _search
{
  "query": {
    "match_all": {}
  }
}

所有的命令可以总结为<REST Verb> /<Index>/<Type>/<ID>

排序

POST /_search
{
   "query" : {
      "term" : { "product" : "chocolate" }
   },
   "sort" : [
      {"price" : {"order" : "asc"}}
   ]
}

聚合操作

ES不仅提供了数据的搜索还提供了数据的检索,聚合。下面就来聊一聊聚合操作。

简单聚合

先来个简单的聚合操作。 首先下载app.json的原始数据。需求是按state来分组计算出分组中document的数量,然后计算分组内金额的平均数。size:0表示不会返回命中的document,只是返回分组,在ES中分组又被称之为桶bucket,默认返回10个桶。

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword"
      },
      "aggs": {
        "average_balance77": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}

得到如下结果

{
  "took": 75,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1000,
    "max_score": 0,
    "hits": []//size:0,所示hits不展示
  },
  "aggregations": {
    "group_by_state": {
      "doc_count_error_upper_bound": 20,
      "sum_other_doc_count": 770,
      "buckets": [//第一个bucket
        {
          "key": "ID",
          "doc_count": 27,
          "average_balance77": {//balance平均数
            "value": 24368.777777777777
          }
        },
        {//第二个bucket
          "key": "TX",
          "doc_count": 27,
          "average_balance77": {//balance平均数
            "value": 27462.925925925927
          }
        },

嵌套聚合

看过简单的聚合之后,是不是迫不及待想要看看复杂的聚合操作呢?先上例子。

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_age": {
      "range": {
        "field": "age",
        "ranges": [
          {
            "from": 20,
            "to": 30
          },
          {
            "from": 30,
            "to": 40
          },
          {
            "from": 40,
            "to": 50
          }
        ]
      },
      "aggs": {
        "group_by_gender": {
          "terms": {
            "field": "gender.keyword"
          },
          "aggs": {
            "average_balance": {
              "avg": {
                "field": "balance"
              }
            }
          }
        }
      }
    }
  }
}

其实就是聚合之后可以再次对于已经聚合之后的数据进行聚合操作。就好像sql中group by之后,再对group by之后的数据再次进行group by操作。分桶之后可以再次分桶。

小结

好了,看完了上文,关于ES的安装、部署、查询、更新、聚合等基本操作都掌握了吗?

关于写作

以后这里每天都会写一篇文章,题材不限,内容不限,字数不限。尽量把自己每天的思考都放入其中。

如果这篇文章给你带来了一些帮助,可以动动手指点个赞,顺便关注一波就更好了。

如果上面都没有,那么写下读完之后最想说的话?有效的反馈和你的鼓励是对我最大的帮助。

另外打算把博客给重新捡起来了。欢迎大家来访问吃西瓜

我是shane。今天是2019年8月16日。百天写作计划的第二十三天,23/100。