ElasticSearch初探

Elasticsearch是一个分布式搜索和数据分析引擎,基于Java开发并利用Lucene。ELK栈由Elasticsearch、Logstash(数据同步与过滤)和Kibana(数据可视化)组成,常用于日志管理和分析。文章介绍了Elasticsearch的基本概念如索引(index)、文档(document)和字段(field),以及如何使用API进行数据操作和查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ElasticSearch 是什么?

ElasticSearch是一个分布式、Restfui 风格的搜索和数据分析引擎,国内简称ES, ElasticSearch 是用Java开发的,底层基于Lucene,Lucene是一种全文的索引库,直接采用Lucene是比较麻烦的,Elasticsearch在Lucene的基础上开发了一个强大的搜索引擎。

  1. ELK是什么?

使用到ES, 那么ELK是绕不过去的一环, ELK代表了Elasticsearch + Logstash + Kibana 三套软件,他们的作用如下:

  • Elasticsearch - 前面简介提到过,解决海量数据搜索问题。
  • Logstash - 解决数据同步问题,因为我们数据一般存储在Mysql之类的数据库中,需要将数据导入到ES中,Logstash就支持数据同步、数据过滤、转换功能。
  • Kibana - Elasticsearch数据可视化支持,例如:通过各种图表展示ES的查询结果,也可以在Kibana通过ES查询语句分析数据,起到类似ES Web后台的作用。
  1. ES的应用场景
  • 各种搜索场景,例如:订单搜索、商品搜索。
  • 日志处理和分析,例如:通过ELK搭建日志处理和分析方案。
  • 地理空间数据搜索,例如:查询距离最近的店铺、查询某个空间范围内的店铺。

基本概念

  1. index(索引)

在Elasticsearch中索引(index)类似mysql的表,代表文档数据的集合,文档指的是ES中存储的一条数据。

  1. type(文档类型)

在新版的Elasticsearch中,已经不使用文档类型了,在Elasticsearch老的版本中文档类型,代表一类文档的集合,index(索引)类似mysql的数据库、文档类型类似Mysql的表。既然新的版本文档类型没什么作用了,那么index(索引)就类似mysql的表的概念,ES没有数据库的概念了。(针对老的涉及ES的项目,可以将索引看作是数据库,索引类型看作表)。

  1. Document(文档)

Elasticsearch是面向文档的数据库,文档是最基本的存储单元,文档类似mysql表中的一行数据,简单的说在ES中,文档指的就是一条JSON数据。

  1. Field(文档字段)

文档由多个json字段(Field)组成, 这里的字段类似mysql中表的字段。

ES中涉及到的常用的数据类型有:

  • 数值类型(包括: long、integer、short、byte、double、float)
  • text - 支持全文搜索
  • keyword - 不支持全文搜索,例如:email、电话这些数据,作为一个整体进行匹配就可以,不需要分词处理。
  • date - 日期类型
  • boolean
  1. mapping(映射)

Elasticsearch的mapping (映射)类似mysql中的表结构定义,每个索引都有一个映射规则,我们可以通过定义索引的映射规则,提前定义好文档的json结构和字段类型,如果没有定义索引的映射规则,Elasticsearch会在写入数据的时候,根据我们写入的数据字段推测出对应的字段类型,相当于自动定义索引的映射规则。

  1. ES 基本概念和MySQL比较
Elasticsearch存储结构MYSQL存储结构
index(索引)行,一行数据
文档单元格
Field(字段)表字段
mapping (映射))表结构定义

ES中api操作

  1. 文档元数据
  • _index - 代表当前JSON文档所属的文档名字
  • _type - 代表当前JSON文档所属的类型,虽然新版ES废弃了type的用法,但是元数据还是可以看到。
  • _id - 文档唯一Id, 如果我们没有为文档指定id,系统会自动生成
  • _source - 代表我们插入进去的JSON数据
  • _version - 文档的版本号,每修改一次文档数据,字段就会加1, 这个字段新版的ES已经不使用了
  • _seq_no - 文档的版本号, 替代老的_version字段
  • _primary_term - 文档所在主分区,这个可以跟_seq_no字段搭配实现乐观锁。
  1. index(索引)
  • 插入index
PUT /{indexName}

服务器返回一个 JSON 对象,里面的acknowledged字段表示操作成功。

{
  "acknowledged":true,
  "shards_acknowledged":true
}
  • 删除index
DELETE /{indexName}
  • 查询当前结点下的所有index
GET /_cat/indices?v
  1. document(文档)
  • 插入文档
PUT /{index}/{type}/{id}

{
  "field": "value",
  ...
}

{index} - 索引名
{type} - 文档类型名 - 新版的Elasticsearch为了兼容老版本,只允许指定一个类型,随便设置一个类型名就行。
{id} - 文档的唯一id, 可以不指定, 如果不指定id, 需要使用POST发送请求

  • 查询文档
GET /{index}/{type}/{id}
  • 更新文档
POST /{index}/_update/{id}

{
  "doc":{ // 在doc字段中指定需要更新的字段
    // 需要更新的字段列表
  }
}

如果我们只想更新json文档的某些字段,可以使用局部更新

  • 删除文档
DELETE /{index}/{type}/{id}

查询语法

  1. 查询的基本语法结构
GET /{索引名}/_search
{
	"from" : 0,  // 返回搜索结果的开始位置
  	"size" : 10, // 分页大小,一次返回多少数据
  	"_source" :[ ...需要返回的字段数组... ],
	"query" : { ...query子句... },
	"aggs" : { ..aggs子句..  },
	"sort" : { ..sort子句..  }
}

{索引名},支持支持一次搜索多个索引,多个索引使用逗号分隔,例子:GET /order1,order2/_search

或者按照前缀匹配索引名称:

GET /order*/_search

当我们执行查询语句时,返回的json数据格式如下:

{
  "took" : 5, // 查询消耗时间,单位毫秒 
  "timed_out" : false, // 查询是否超时
  "_shards" : { // 本次查询参与的ES分片信息,查询中参与分片的总数,以及这些分片成功了多少个失败了多少个
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : { // hits字段包含我们搜索匹配的结果
    "total" : { // 匹配到的文档总数
      "value" : 1, // 找到1个文档
      "relation" : "eq"
    },
    "max_score" : 1.0, // 匹配到的最大分值
    "hits" : [ 
         // 这里就是我们具体的搜索结果,是一个JSON文档数组
    ]
  }
}
  1. 查询语句
  • query子句查询

query子句主要用来编写类似SQL的Where语句,支持布尔查询(and/or)、IN、全文搜索、模糊匹配、范围查询(大于小于)。

  • aggs子句查询

aggs子句,主要用来编写统计分析语句,类似SQL的group by语句

  • sort子句查询

sort子句,用来设置排序条件,类似SQL的order by语句

  • 分页查询

ES查询的分页主要通过from和size参数设置,类似MYSQL 的limit和offset语句

例如:

GET /order_v2/_search
{
  "from": 0,
  "size": 20, 
  "query": {
    "match_all": {}
  }
}
  • _source

_source用于设置查询结果返回什么字段,类似Select语句后面指定字段。

GET /order_v2/_search
{
  "_source": ["order_no","shop_id"], 
  "query": {
    "match_all": {}
  }
}
Query查询语法
  1. 匹配单个字段

{FIELD} - 就是我们需要匹配的字段名
{TEXT} - 就是我们需要匹配的内容

GET /{索引名}/_search
{
  "query": {
    "match": {
      "{FIELD}": "{TEXT}"
    }
  }
}


GET /article/_search
{
    "query": {
        "match" : {
            "title" : "ES教程"
        }
    }
}
  1. 精准匹配单个字段

如果我们想要类似SQL语句中的等值匹配,不需要进行分词处理,例如:订单号、手机号、时间字段,不需要分值处理,只要精确匹配。

{FIELD} - 就是我们需要匹配的字段名
{VALUE} - 就是我们需要匹配的内容,除了TEXT类型字段以外的任意类型。

GET /{索引名}/_search
{
  "query": {
    "term": {
      "{FIELD}": "{VALUE}"
    }
  }
}


GET /order_v2/_search
{
  "query": {
    "term": {
      "order_no": "202003131209120999"
    }
  }
}

select * from order_v2 where order_no = “202003131209120999”

  1. SQL中的in语法

如果我们要实现SQL中的in语句,一个字段包含给定数组中的任意一个值就匹配。

{FIELD} - 就是我们需要匹配的字段名
{VALUE1}, {VALUE2} … {VALUE N} - 就是我们需要匹配的内容,除了TEXT类型字段以外的任意类型。

GET /order_v2/_search
{
  "query": {
    "terms": {
      "{FIELD}": [
        "{VALUE1}",
        "{VALUE2}"
      ]
    }
  }
}

GET /order_v2/_search
{
  "query": {
    "terms": {
      "shop_id": [123,100,300]
    }
  }
}

select * from order_v2 where shop_id in (123,100,300)

  1. 范围查询

通过range实现范围查询,类似SQL语句中的>, >=, <, <=表达式。

参数说明:
{FIELD} - 字段名
gte范围参数 - 等价于>=
lte范围参数 - 等价于 <=
范围参数可以只写一个,例如:仅保留 “gte”: 10, 则代表 FIELD字段 >= 10

参数范围:
gt - 大于 ( > )
gte - 大于且等于 ( >= )
lt - 小于 ( < )
lte - 小于且等于 ( <= )

GET /{索引名}/_search
{
  "query": {
    "range": {
      "{FIELD}": {
        "gte": 10, 
        "lte": 20
      }
    }
  }
}


GET /order_v2/_search
{
  "query": {
    "range": {
      "shop_id": {
        "gte": 10,
        "lte": 200
      }
    }
  }
}

select * from order_v2 where shop_id >= 10 and shop_id <= 200

  1. 组合查询(bool)

前面的例子都是设置单个字段的查询条件,如果需要编写类似SQL的Where语句,组合多个字段的查询条件,可以使用bool语句。在ES中bool查询就是用来组合布尔查询条件,布尔查询条件,就是类似SQL中的and (且)、or (或)。在SQL中,我们需要and和or,还有括号来组合查询条件,在ES中使用bool查询可用做到同样的效果。

GET /{索引名}/_search
{
  "query": {
    "bool": { // bool查询
      "must": [], // must条件,类似SQL中的and, 代表必须匹配条件
      "must_not": [], // must_not条件,跟must相反,必须不匹配条件
      "should": [] // should条件,类似SQL中or, 代表匹配其中一个条件
    }
  }
}
  1. SQL中的and查询(must)

类似SQL的and,代表必须匹配的条件。

GET /{索引名}/_search
{
  "query": {
    "bool": {
      "must": [
         {匹配条件1},
         {匹配条件2},
         ...可以有N个匹配条件...
        ]
    }
  }
}


GET /order_v2/_search
{
  "query": {
    "bool": {
      "must": [
          {
            "term": {
              "order_no":  "202003131209120999"
            }
          },
          {
            "term": {
              "shop_id":  123
            }
          }
        ]
    }
  }
}

select * from order_v2 where order_no=“202003131209120999” and shop_id=123

  1. SQL中的不等于条件(must_not)

跟must的作用相反。

GET /{索引名}/_search
{
  "query": {
    "bool": {
      "must_not": [
         {匹配条件1},
         {匹配条件2},
         ...可以有N个匹配条件...
        ]
    }
  }
}

GET /order_v2/_search
{
  "query": {
    "bool": {
      "must_not": [
          {
            "term": {
              "shop_id": 1
            }
          },
          {
            "term": {
              "shop_id":  2
            }
          }
        ]
    }
  }
}

select * from order_v2 where shop_id != 1 and shop_id != 2

  1. SQL中的or条件(should)

类似SQL中的 or, 只要匹配其中一个条件即可

GET /{索引名}/_search
 {
   "query": {
     "bool": {
       "should": [
          {匹配条件1},
          {匹配条件2},
          …可以有N个匹配条件…
         ]
     }
   }
}

GET /order_v2/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "order_no": "202003131209120999"
          }
        },
        {
          "match": {
            "order_no": "22222222222222222"
          }
        }
      ]
    }
  }
}

select * from order_v2 where order_no=“202003131209120999” or order_no=“22222222222222222”

  1. bool综合条件
GET /order_v2/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "bool": {
            "must": [
              {
                "term": {
                  "order_no": "2020031312091209991"
                }
              },
              {
                "range": {
                  "shop_id": {
                    "gte": 10,
                    "lte": 200
                  }
                }
              }
            ]
          }
        },
        {
          "terms": {
            "tag": [
              1,
              2,
              3,
              4,
              5,
              12
            ]
          }
        }

    ]
    }
  }
}        

select * from order_v2 where (order_no=‘202003131209120999’ and (shop_id>=10 and shop_id<=200)) or tag in (1,2,3,4,5)

  1. MySQL中的order by(排序子句)
GET /{索引名}/_search
{
  "query": {
    ...查询条件....
  },
  "sort": [
    {
      "{Field1}": { // 排序字段1
        "order": "desc" // 排序方向,asc或者desc, 升序和降序
      }
    },
    {
      "{Field2}": { // 排序字段2
        "order": "desc" // 排序方向,asc或者desc, 升序和降序
      }
    }
    ....多个排序字段.....
  ]
}



GET /order_v2/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "order_no": {
        "order": "desc"
      }
    },
    {
      "shop_id": {
        "order": "asc"
      }
    }
  ]
}

select * from order_v2 order by order_no desc, shop_id asc

  1. MySQL中的(sum/count/avg/group by) 聚合统计
{
  "aggregations" : {
    "<aggregation_name>" : {
        "<aggregation_type>" : {
            <aggregation_body>
        }
        [,"aggregations" : { [<sub_aggregation>]+ } ]? // 嵌套聚合查询,支持多层嵌套
    }
    [,"<aggregation_name_2>" : { ... } ]* // 多个聚合查询,每个聚合查询取不同的名字
  }
}
  • aggregations - 代表聚合查询语句,可以简写为aggs
  • <aggregation_name> - 代表一个聚合计算的名字,可以随意命名,因为ES支持一次进行多次统计分析查询,后面需要通过这个名字在查询结果中找到我们想要的计算结果。
  • <aggregation_type> - 聚合类型,代表我们想要怎么统计数据,主要有两大类聚合类型,桶聚合和指标聚合,这两类聚合又包括多种聚合类型,例如:指标聚合:sum、avg, 桶聚合:terms、Date histogram等等。
  • <aggregation_body> - 聚合类型的参数,选择不同的聚合类型,有不同的参数。
  • aggregation_name_2 - 代表其他聚合计算的名字,意思就是可以一次进行多种类型的统计。

案例:

GET /order/_search
{
    "size" : 0, // 设置size=0的意思就是,仅返回聚合查询结果,不返回普通query查询结果。
    "aggs" : { // 聚合查询语句的简写
        "popular_colors" : { // 给聚合查询取个名字,叫popular_colors
            "terms" : { // 聚合类型为,terms,terms是桶聚合的一种,类似SQL的group by的作用,根据字段分组,相同字段值的文档分为一组。
              "field" : "color" // terms聚合类型的参数,这里需要设置分组的字段为color,根据color分组
            }
        }
    }
}

select count(color) from order group by color

  1. MySQL中聚合函数(指标聚合)

ES指标聚合,就是类似SQL的统计函数,指标聚合可以单独使用,也可以跟桶聚合一起使用。

  • Value Count - 类似sql的count函数,统计总数

  • Cardinality - 类似SQL的count(DISTINCT 字段), 统计不重复的数据总数

  • Avg - 求平均值

  • Sum - 求和

  • Max - 求最大值

  • Min - 求最小值

  • MySQL中的count

GET /sales/_search?size=0
{
  "aggs": {
    "types_count": { // 聚合查询的名字,随便取个名字
      "value_count": { // 聚合类型为:value_count
        "field": "type" // 计算type这个字段值的总数
      }
    }
  }
}


{
    ...
    "aggregations": {
        "types_count": { // 聚合查询的名字
            "value": 7 // 统计结果
        }
    }
}

值聚合,主要用于统计文档总数,类似SQL的count函数。

select count(type) from sales

  • MySQL中的distinct(Cardinality)

基数聚合,也是用于统计文档的总数,跟Value Count的区别是,基数聚合会去重,不会统计重复的值,类似SQL的count(DISTINCT 字段)用法。

前面提到基数聚合的作用等价于SQL的count(DISTINCT 字段)的用法,其实不太准确,因为SQL的count统计结果是精确统计不会丢失精度,但是ES的cardinality基数聚合统计的总数是一个近似值,会有一定的误差,这么做的目的是为了性能,因为在海量的数据中精确统计总数是非常消耗性能的,但是很多业务场景不需要精确的结果,只要近似值,例如:统计网站一天的访问量,有点误差没关系。

POST /sales/_search?size=0
{
    "aggs" : {
        "type_count" : { // 聚合查询的名字,随便取一个
            "cardinality" : { // 聚合查询类型为:cardinality
                "field" : "type" // 根据type这个字段统计文档总数
            }
        }
    }
}


{
    ...
    "aggregations" : {
        "type_count" : { // 聚合查询的名字
            "value" : 3 // 统计结果
        }
    }
}

select count(DISTINCT type) from sales

  • Avg
POST /exams/_search?size=0
{
  "aggs": {
    "avg_grade": { // 聚合查询名字,随便取一个名字
      "avg": { // 聚合查询类型为: avg
        "field": "grade" // 统计grade字段值的平均值
      }
    }
  }
}


{
    ...
    "aggregations": {
        "avg_grade": { // 聚合查询名字
            "value": 75.0 // 统计结果
        }
    }
}
  • Sum
POST /sales/_search?size=0
{
  "aggs": {
    "hat_prices": { // 聚合查询名字,随便取一个名字
      "sum": { // 聚合类型为:sum
        "field": "price" // 计算price字段值的总和
      }
    }
  }
}



{
    ...
    "aggregations": {
        "hat_prices": { // 聚合查询名字
           "value": 450.0 // 统计结果
        }
    }
}
  • Max
POST /sales/_search?size=0
{
  "aggs": {
    "max_price": { // 聚合查询名字,随便取一个名字
      "max": { // 聚合类型为:max
        "field": "price" // 求price字段的最大值
      }
    }
  }
}


{
    ...
    "aggregations": {
        "max_price": { // 聚合查询名字
            "value": 200.0 // 最大值
        }
    }
}
  • Min
POST /sales/_search?size=0
{
  "aggs": {
    "min_price": { // 聚合查询名字,随便取一个
      "min": { // 聚合类型为: min
        "field": "price" // 求price字段值的最小值
      }
    }
  }
}


{
    ...

    "aggregations": {
        "min_price": { // 聚合查询名字
            "value": 10.0 // 最小值
        }
    }
}
  • group by

Elasticsearch桶聚合,目的就是数据分组,先将数据按指定的条件分成多个组,然后对每一个组进行统计。 组的概念跟桶是等同的,在ES中统一使用桶(bucket)这个术语。ES桶聚合的作用跟SQL的group by的作用是一样的,区别是ES支持更加强大的数据分组能力,SQL只能根据字段的唯一值进行分组,分组的数量跟字段的唯一值的数量相等,例如: group by 店铺id, 去掉重复的店铺ID后,有多少个店铺就有多少个分组。

桶聚合如下:

  1. Terms聚合 - 类似SQL的group by,根据字段唯一值分组
  2. Histogram聚合 - 根据数值间隔分组,例如: 价格按100间隔分组,0、100、200、300等等
  3. Date histogram聚合 - 根据时间间隔分组,例如:按月、按天、按小时分组
  4. Range聚合 - 按数值范围分组,例如: 0-150一组,150-200一组,200-500一组。

terms聚合的作用跟SQL中group by作用一样,都是根据字段唯一值对数据进行分组(分桶),字段值相等的文档都分到同一个桶内。

GET /order/_search?size=0
{
  "aggs": {
    "shop": { // 聚合查询的名字,随便取个名字
      "terms": { // 聚合类型为: terms
        "field": "shop_id" // 根据shop_id字段值,分桶
      }
    }
  }
}

{
    ...
    "aggregations" : {
        "shop" : { // 聚合查询名字
            "buckets" : [ // 桶聚合结果,下面返回各个桶的聚合结果
                {
                    "key" : "1", // key分桶的标识,在terms聚合中,代表的就是分桶的字段值
                    "doc_count" : 6 // 默认的指标聚合是统计桶内文档总数
                },
                {
                    "key" : "5",
                    "doc_count" : 3
                },
                {
                    "key" : "9",
                    "doc_count" : 2
                }
            ]
        }
    }
}

select shop_id, count(*) from order group by shop_id

参考资料

  1. https://www.ruanyifeng.com/blog/2017/08/elasticsearch.html

  2. https://www.tizi365.com/archives/628.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值