Elasticsearch概述

Elasticsearch是什么

The Elastic Stack, 包括 Elasticsearch、Kibana、Beats 和 Logstash(也称为 ELK Stack)。 能够安全可靠地获取任何来源、任何格式的数据,然后实时地对数据进行搜索、分析和可视化。Elaticsearch,简称为 ES,ES 是一个开源的高扩展的分布式全文搜索引擎,是整个 Elastic Stack 技术栈的核心。它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理 PB 级别的数据

全文搜索引擎

Google,百度类的网站搜索,它们都是根据网页中的关键字生成索引,我们在搜索的时候输入关键字,它们会将该关键字即索引匹配到的所有网页返回;还有常见的项目中应用日志的搜索等等。对于这些非结构化的数据文本,关系型数据库搜索不是能很好的支持。
一般传统数据库,全文检索都实现的很鸡肋,因为一般也没人用数据库存文本字段。进行全文检索需要扫描整个表,如果数据量大的话即使对 SQL 的语法优化,也收效甚微。建立了索引,但是维护起来也很麻烦,对于 insert 和 update 操作都会重新构建索引。
基于以上原因可以分析得出,在一些生产环境中,使用常规的搜索方式,性能是非常差的:

  • 搜索的数据对象是大量的非结构化的文本数据
  • 文件记录量达到数十万或数百万个甚至更多
  • 支持大量基于交互式文本的查询。
  • 需求非常灵活的全文搜索查询。
  • 对高度相关的搜索结果的有特殊需求,但是没有可用的关系数据库可以满足
  • 对不同记录类型、非文本数据操作或安全事务处理的需求相对较少的情况。 为了解决结构化数据搜索和非结构化数据搜索性能问题,我们就需要专业,健壮,强大的全文搜索引擎

Elasticsearch And Solr

Lucene 是 Apache 软件基金会 Jakarta 项目组的一个子项目,提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻。在 Java 开发环境里 Lucene 是一个成熟的免费开源 工具。就其本身而言,Lucene 是当前以及最近几年最受欢迎的免费 Java 信息检索程序库。 但 Lucene 只是一个提供全文搜索功能类库的核心工具包,而真正使用它还需要一个完善的 服务框架搭建起来进行应用。
目前市面上流行的搜索引擎软件,主流的就两款:Elasticsearch 和 Solr,这两款都是基 于 Lucene 搭建的,可以独立部署启动的搜索引擎服务软件。由于内核相同,所以两者除了 服务器安装、部署、管理、集群以外,对于数据的操作 修改、添加、保存、查询等等都十 分类似。
在使用过程中,一般都会将 Elasticsearch 和 Solr 这两个软件对比,然后进行选型。这两 个搜索引擎都是流行的,先进的的开源搜索引擎。它们都是围绕核心底层搜索库 Lucene 构建的,但它们又是不同的。像所有东西一样,每个都有其优点和缺点:

无论选择 Solr 还是 Elasticsearch,首先需要了解正确的用例和未来需求。总结一下Elasticsearch的特点:

  • 由于易于使用,Elasticsearch 在新开发者中更受欢迎。一个下载和一个命令就可以启动一切
  • 如果除了搜索文本之外还需要它来处理分析查询,Elasticsearch 是更好的选择
  • 如果需要分布式索引,则需要选择 Elasticsearch。对于需要良好可伸缩性和以及性能分布式环境,Elasticsearch 是更好的选择
  • Elasticsearch 在开源日志管理用例中占据主导地位,许多组织在 Elasticsearch 中索引它们的日志以使其可搜索
  • 如果你喜欢监控和指标,那么请使用 Elasticsearch,因为相对于 Solr,Elasticsearch 暴露了更多的关键指标

Elasticsearch 安装

Elasticsearch 的官方地址:https://www.elastic.co/cn/

目录结构如下:

目录 含义
bin 可执行脚本目录
config 配置目录
jdk 内置 JDK 目录
lib 类库
logs 日志目录
modules 模块目录
plugins 插件目录

进入bin目录,启动elasticsearch即可启动服务

[2021-04-13T13:19:55,134][INFO ][o.e.n.Node               ] [B-LW01J1WL-0028.local] initialized
[2021-04-13T13:19:55,134][INFO ][o.e.n.Node               ] [B-LW01J1WL-0028.local] starting ...
[2021-04-13T13:19:55,193][INFO ][o.e.x.s.c.PersistentCache] [B-LW01J1WL-0028.local] persistent cache index loaded
[2021-04-13T13:20:00,475][INFO ][o.e.t.TransportService   ] [B-LW01J1WL-0028.local] publish_address {127.0.0.1:9300}, bound_addresses {[::1]:9300}, {127.0.0.1:9300}
[2021-04-13T13:20:00,821][WARN ][o.e.b.BootstrapChecks    ] [B-LW01J1WL-0028.local] the default discovery settings are unsuitable for production use; at least one of [discovery.seed_hosts, discovery.seed_providers, cluster.initial_master_nodes] must be configured
[2021-04-13T13:20:00,835][INFO ][o.e.c.c.ClusterBootstrapService] [B-LW01J1WL-0028.local] no discovery configuration found, will perform best-effort cluster bootstrapping after [3s] unless existing master is discovered
[2021-04-13T13:20:03,845][INFO ][o.e.c.c.Coordinator      ] [B-LW01J1WL-0028.local] setting initial configuration to VotingConfiguration{havNlIPpSIKsyb4mq42F3Q}
[2021-04-13T13:20:04,170][INFO ][o.e.c.s.MasterService    ] [B-LW01J1WL-0028.local] elected-as-master ([1] nodes joined)[{B-LW01J1WL-0028.local}{havNlIPpSIKsyb4mq42F3Q}{GWO-_Ju0TgKDaXcELngWqw}{127.0.0.1}{127.0.0.1:9300}{cdfhilmrstw}{ml.machine_memory=8589934592, xpack.installed=true, transform.node=true, ml.max_open_jobs=20, ml.max_jvm_size=4294967296} elect leader, _BECOME_MASTER_TASK_, _FINISH_ELECTION_], term: 1, version: 1, delta: master node changed {previous [], current [{B-LW01J1WL-0028.local}{havNlIPpSIKsyb4mq42F3Q}{GWO-_Ju0TgKDaXcELngWqw}{127.0.0.1}{127.0.0.1:9300}{cdfhilmrstw}{ml.machine_memory=8589934592, xpack.installed=true, transform.node=true, ml.max_open_jobs=20, ml.max_jvm_size=4294967296}]}
[2021-04-13T13:20:04,256][INFO ][o.e.c.c.CoordinationState] [B-LW01J1WL-0028.local] cluster UUID set to [8s0OS-nhQHazoUw6adHJEw]
[2021-04-13T13:20:04,324][INFO ][o.e.c.s.ClusterApplierService] [B-LW01J1WL-0028.local] master node changed {previous [], current [{B-LW01J1WL-0028.local}{havNlIPpSIKsyb4mq42F3Q}{GWO-_Ju0TgKDaXcELngWqw}{127.0.0.1}{127.0.0.1:9300}{cdfhilmrstw}{ml.machine_memory=8589934592, xpack.installed=true, transform.node=true, ml.max_open_jobs=20, ml.max_jvm_size=4294967296}]}, term: 1, version: 1, reason: Publication{term=1, version=1}
[2021-04-13T13:20:04,431][INFO ][o.e.h.AbstractHttpServerTransport] [B-LW01J1WL-0028.local] publish_address {127.0.0.1:9200}, bound_addresses {[::1]:9200}, {127.0.0.1:9200}
[2021-04-13T13:20:04,434][INFO ][o.e.n.Node               ] [B-LW01J1WL-0028.local] started

9300端口为 Elasticsearch 集群间组件的通信端口,9200 端口为浏览器访问的 http 协议 RESTful 端口

// 访问服务
huanliu@B-LW01J1WL-0028 ~ % curl http://127.0.0.1:9200
{
  "name" : "B-LW01J1WL-0028.local",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "8s0OS-nhQHazoUw6adHJEw",
  "version" : {
    "number" : "7.12.0",
    "build_flavor" : "default",
    "build_type" : "tar",
    "build_hash" : "78722783c38caa25a70982b5b042074cde5d3b3a",
    "build_date" : "2021-03-18T06:17:15.410153305Z",
    "build_snapshot" : false,
    "lucene_version" : "8.8.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

数据格式


Elasticsearch 是面向文档型数据库,一条数据在这里就是一个文档。为了方便理解,我们将 Elasticsearch 里存储文档数据和关系型数据库 MySQL 存储数据的概念进行一个类比:ES 里的 Index 可以看做一个库,而 Types 相当于表,Documents 则相当于表的行。这里 Types 的概念已经被逐渐弱化,Elasticsearch 6.X 中,一个 index 下已经只能包含一个type,Elasticsearch 7.X 中, Type的概念已经被删除了

Elasticsearch 基本操作

索引操作

// 创建索引,对比关系型数据库,创建索引就等同于创建数据库
PUT请求:http://127.0.0.1:9200/shopping
{
    "acknowledged"【响应结果】: true, # true操作成功 
    "shards_acknowledged"【分片结果】: true, # 分片操作成功 
    "index"【索引名称】: "shopping"
}

// 如果重复添加索引,会返回错误信息
{
    "error": {
        "root_cause": [
            {
                "type": "resource_already_exists_exception",
                "reason": "index [shopping/5upLIUrhTGWX6lWTG6BQBg] already exists",
                "index_uuid": "5upLIUrhTGWX6lWTG6BQBg",
                "index": "shopping"
            }
        ],
        "type": "resource_already_exists_exception",
        // 索引已存在
        "reason": "index [shopping/5upLIUrhTGWX6lWTG6BQBg] already exists",
        "index_uuid": "5upLIUrhTGWX6lWTG6BQBg",
        "index": "shopping"
    },
    "status": 400
}

// 查看所有索引
GET请求:http://127.0.0.1:9200/_cat/indices?v
/**
 * 这里请求路径中的_cat 表示查看的意思,indices 表示索引,所以整体含义就是查看当前 ES 服务器中的所有索引,就好像 MySQL 中的 show tables 的感觉,服务器响应结果如下
 * health:当前服务器健康状态: green(集群完整) yellow(单点正常、集群不完整) red(单点不正常)
 * status:索引打开、关闭状态
 * index:索引名
 * uuid:索引统一编号
 * pri:主分片数量
 * rep:副本数量
 * docs.count:可用文档数量
 * docs.deleted:文档删除状态(逻辑删除)
 * store.size:主分片和副分片整体占空间大小
 * pri.store.size:主分片占空间大小
 */
health status index    uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   shopping 5upLIUrhTGWX6lWTG6BQBg   1   1          0            0       208b           208b

// 查看单个索引
GET请求:http://127.0.0.1:9200/shopping
{
    // 索引名
    "shopping": {
        // 别名
        "aliases": {},
        // 映射
        "mappings": {},
       // 设置
        "settings": {
            // 索引
            "index": {
                "routing": {
                    "allocation": {
                        "include": {
                            "_tier_preference": "data_content"
                        }
                    }
                },
                // 主分片数据量
                "number_of_shards": "1",
                // 索引名称
                "provided_name": "shopping",
                // 创建时间
                "creation_date": "1618294231972",
                // 副分片数量
                "number_of_replicas": "1",
                // 唯一标识
                "uuid": "5upLIUrhTGWX6lWTG6BQBg",
                // 版本
                "version": {
                    "created": "7120099"
                }
            }
        }
    }
}

// 删除索引
DELETE请求:http://127.0.0.1:9200/shopping
{
    "acknowledged": true
}

// 重复请求会报错
{
    "error": {
        "root_cause": [
            {
                "type": "index_not_found_exception",
                "reason": "no such index [shopping]",
                "resource.type": "index_or_alias",
                "resource.id": "shopping",
                "index_uuid": "_na_",
                "index": "shopping"
            }
        ],
        "type": "index_not_found_exception",
        "reason": "no such index [shopping]",
        "resource.type": "index_or_alias",
        "resource.id": "shopping",
        "index_uuid": "_na_",
        "index": "shopping"
    },
    "status": 404
}

文档操作

// 创建文档,索引已经创建好了,接下来我们来创建文档,并添加数据。这里的文档可以类比为关系型数 据库中的表数据,添加的数据格式为 JSON 格式
POST 请求 :http://127.0.0.1:9200/shopping/_doc
请求体:
// 这里数据创建后,由于没有指定数据唯一性标识(ID),默认情况下,ES 服务器会随机 生成一个。 如果想要自定义唯一性标识,需要在创建时指定:http://127.0.0.1:9200/shopping/_doc/1
{
    "title":"小米手机",
    "category":"小米",
    "image":"https://www.xiaomi.com/1.jpg",
    "price":1999
}

{
    // 索引
    "_index": "shopping",
    // 类型-文档
    "_type": "_doc",
    // 唯一标识,可以类比为MySQL中的主键,随机生成
    "_id": "6GbxyXgBsiazCaso13gh",
    // 版本
    "_version": 1,
    // 结果,这里的create表示创建成功
    "result": "created",
    // 分片
    "_shards": {
        // 总数
        "total": 2,
        // 成功
        "successful": 1,
        // 失败
        "failed": 0
    },
    "_seq_no": 0,
    "_primary_term": 1
}

// 查看文档
GET请求:http://127.0.0.1:9200/shopping/_doc/6GbxyXgBsiazCaso13gh
{
    // 索引
    "_index": "shopping",
    // 文档类型
    "_type": "_doc",
    "_id": "6GbxyXgBsiazCaso13gh",
    "_version": 1,
    "_seq_no": 0,
    "_primary_term": 1,
    // 查询结果:true表示查找到,false表示未查找到
    "found": true,
    // 文档源信息
    "_source": {
        "title": "小米手机",
        "category": "小米",
        "image": "https://www.xiaomi.com/1.jpg",
        "price": 1999
    }
}

// 修改文档
和新增文档一样,输入相同的 URL 地址请求,如果请求体变化,会将原有的数据内容覆盖
POST 请求 :http://127.0.0.1:9200/shopping/_doc/6GbxyXgBsiazCaso13gh
请求体内容为
{
    "title": "华为手机",
    "category": "华为",
    "image": "https://www.huawei.com/1.jpg",
    "price": 2999
}

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "6GbxyXgBsiazCaso13gh",
    // 版本
    "_version": 2,
    // 结果,updated表示数据被更新
    "result": "updated",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 1,
    "_primary_term": 1
}

// 修改字段,修改数据时,也可以只修改某一给条数据的局部信息
POST 请求 :http://127.0.0.1:9200/shopping/_update/6GbxyXgBsiazCaso13gh
{
    "doc": {
        "price":3000.00 
    }
}

{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "6GbxyXgBsiazCaso13gh",
    "_version": 3,
    "result": "updated",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 2,
    "_primary_term": 1
}

// 删除文档,删除一个文档不会立即从磁盘上移除,它只是被标记成已删除(逻辑删除)。
DELETE 请求 :http://127.0.0.1:9200/shopping/_doc/6GbxyXgBsiazCaso13gh
{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1",
    //#对数据的操作,都会更新版本
    "_version" : 4,
    // deleted表示数据被标记为删除 
    "result"【结果】: "deleted",
    "_shards": {
           "total": 2,
           "successful": 1,
           "failed": 0
       },
       "_seq_no": 4,
       "_primary_term": 2
}

// 条件删除文档:一般删除数据都是根据文档的唯一性标识进行删除,实际操作时,也可以根据条件对多条数据进行删除
POST 请求 :http://127.0.0.1:9200/shopping/_delete_by_query
请求体内容为:
{
    "query":{
        "match":{ 
            "price":4000.00
        } 
    }
}

{
    // 耗时
    "took": 175,
    // 是否超时 
    "timed_out": false, 
    // 总数
    "total": 2,
    // 删除数量 
    "deleted": 2,
    "batches": 1,
    "version_conflicts": 0,
    "noops": 0,
    "retries": {
           "bulk": 0,
           "search": 0
     },
    "throttled_millis": 0, 
    "requests_per_second": -1.0, 
    "throttled_until_millis": 0, 
    "failures": []
}

映射操作

有了索引库,等于有了数据库中的 database。接下来就需要建索引库(index)中的映射了,类似于数据库(database)中的表结构(table)。 创建数据库表需要设置字段名称,类型,长度,约束等;索引库也一样,需要知道这个类型下有哪些字段,每个字段有哪些约束信息,这就叫做映射(mapping)。

// 创建映射
PUT 请求 :http://127.0.0.1:9200/student/_mapping
//
// 映射说明:
// 字段名:任意填写,下面指定许多属性,例如:title、subtitle、images、price
// type:类型,Elasticsearch中支持的数据类型非常丰富,说几个关键的:
//           String 类型,又分两种: 
//                   text:可分词
//                   keyword:不可分词,数据会作为完整字段进行匹配
//           Numerical:数值类型,分两类
//                   基本数据类型:long、integer、short、byte、double、float、half_float
//                   浮点数的高精度类型:scaled_float
//           Date:日期类型
//           Array:数组类型
//           Object:对象
// index:是否索引,默认为true,也就是说你不进行任何配置,所有字段都会被索引。
//           true:字段会被索引,则可以用来进行搜索
//           false:字段不会被索引,不能用来搜索
// store:是否将数据进行独立存储,默认为 false。原始的文本会存储在_source 里面,默认情况下其他提取出来的字段都不是独立存储 的,是从_source 里面提取出来的。当然你也可以独立的存储某个字段,只要设置 "store": true 即可,获取独立存储的字段要比从_source 中解析快得多,但是也会占用 更多的空间,所以要根据实际业务需求来设置。
// analyzer:分词器,这里的 ik_max_word 即使用 ik 分词器
{
    "properties": {
         "name":{
             "type": "text",
             "index": true
         }, 
         "sex":{
             "type": "text",
             "index": false
          },
          "age":{
             "type": "long",
             "index": false
          } 
    }
}

// 查看映射
GET 请求 :http://127.0.0.1:9200/student/_mapping
{
    "student": {
        "mappings": {
            "properties": {
                "age": {
                    "type": "long",
                    "index": false
                },
                "name": {
                    "type": "text"
                },
                "sex": {
                    "type": "text",
                    "index": false
                }
            }
        }
    }
}

// 索引映射关联
 PUT 请求 :http://127.0.0.1:9200/student1
请求体:
{
	"settings": {},
	"mappings": {
		"properties": {
			"name": {
				"type": "text",
				"index": true
			},
			"sex": {
				"type": "text",
				"index": false
			},
			"age": {
				"type": "long",
				"index": false
			}
		}
	}
}
posted @ 2021-04-13 15:35  叮叮叮叮叮叮当  阅读(362)  评论(0编辑  收藏  举报