Elasticsearch 基本原理和概念

·  阅读 13

从上面的例子中,我们看到了很多熟悉又陌生的概念,比如索引、文档等等。这些概念 和我们平时看到的数据库里的比如索引有什么区别呢?

举个例子,现在我们要保存唐宋诗词,数据库中我们们会怎么设计?诗词表 我们可能的设计如下:

image.png

要根据朝代或者作者寻找诗,都很简单,比如“select 诗词全文 from 诗词表 where 作者=‘李白’”,如果数据很多,查询速度很慢,怎么办?我们可以在 对应的查询字段上建立索引加速查询

但是如果我们现在有个需求:要求找到包含“望”字的诗词怎么办?用“select 诗词全文 from 诗词表 where 诗词全文 like‘%望%’”,这个意味着 要扫描库中的诗词全文字段,逐条比对,找出所有包含关键词“望”字的记录,。 基本上,数据库中一般的 SQL 优化手段都是用不上的。数量少,大概性能还能接 受,如果数据量稍微大点,就完全无法接受了,更何况在互联网这种海量数据的 情况下呢?

怎么解决这个问题呢,用倒排索引。

倒排索引 Inverted index

比如现在有:

蜀道难(唐)李白 蜀道之难难于上青天,侧身西望长咨嗟。 
静夜思(唐)李白 举头望明月,低头思故乡。 
春台望(唐)李隆基 暇景属三春,高台聊四望。 
鹤冲天(宋)柳永 黄金榜上,偶失龙头望。明代暂遗贤,如何向?未遂风云便, 争不恣狂荡。何须论得丧?才子词人,自是白衣卿相。烟花巷陌,依约丹青屏障。 幸有意中人,堪寻访。且恁偎红翠,风流事,平生畅。青春都一饷。忍把浮名, 换了浅斟低唱!
复制代码

都有望字,于是我们可以这么保存

image.png

如果查哪个诗词中包含上,怎么办,上述的表格可以继续填入新的记录

image.png

其实,上述诗词的中每个字都可以作为关键字,然后建立关键字和文档之间 的对应关系,也就是标识关键字被哪些文档包含。

所以,倒排索引就是,将文档中包含的关键字全部提取处理,然后再将关键 字和文档之间的对应关系保存起来,最后再对关键字本身做索引排序。用户在检 索某一个关键字是,先对关键字的索引进行查找,再通过关键字与文档的对应关 系找到所在文档

在存储在关系型数据库中的数据,需要我们事先分析将数据拆分为不同的字 段,而在 es 这类的存储中,需要应用程序根据规则自动提取关键字,并形成对 应关系。

这些预先提取的关键字,在全文检索领域一般被称为 term(词项),文档 的词项提取在 es 中被称为文档分析,这是全文检索很核心的过程,必须要区分 哪些是词项,哪些不是,比如很多场景下,apple 和 apples 是同一个东西,望和 看其实是同一个动作。

Elasticsearch 基本概念

Elasticsearch 中比较关键的基本概念有索引、文档、映射、映射类型、文档字段概念, 为了方便理解,可以和关系数据库中的相关概念进行个比对:

image.png

Elasticsearch 索引

Elasticsearch 索引是映射类型的容器。一个 Elasticsearch 索引非常像关系型 世界的数据库,是独立的大量文档集合。

当然在底层,肯定用到了倒排索引,最基本的结构就是“keyword”和“Posting List”,Posting list 就是一个 int 的数组,存储了所有符合某个 term 的文档 id。

另外,这个倒排索引相比特定词项出现过的文档列表,会包含更多其它信息。 它会保存每一个词项出现过的文档总数, 在对应的文档中一个具体词项出现的 总次数,词项在文档中的顺序,每个文档的长度,所有文档的平均长度等等相关 信息。

文档 (Document)

文档是 es 中所有可搜索数据的最小单位,比如日志文件中的日志项、一部 电影的具体信息等等。

文档会被序列化 JSON 格式保存到 ElasticSearch 中,JSON 对象由字段组成, 每个字段都有对象的字段类型(字符串,数值,布尔,日期,二进制,范围类型)。 同时每个文档都有一个 Unique ID,可以自己指定 ID,或者通过 ElasticSearch 自 动生成。

所以严格来说,es 中存储的文档是一种半结构化的数据。

映射

映射(mapping)定义了每个字段的类型、字段所使用的分词器等。

get /enjoy_test/_mapping
复制代码

image.png

可以显式映射,由我们在索引映射中进行预先定义;也可以动态映射,在添 加文档的时候,由 es 自动添加到索引,这个过程不需要事先在索引进行字段数 据类型匹配等等,es 会自己推断数据类型。

既然说到了字段类型,当然就离不开字段的数据类型了。

文档字段

文档中的一个字段 field 就相当于关系型数据库中的一列 column,那么它肯 定有数据类型,es 提供的数据类型包括至少有:

数据类型

核心数据类型

字符串类型:string:字符串类还可被分为 text 和 keyword 类型,如果我 们让 es 自动映射数据,那么 es 会把字符串定义为 text,并且还加了一个 keyword 类型字段。

text: 文本数据类型,用于索引全文值的字段。使用文本数据类型的字段,它 们会被分词,在索引之前将字符串转换为单个术语的列表(倒排索引),分词过程 允许 ES 搜索每个全文字段中的单个单词。什么情况适合使用 text,只要不具备 唯一性的字符串一般都可以使用 text。
keyword:关键字数据类型,用于索引结构化内容的字段。使用 keyword 类 型的字段,其不会被分析,给什么值就原封不动地按照这个值索引,所以关键字 字段只能按其确切值进行搜索。什么情况下使用 keyword,具有唯一性的字符串, 例如:电子邮件地址、MAC 地址、身份证号、状态代码...等等。

数字型数据类型:long、integer、short、byte、double、float

日期类型:date

布尔类型:boolean

复杂数据类型
数组:无需专门的数据类型
对象数据类型:单独的 JSON 对象
嵌套数据类型:nested,关于 JSON 对象的数组

地理数据类型
地理点数据类型
地理形状数据类型

专门数据类型
IPv4 数据类型
单词计数数据类型 token_count

字符串联系

创建一个新的索引

put /open-soft/_mapping
{
    "properties":{
        "corp":{
            "type":"text"
        },
        "lang":{
            "type":"text"
        },
        "name":{
            "type":"text"
        }
    }
}
复制代码

入库一个文档

put /open-soft/_doc/1
{
    "name":"Apache Hadoop",
    "lang":"Java",
    "corp":"Apache",
    "stars":200 ## 新增的字段也会添加进文档。
}
复制代码

通过 get /open-soft/_mapping,我们可以看到 es 自动帮我们新增了 stars 这 个字段。

{
  "open-soft" : {
    "mappings" : {
      "properties" : {
        "corp" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "lang" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "name" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "stars" : {
          "type" : "long"
        }
      }
    }
  }
}

复制代码

注:
1、如果不指定文档字段的类型,会默认创建两种类型 一种text,一种keyword。
2、添加文档记录如果字段不存在,会自动添加字段。 3、文档的字段类型是不能修改,只能追加。

数组

不需要特殊配置,一个字段如果被配置为基本数据类型,就是天生支持数组类型的。任何字段都可以有 0 个或多个值,但是在一个数组中数据类型必须一样。

put /open-soft/_doc/2
{
    "name":[
        "Apache Activemq",
        "Activemq Artemis"
    ],
    "lang":"Java",
    "corp":"Apache",
    "stars":[
        500,
        200
    ]
}
复制代码

是没问题的,但是如果:数组的类型是不一致的。

put /open-soft/_doc/3 
{
    "name":[
        "Apache Kafka"
    ],
    "lang":"Java",
    "corp":"Apache",
    "stars":[
        500,
        "kafka"
    ]
}
复制代码

对象

JSON 文档是有层次结构的,一个文档可能包含其他文档,如果一个文档包 含其他文档,那么该文档值是对象类型,其数据类型是对象。当然 ElasticSearch 中是没有所谓对象类型的,比如:

put /open-soft/_doc/object 
{
    "name":[
        "Apache ShardingSphere"
    ],
    "lang":"Java",
    "corp":"JingDong",
    "stars":400,
    "address":{
        "city":"BeiJing",
        "country":"亦庄"
    }
}
复制代码

image.png

对象类型可以在定义索引的映射关系时进行指定

多数据类型

如果说数组允许你使用同一个设置索引多项数据,那么多数据类型允许使用 不同的设置,对同一项数据索引多次。带来的好处就是可以同一文本有多种不同 的索引方式,比如一个字符串类型的字段,可以使用 text 类型做全文检索,使用 keyword 类型做聚合和排序。我们可以看到 es 的动态映射生成的字段类型里, 往往字符串类型都使用了多数据类型。当然,我们一样也可以自己定义:

put /open-soft/_mapping
{
    "properties":{
        "name":{ 字段参数,系列为字段参数。
            "type":"text",
            "fields":{
                "raw":{
                    "type":"keyword"
                },
                "length":{
                    "type":"token_count", 词评率统计
                    "analyzer":"standard"  指定分词器。
                }
            }
        }
    }
}
复制代码

在上面的代码里,我们使用"fields"就把 name 字段扩充为多字段类型,为 name 新增了两个子字段 raw 和 length,raw 设置类型为 keyword,length 设置类 型为 token_count,告诉 es 这个字段在保存还需要做词频统计 通过 fields 字段设置的子字段 raw 和 length,在我们添加文档时,并不需要 单独设置值,他们 name 共享相同的值,只是 es 会以不同的方式处理字段值。 同样在检索文档的时候,它们也不会显示在结果中,所以它们一般都是在检索中 以查询条件的形式出现,以减少检索时的性能开销。

分词器的优先级

    1、字段上定义的分词器最优先。
    2、索引上定义的分词器。
    3、es默认给的。
复制代码

字段参数

定义索引中文档字段的相关属性。

put /open-soft/_mapping
{
    "properties":{
        "name":{ 字段参数,下列为字段参数。
            "type":"text",
            "fields":{
                "raw":{
                    "type":"keyword"
                },
                "length":{
                    "type":"token_count", 词评率统计
                    "analyzer":"standard"  指定分词器。
                }
            }
        }
    }
}
复制代码

元字段 meta-fields

一个文档根据我们定义的业务字段保存有数据之外,它还包含了元数据字段 (meta-fields)。元字段不需要用户定义,在任一文档中都存在,有点类似于数据库 的表结构数据。在名称上有个显著的特征,都是以下划线“_”开头。 例如

get /open-soft/_doc/1   
{
  "_index" : "open-soft",  #索引名称
  "_type" : "_doc",   #文档类型,已废弃。
  "_id" : "1",  #文档id。
  "_version" : 2, #并发控制,乐观锁控制。
  "_seq_no" : 1, 
  "_primary_term" : 1,
  "found" : true,
  "_source" : {#保存存入文档的原始数据。
    "name" : "Apache Hadoop",
    "lang" : "Java",
    "corp" : "Apache",
    "stars" : 200
  }
}

复制代码

大体分为五种类型:身份(标识)元数据索引元数据文档元数据路由 元数据以及其他类型的元数据,当然不是每个文档这些元字段都有的。

身份(标识)元数据

index:文档所属索引 , 自动被索引,可被查询,聚合,排序使用,或者脚本里访问

_type:文档所属类型,自动被索引,可被查询,聚合,排序使用,或者脚本里访问

_id:文档的唯一标识,建索引时候传入 ,不被索引,可通过_uid 被查询,脚本里使用,不能参与聚合或排序

_uid:由_type 和_id 字段组成,自动被索引 ,可被查询,聚合,排序使用,或者脚本里访问,6.0.0 版本后已废止。
复制代码

索引元数据

_all: 自动组合所有的字段值,以空格分割,可以指定分器词索引,但是整个值不被存储,所以此字段仅仅能被搜索,不能获取到具体的值。6.0.0 版本后已废止。

_field_names:索引了每个字段的名字,可以包含 null 值,可以通过 exists查询或 missing 查询方法来校验特定的字段
复制代码

文档元数据

_source : 一个 doc 的原生的 json 数据,不会被索引,用于获取提取字段值 ,启动此字段,索引体积会变大,如果既想使用此字段又想兼顾索引体积,可以开启索引压缩。

_source 是可以被禁用的,不过禁用之后部分功能不再支持,这些功能包括:部分 update api、运行时高亮搜索结果索引重建、修改 mapping 以及分词、索引升级debug 查询或者聚合语句索引自动修复

_size: 整个_source 字段的字节数大小,需要单独安装一个 mapper-size 插件才能展示。
复制代码

路由元数据

_routing: 一个 doc 可以被路由到指定的 shard 上。

其他

_meta:一般用来存储应用相关的元信息。

例如:

put /open-soft/_mapping

{

"_meta": {

"class": "cn.enjoyedu.User",

"version": {"min": "1.0", "max": "1.3"}

}

}
复制代码
分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改