Elasticsearch学习(一):基础概念

1、什么是Elasticsearch?

1.1、介绍

        Elasticsearch(简称ES)是一个基于Apache Lucene构建的开源、分布式、RESTful接口的全文搜索引擎,Elasticsearch通过对Lunece的封装,隐藏了复杂性,提供了使用简单的RESTful Api。

        Elasticsearch还是一个分布式文档数据库,其中每个字段均可被索引,而且每个字段的数据均可被搜索,因为对文档进行了分词处理。ES能够横向扩展至数以百计的服务器存储以及处理PB级的数据,可以在极短的时间内存储、搜索和分析大量的数据。

        Elasticsearch 是用 Java 开发的,并作为 Apache 许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

 注:PB是数据存储容量的单位,它等于2的50次方个字节,或者在数值上大约等于1000个TB。

1.2、为什么要使用它

在业务开发中,基于ES的特性,通常有下面这些场景需要使用它:

  • 存储大量数据。通过在使用mysql存储的时候,数据的单位是G。使用ES的时候,数据的单位是T。由此可以看出ES使用于大数据量的存储场景,基于分布式特性,它也支持备份和容灾,并且可以很容易水平扩展容量。
  • 分词搜索引擎。ES具有强大的分词能力,可以支持高性能的实时搜索。
  • 高效数据分析。ES提供的聚合分析功能,可实现对保存的大量数据的近实时统计分析。

1.3、特性

  • 分布式的实时文件存储,每个字段都被索引并可被搜索;
  • 分布式的实时分析搜索引擎;
  • 可以扩展到成百上千台服务器,处理PB级结构化或非结构化数据。

1.4、使用场景

  • 搜索领域: 如百度、谷歌,全文检索等。
  • 门户网站: 访问统计、文章点赞、留言评论等。
  • 广告推广: 记录员工行为数据、消费趋势、员工群体进行定制推广等。
  • 信息采集: 记录应用的埋点数据、访问日志数据等,方便大数据进行分析。

1.5、ElasticSearch与Solr的比较

  • ES和Solr都是是基于Lucene的,它们都是成熟的产品,拥有强大而广泛的用户社区;
  • Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能;
  • Solr 支持更多格式的数据,而 Elasticsearch 仅支持json文件格式;
  • Solr 官方提供的功能更多,而 Elasticsearch 本身更注重于核心功能,高级功能多由第三方插件提供;
  • Solr 在传统的搜索应用中表现好于 Elasticsearch,但在处理实时搜索应用时效率明显低于 Elasticsearch。

2、elasticsearch相关概念

es底层无更新数据操作,上层封装的更新实际是删除后再新增。

ES 和传统的关系型数据库有这么一种关系:

RDMS Elasticsearch
数据库(database) 索引(index)
表(table) 类型(type)
行(row) 文档(document)
列(column) 字段(field)
表结构 映射
索引 全文索引
SQL 查询DSL
SELECT * FROM tablename GET http://...
UPDATE table SET PUT http://...
DELETE DELETE http://...

        Elasticsearch是面向文档型数据库,一条数据在这里就是一个文档,用JSON作为文档序列化的格式,比如下面这条用户数据:

{
  "name": "John",
  "sex": "Male",
  "age": 25,
  "birthDate": "1990/05/01",
  "about": "I love to go rock climbing",
  "interests": ["sports","music"]
}

 2.1、Index 索引

        Elasticsearch 数据管理的顶层单位就叫做 Index(索引),相当于关系型数据库里的database的概念。另外,每个Index的名字必须是小写。一个索引是一个文档的集合(等同于solr中的集合)。每个索引有唯一的名字,通过这个名字来操作它。一个集群中可以有任意多个索引。

        Elasticsearch的索引原理是通过倒排索引来实现的。

Elasticsearch学习(一):基础概念_第1张图片

2.2、type 类型

        这里指的是文档的类型,而不是字段的类型。type类似于MySQL数据库中 表 的概念。而ES中没有表的概念,这是ES和数据库的一个区别。在6.0版本之前,ES中有Type的概念,但是后来官方说这是一个设计上的失误。所以这个 type 从 7.x 开始就被移除了!系统默认使用_doc(现在8.x 版本就也不再支持修改这个类型了), 因为这个设计会降低 Lucene 压缩数据的能力,导致数据稀疏。

        我们都知道elasticsearch是基于Lucene开发的搜索引擎,而ES中不同type下名称相同的filed最终在Lucene中的处理方式是一样的。举个例子,两个不同type下的两个user_name,在ES同一个索引下其实被认为是同一个filed,你必须在两个不同的type中定义相同的filed映射。否则,不同type中的相同字段名称就会在处理中出现冲突的情况,导致Lucene处理效率下降。从本质上来看,这个 Type是对索引进行逻辑分区,使用文档类型_type 和文档_id 组成_uid ,形成文档的唯一ID,对索引进行细分。而在 Lucene 中,我们这个字段域在索引中是唯一的,所以原本的字段也会被细分,导致字段域增多的同时,数据的密度也就降低了,压缩效率也就降低了,导致ES查询效率的降低。

        在 5.X 版本中,一个 index 下可以创建多个 type;

        在 6.X 版本中,一个 index 下只能存在一个 type(即:"_type" : "doc");

        在 7.X 版本中,直接去除了 type 的概念,就是说 index 不再会有 type。

        8.0开始,将移除接受类型的API

        ES 的Type 被废弃后,库表合一,Index 既可以被认为对应 MySQL 的 Database,也可以认为对应 table。

也可以这样理解:

  • ES 实例:对应 MySQL 实例中的一个 Database。
  • Index 对应 MySQL 中的 Table 。
  • Document 对应 MySQL 中表的记录。

2.3、Document 文档

        文档在ES中相当于传统数据库中的行的概念,即每一行的数据,ES中的数据都以JSON的形式来表示,在MySQL中插入一行数据和ES中插入一个JSON文档是一个意思。文档是Elasticsearch中的最小单位,每个索引都是有数量众多的文档组成的。文档由多个字段组成,每个字段的类型由mapping定义,每个字段的类型,可以是文本、数值、日期等。字段类型也可以是复杂类型,一个字段包含其他子文档或者数组,但 Elasticsearch 的文档中,相同字段必须有相同类型。下面的JSON数据表示一个包含7个字段的文档。

{
  "_index": "user",
  "_type": "_doc",
  "_id": "qbuOs4AB1VH6WaY_OsFW",
  "_version": 1,
  "_score": 1,
  "_source": {
    "name": "张三",
    "address": "广东省深圳市",
    "remark": "他是一个程序员",
    "age": 28,
    "salary": 8800,
    "birthDate": "1991-10-05",
    "createTime": "2019-07-22T13:22:00.000Z"
  }
}

 上面为 ES 一条文档数据,而一个文档不只有基础数据,它还包含了元数据(metadata)——关于文档的信息,也就是用下划线开头的字段,它是官方提供的字段:

  • _index :文档所属索引名称,即文档存储的地方。
  • _type :文档所属类型名(此处已默认为_doc)。
  • _id :文档的唯一标识。在写入的时候,可以指定该 Doc 的 ID 值,如果不指定,则系统自动生成一个唯一的 UUID 值。
  • _score :顾名思义,得分,也可称之为相关性,在查询是 ES 会 根据一些规则计算得分,并根据得分进行倒排。除此之外,ES 支持通过 Function score query 在查询时自定义 score 的计算规则。
  • _source :文档的原始 JSON 数据。

2.4、字段Field

        相当于是数据表的字段,字段在ES中可以理解为JSON数据的键,是文档中的基本单位,以键值对的形式存在。在下面的JSON数据中,键都是一个字段。

{
  "name": "张三",
  "address": "广东省深圳市",
  "remark": "他是一个程序员",
  "age": 28,
  "salary": 8800,
  "birthDate": "1991-10-05",
  "createTime": "2019-07-22T13:22:00.000Z"
}

2.5、映射mapping

        相当于数据库中的schema,用来约束字段的数据类型,每一种数据类型都有对应的使用场景。mapping 中定义了一个文档所包含的所有 field 信息,每个文档都有映射,但是在大多数使用场景中,我们并不需要显示的创建映射,因为ES中实现了动态映射。我们在索引中写入一个下面的JSON文档:

{
    "name":"jack",
    "age":18,
    "birthDate": "1991-10-05"
}

        在动态映射的作用下,name会映射成text类型,age会映射成long类型,birthDate会被映射为date类型,映射的索引信息如下。

{
  "mappings": {
    "_doc": {
      "properties": {
        "age": {
          "type": "long"
        },
        "birthDate": {
          "type": "date"
        },
        "name": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        }
      }
    }
  }
}

自动判断的规则如下:

JSON Type Field Type
Boolean:true、flase boolean
Whole number:123、456、876 long
Floating point:123.43、234.534 double
String,valid date:"2022-05-15" date
String:"Hello Elasticsearch" string

常见的ELasticSearch数据类型如下:

数据类型 具体类型
字符串类型 string,text,keyword
整数类型 integer,long,short,byte
浮点类型 double,float,half_float,scaled_float
逻辑类型 boolean
日期类型 date
范围类型 range
二进制类型 binary
数组类型 array
对象类型 object
嵌套类型 nested
地理坐标类型 geo_point
地理地图 geo_shape
IP类型 ip
令牌计数类型 token_count

注意事项关于字符串类型:

        string类型: 在ElasticSearch 旧版本中使用较多,从ElasticSearch 5.x开始不再支持string,由text和keyword类型替代。

  • text类型的字段不用于排序,很少用于聚合,需要分词设置text类型
  • keyword类型适用于索引结构化的字段,keyword类型的字段只能通过精确值搜索到。不需要分词设置keyword类型

补充:

        对text类型的字段,会先使用分词器分词,生成倒排索引,用于之后的搜索。

        对keyword类型的字段,不会分词,搜索时只能精确查找

2.6、集群Cluster

        ElasticSearch 是一个分布式的搜索引擎,所以一般由多台物理机组成。而在这些机器上通过配置一个相同的cluster name,让其互相发现从而把自己组织成一个集群。

2.7、Node 节点

       即一个elasticsearch 实例。节点也有自己的名称,默认在启动时会以一个随机的UUID的前七个字符作为节点的名字,你可以为其指定任意的名字。通过集群名在网络中发现同伴组成集群。一个节点也可是集群。(每个物理机器上可以有多个节点,使用不同的端口和节点名)。

        node节点可以分为三类:

2.7.1、master节点

        集群中的一个节点会被选为master节点,它将负责管理集群范畴的变更:处理创建,删除索引等请求,维护集群状态信息。master节点无需参与文档层面的变更和搜索,这意味着仅有一个master节点并不会因流量增长而成为瓶颈。任意一个节点都可以成为 master 节点。

2.7.2、data节点:

        持有数据倒排索引。默认情况下,每个节点都可以通过设定配置文件elasticsearch.yml中的node.data属性为true(默认)成为数据节点。如果需要一个专门的主节点,应将其node.data属性设置为false。

2.7.3、Client节点:

        如果将node.master属性和node.data属性都设置为false,那么该节点就是一个客户端节点,扮演一个负载均衡的角色,将到来的请求路由到集群中的各个节点。

2.8、Shard 分片:

        为了将数据添加到 Elasticsearch,我们需要索引(index)——一个存储关联数据的地方。实际上,索引只是一个用来指向一个或多个分片(shards)的 逻辑命名空间 (logical namespace)。       

           当你查询的索引分布在多个分片上时,ES会把查询发送给每个相关的分片,并将结果组合在一起,而应用程序并不知道分片的存在。即:这个过程对用户来说是透明的。 

        分片分为:主分片(Primary shard)副本分片(Replica shard)

主分片Primary shard:

        用于解决数据水平扩展的问题,通过主分片,可以将数据分布到集群内的所有节点之上,将一份索引数据划分为多小份的能力,允许水平分割和扩展容量。多个分片可以响应请求,提高性能和吞吐量。一个节点(Node)一般会管理多个分片。

副本分片Replica shard:

        副本分片只是一个主分片的拷贝。 副本分片作为硬件故障时保护数据不丢失的冗余备份,并为搜索和返回文档等读操作提供服务。

分片的特点:

  • 一个Index数据在物理上被分布在多个主分片中,每个主分片只存放部分数据,每个主分片可以有多个副本。
  • 主分片的作用:对索引的扩容,使一个索引的容量可以突破单机的限制。
  • 副本分片是对数据的保护,每个主分片对应一个或多个副本分片,当主分片所在节点宕机时,副本分片会被提升为对应的主分片使用。
  • 一个主分片和它的副本分片,不会分配到同一个节点上。
  • 一个分片就是一个Lucene实例,并且它本身就是一个完整的搜索引擎。应用程序不会和它直接通信。
  • 当索引创建完成的时候,主分片的数量就固定了,如果要修改,需要重建索引,代价很高,但是复制分片的数量可以随时调整。

分片的设定:

        对于生产环境中分片的设定,需要提前做好容量规划,主分片数是在索引创建的时候预先设定,事后无法修改。

  • 分片数设置过小
    • 导致后续无法增加节点实现水平扩展;
    • 单个分片的数据量太大,导致数据重新分配耗时。
  • 分片数设置过大,7.0开始,默认主分片设置成1,解决了over-sharding的问题
    • 影响搜索结果的相关性打分,影响统计结果的准确性
    • 单个节点上过多的分片,会导致资源浪费,同时也会影响性能

用图形表示出来可能是这样子的:

Elasticsearch学习(一):基础概念_第2张图片

  • Index 1:蓝色部分,有3个shard,分别是P1,P2,P3,位于3个不同的Node中,这里没有Replica。
  • Index 2:绿色部分,有2个shard,分别是P1,P2,位于2个不同的Node中。并且每个shard有一个replica,分别是R1和R2。

        基于系统可用性的考虑,同一个shard的primary和replica不能位于同一个Node中。这里Shard1的P1和R1分别位于Node3和Node2中,如果某一刻Node2发生宕机,服务基本不会受影响,因为还有一个P1和R2都还是可用的。因为是主备架构,当主分片发生故障时,需要切换,这时候需要选举一个副本作为新主,这里除了会耗费一点点时间外,也会有丢失数据的风险。

2.8.1、路由

        Elasticsearch 如何知道一个文档应该存放到哪个分片中呢?实际上,这个过程是根据下面这个公式决定的:

shard = hash(routing) % number_of_primary_shards

routing 是一个可变值,唯一不可重复,默认是文档的 _id ,也可以设置成一个自定义的值。 routing 通过 hash 函数生成一个数字,然后这个数字再除以 number_of_primary_shards (主分片的数量)后得到余数 。这个分布在 0 到 number_of_primary_shards-1 之间的余数,就是我们所寻求的文档所在分片的位置。

        这就解释了为什么我们要在创建索引的时候就确定好主分片的数量 并且永远不会改变这个数量:因为如果数量变化了,那么所有之前路由的值都会无效,文档也再也找不到了。
        所有的文档 API( get 、 index 、 delete 、 bulk 、 update 以及 mget )都接受一个叫做 routing 的路由参数 ,通过这个参数我们可以自定义文档到分片的映射。一个自定义的路由参数可以用来确保所有相关的文档(例如所有属于同一个用户的文档)都被存储到同一个分片中。

3、ElasticSearch中索引原理

我们知道ES的搜索是非常快的,并且比MySQL快很多,所以来看下两者的索引原理:

  • MySQL的索引原理:B+Tree索引
  • ElasticSearch的索引原理:倒排索引

        倒排索引:也叫反向索引,首先对文档数据按照id进行索引存储,然后对文档中的数据分词,记录对词条进行索引,并记录词条在文档中出现的位置。这样查找时只要找到了词条,就找到了对应的文档。概括来讲是先找到词条,然后看看哪些文档包含这些词条。通俗地来讲,正向索引是通过key找value,倒排索引则是通过value找key。跟MySQL中的索引回表查询有点类似。

你可能感兴趣的:(#,elasticsearch,技术点,搜索引擎,elasticsearch,lucene)