es修改mapping字段类型_es数据模型简介

7115261f2102054105d70b3e7a870e49.png

本文主要分为两大部分

  1. 数据模型
  2. 读写方式

数据模型

  • 业务场景

Elasticsearch是一个实时的分布式搜索和分析引擎,它可以帮助我们用很快的速度去处理大规模数据,可以用于全文检索、结构化检索、推荐、分析以及统计聚合等多种场景。

Elasticsearch是一个建立在全文搜索引擎库Apache Lucene 基础上的分布式搜索引擎。

  • Lucene数据模型
  1. Index:索引,由很多的Document组成。
  2. Document:由很多的Field组成,是Index和Search的最小单位。
  3. Field:由很多的Term组成,包括name(String)、fieldsData(BytesRef)和type(FieldType)这3个属性。
  4. Term:由很多的字节组成,可以分词。

上述四种类型在Elasticsearch中同样存在,意思也一样。

Lucene中存储的索引主要分为三种类型:

  • Invert Index:倒排索引,或者简称Index,通过Term可以查询到拥有该Term的文档。可以配置为是否分词,如果分词可以配置不同的分词器。
  • DocValues:正排索引,采用列式存储。通过DocID可以快速读取到该Doc的特定字段的值。由于是列式存储,性能会比较好。一般用于sort,agg等需要高频读取Doc字段值的场景。
  • Store:字段原始内容存储,同一篇文章的多个Field的Store会存储在一起,适用于一次读取少量且多个字段内存的场景,比如摘要等。

由于Lucene中没有主键概念和更新逻辑,所有对Lucene的更新都是Append一个新Doc,类似于一个只能Append的队列,所有Doc都被同等处理。其中的Doc由众多Field组成,没有特殊Field,每个Field都被同等对待。

Lucene只是提供了一个索引和查询的最基本的功能,距离一个完全可用的搜索引擎还有一些距离:

Lucene未考虑的事情

  • Lucene是一个单机的搜索库,如何以分布式形式支持海量数据?
  • Lucene中没有更新,每次都是Append一个新文档,如何做部分字段的更新?
  • Lucene中没有主键索引,如何处理同一个Doc的多次写入?
  • 在稀疏列数据中,如何判断某些文档是否存在特定字段?
  • Lucene中生成完整Segment后,该Segment就不能再被更改,此时该Segment才能被搜索,这种情况下,如何做实时搜索?

上述几个问题,对于搜索而言都是至关重要的功能诉求,我们接下来看看Elasticsearch中是如何来解这些问题的。

Elasticsearch怎么做

在Elasticsearch中,为了支持分布式,增加了一个系统字段_routing(路由),通过_routing将Doc分发到不同的Shard,不同的Shard可位于不同的机器上,这样就能实现简单的分布式了。

采用类似的方式,Elasticsearch增加了_id、_version、_source和_seq_no等多个系统字段,通过这些Elasticsearch中特有的系统字段可以有效解决上述的几个问题,如下:

4bdf6964198ebf9d085a9f3b3e1bfcb1.png

1. _id

Doc的主键,在写入的时候,可以指定该Doc的ID值,如果不指定,则系统自动生成一个唯一的UUID值。

Lucene中没有主键索引,要保证系统中同一个Doc不会重复,Elasticsearch引入了_id字段来实现主键。每次写入的时候都会先查询id,如果有,则说明已经有相同Doc存在了。

通过_id值(ES内部转换成_uid)可以唯一在Elasticsearch中确定一个Doc。

Elasticsearch中,_id只是一个用户级别的虚拟字段,在Elasticsearch中并不会映射到Lucene中,所以也就不会存储到该字段的值中。

_id的值可以由_uid解析而来(_uid = type + '#' + id),Elasticsearch中会存储_uid。

2. _version

Elasticsearch中每个Doc都会有一个Version,该Version可以由用户指定,也可由系统自动生成。如果是系统自动生成,那么每次Version都是递增1。

Elasticsearch通过使用version来保证对文档的变更能以正确的顺序执行,避免乱序造成的数据丢失:

  • 首次写入Doc的时候,会为Doc分配一个初始的Version:V0,该值根据VersionType不同而不同。
  • 再次写入Doc的时候,如果Request中没有指定Version,则会先加锁,然后去读取该Doc的最大版本V1,然后将V1+1后的新版本写入Lucene中。
  • 再次写入Doc的时候,如果Request中指定了Version:V1,则继续会先加锁,然后去读该Doc的最大版本V2,判断V1==V2,如果不相等,则发生版本冲突。版本吻合则继续写入Lucene。
  • 当做部分更新的时候,会先通过GetRequest读取当前id的完整Doc和V1,接着和当前Request中的Doc合并为一个完整Doc。然后执行一些逻辑后,加锁,再次读取该Doc的最大版本号V2,判断V1==V2,如果不相等,则在刚才执行其他逻辑时被其他线程更改了当前文档,需要报错后重试。如果相等,则期间没有其他线程修改当前文档,继续写入Lucene中。这个过程就是一个典型的read-then-update事务。

3. _source

Elasticsearch中有一个重要的概念是source,存储原始文档,也可以通过过滤设置只存储特定Field。

Elasticsearch中使用_source字段可以实现以下功能:

  • Update:部分更新时,需要从读取文档保存在_source字段中的原文,然后和请求中的部分字段合并为一个完整文档。如果没有_source,则不能完成部分字段的Update操作。
  • Rebuild:最新的版本中新增了rebuild接口,可以通过Rebuild API完成索引重建,过程中不需要从其他系统导入全量数据,而是从当前文档的_source中读取。如果没有_source,则不能使用Rebuild API。
  • Script:不管是Index还是Search的Script,都可能用到存储在Store中的原始内容,如果禁用了_source,则这部分功能不再可用。
  • Summary:摘要信息也是来源于_source字段。

4. _seq_no

严格递增的顺序号,每个文档一个,Shard级别严格递增,保证后写入的Doc的_seq_no大于先写入的Doc的_seq_no。

任何类型的写操作,包括index、create、update和Delete,都会生成一个_seq_no。

每个文档在使用Lucene的document操作接口之前,会获取到一个_seq_no,这个_seq_no会以系统保留Field的名义存储到Lucene中,文档写入Lucene成功后,会标记该seq_no为完成状态,这时候会使用当前seq_no更新local_checkpoint。

5. _primary_term

_primary_term也和_seq_no一样是一个整数,每当Primary Shard发生重新分配时,比如重启,Primary选举等,_primary_term会递增1。

_primary_term主要是用来恢复数据时处理当多个文档的_seq_no一样时的冲突,避免Primary Shard上的写入被覆盖。

6. _routing

路由规则,写入和查询的routing需要一致,否则会出现写入的文档没法被查到情况。

在mapping中,或者Request中可以指定按某个字段路由。默认是按照_Id值路由。

Elasticsearch的写

Elasticsearch采用多Shard方式,通过配置routing规则将数据分成多个数据子集,每个数据子集提供独立的索引和搜索功能。当写入文档的时候,根据routing规则,将文档发送给特定Shard中建立索引。这样就能实现分布式了。

此外,Elasticsearch整体架构上采用了一主多副的方式:

5d67a68ae21a1f2ea162df485a887b2c.png

每个Index由多个Shard组成,通常每个Shard有一个主节点和多个副本节点,副本个数可配。但每次写入时,写入请求会先根据_routing规则选择发给哪个Shard,Index Request中可以设置使用哪个值作为路由参数(如果没设置,则使用Mapping中的配置,如果mapping中也没有配,则使用_id作为路由参数)然后通过_routing的Hash值选择出Shard,最后从集群的Meta中找出该Shard的Primary节点。

1c89d749c3ff4d9e50e5f043f18e5a96.png

70a3728fd8437e0ee5d243911d278f44.png

a4c21000c66a48bc26bfbc37aa770a9e.png

请求接着会先发送给Primary Shard,在Primary Shard上执行成功后,再从Primary Shard上将请求同时发送给多个Replica Shard,请求在多个Replica Shard上执行成功并返回给Primary Shard后,写入请求才算执行成功,最后返回结果给客户端。

这种模式下,写入操作的延时latency = Latency(Primary Write) + Max(Replicas Write)。只要有副本在,写入延时最小也是两次单Shard的写入时延总和,写入效率会较低,但是这样的好处也很明显,避免写入后,单机或磁盘故障导致数据丢失。

但是Elasticsearch为了减少磁盘IO保证读写性能,一般是每隔一段时间(比如5分钟)才会把Lucene的Segment写入磁盘持久化,对于写入内存,但还未Flush到磁盘的Lucene数据,如果发生机器宕机或者掉电,那么内存中的数据也会丢失,这时候如何保证?

对于这种问题,Elasticsearch学习了数据库中的处理方式:增加CommitLog模块,Elasticsearch中叫TransLog。

c169552a3d2ed85a2ba8d6992d112d33.png

在每一个Shard中,写入流程分为两部分,先写入Lucene,再写入TransLog。

Elasticsearch的读

Elasticsearch使用了Lucene作为搜索引擎库,可通过Lucene完成特定字段的搜索等功能。而对于分布式,数据写入的时候根据_routing规则将数据写入某一个Shard中,这样就能将海量数据分布在多个Shard以及多台机器上,也导致了查询的时候潜在数据会分散在当前index的所有Shard中,所以Elasticsearch查询的时候需要查询所有Shard,同一个Shard的Primary和Replica选择一个即可,查询请求会分发给所有Shard,每个Shard中都是一个独立的查询引擎,比如需要返回Top 10的结果,那么每个Shard就会查询并且返回Top 10的结果,然后在Client Node里面会接收所有Shard的结果,然后通过优先级队列二次排序,再挑出最终的Top 10,返回给用户。

6bb7d12ffd91125791c21f329c42e319.png

Elasticsearch中的查询主要分为两类,Get请求:通过ID查询特定Doc;Search请求:通过Query查询匹配Doc。

11e1cc6707e8bcf77cebbbecae9c6c5f.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值